summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/src
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2008-08-28 02:25:51 +0000
committerpeter <peter@FreeBSD.org>2008-08-28 02:25:51 +0000
commitea50d71feb02a78d4d5fa746a26ca7ddc6e8cb19 (patch)
treedaf40952cf309641cc6c7d987989fd2abce2d758 /contrib/sendmail/src
parenta2b986fa722f9860a6c56bb5cc724b7e2937d1b7 (diff)
downloadFreeBSD-src-ea50d71feb02a78d4d5fa746a26ca7ddc6e8cb19.zip
FreeBSD-src-ea50d71feb02a78d4d5fa746a26ca7ddc6e8cb19.tar.gz
Stage 1 of sendmail dist tree flattening. contrib/sendmail/contrib
prevents doing this in one pass.
Diffstat (limited to 'contrib/sendmail/src')
-rw-r--r--contrib/sendmail/src/Makefile19
-rw-r--r--contrib/sendmail/src/Makefile.m4103
-rw-r--r--contrib/sendmail/src/README1850
-rw-r--r--contrib/sendmail/src/SECURITY203
-rw-r--r--contrib/sendmail/src/TRACEFLAGS102
-rw-r--r--contrib/sendmail/src/TUNING230
-rw-r--r--contrib/sendmail/src/alias.c1015
-rw-r--r--contrib/sendmail/src/aliases65
-rw-r--r--contrib/sendmail/src/aliases.5131
-rw-r--r--contrib/sendmail/src/arpadate.c203
-rw-r--r--contrib/sendmail/src/bf.c864
-rw-r--r--contrib/sendmail/src/bf.h32
-rw-r--r--contrib/sendmail/src/collect.c1101
-rw-r--r--contrib/sendmail/src/conf.c6376
-rw-r--r--contrib/sendmail/src/conf.h232
-rw-r--r--contrib/sendmail/src/control.c431
-rw-r--r--contrib/sendmail/src/convtime.c202
-rw-r--r--contrib/sendmail/src/daemon.c4486
-rw-r--r--contrib/sendmail/src/daemon.h62
-rw-r--r--contrib/sendmail/src/deliver.c6258
-rw-r--r--contrib/sendmail/src/domain.c1166
-rw-r--r--contrib/sendmail/src/envelope.c1293
-rw-r--r--contrib/sendmail/src/err.c1158
-rw-r--r--contrib/sendmail/src/headers.c2311
-rw-r--r--contrib/sendmail/src/helpfile137
-rw-r--r--contrib/sendmail/src/macro.c730
-rw-r--r--contrib/sendmail/src/mailq.1135
-rw-r--r--contrib/sendmail/src/main.c4554
-rw-r--r--contrib/sendmail/src/map.c7989
-rw-r--r--contrib/sendmail/src/map.h86
-rw-r--r--contrib/sendmail/src/mci.c1561
-rw-r--r--contrib/sendmail/src/milter.c4736
-rw-r--r--contrib/sendmail/src/mime.c1325
-rw-r--r--contrib/sendmail/src/newaliases.150
-rw-r--r--contrib/sendmail/src/parseaddr.c3360
-rw-r--r--contrib/sendmail/src/queue.c8895
-rw-r--r--contrib/sendmail/src/ratectrl.c534
-rw-r--r--contrib/sendmail/src/readcf.c4564
-rw-r--r--contrib/sendmail/src/recipient.c2072
-rw-r--r--contrib/sendmail/src/sasl.c287
-rw-r--r--contrib/sendmail/src/savemail.c1761
-rw-r--r--contrib/sendmail/src/sendmail.8748
-rw-r--r--contrib/sendmail/src/sendmail.h2656
-rw-r--r--contrib/sendmail/src/sfsasl.c952
-rw-r--r--contrib/sendmail/src/sfsasl.h25
-rw-r--r--contrib/sendmail/src/shmticklib.c78
-rw-r--r--contrib/sendmail/src/sm_resolve.c456
-rw-r--r--contrib/sendmail/src/sm_resolve.h142
-rw-r--r--contrib/sendmail/src/srvrsmtp.c4974
-rw-r--r--contrib/sendmail/src/stab.c475
-rw-r--r--contrib/sendmail/src/stats.c198
-rw-r--r--contrib/sendmail/src/statusd_shm.h44
-rw-r--r--contrib/sendmail/src/sysexits.c167
-rw-r--r--contrib/sendmail/src/timers.c231
-rw-r--r--contrib/sendmail/src/timers.h33
-rw-r--r--contrib/sendmail/src/tls.c1671
-rw-r--r--contrib/sendmail/src/trace.c224
-rw-r--r--contrib/sendmail/src/udb.c1314
-rw-r--r--contrib/sendmail/src/usersmtp.c3318
-rw-r--r--contrib/sendmail/src/util.c2853
-rw-r--r--contrib/sendmail/src/version.c18
61 files changed, 0 insertions, 93246 deletions
diff --git a/contrib/sendmail/src/Makefile b/contrib/sendmail/src/Makefile
deleted file mode 100644
index a5633c7..0000000
--- a/contrib/sendmail/src/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-# $Id: Makefile,v 8.12 2006/08/29 22:00:11 ca Exp $
-
-SHELL= /bin/sh
-BUILD= ./Build
-OPTIONS= $(CONFIG) $(FLAGS)
-
-all: FRC
- $(SHELL) $(BUILD) $(OPTIONS) $@
-check: 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
deleted file mode 100644
index b2bfa1a..0000000
--- a/contrib/sendmail/src/Makefile.m4
+++ /dev/null
@@ -1,103 +0,0 @@
-dnl $Id: Makefile.m4,v 8.112 2007/10/17 21:29:43 ca Exp $
-include(confBUILDTOOLSDIR`/M4/switch.m4')
-
-define(`confREQUIRE_LIBSM', `true')
-define(`confREQUIRE_SM_OS_H', `true')
-bldPRODUCT_START(`executable', `sendmail')
-define(`bldBIN_TYPE', `G')
-define(`bldINSTALL_DIR', `')
-define(`bldSOURCES', `main.c alias.c arpadate.c bf.c collect.c conf.c control.c convtime.c daemon.c deliver.c domain.c envelope.c err.c headers.c macro.c map.c mci.c milter.c mime.c parseaddr.c queue.c ratectrl.c readcf.c recipient.c sasl.c savemail.c sfsasl.c shmticklib.c sm_resolve.c srvrsmtp.c stab.c stats.c sysexits.c timers.c tls.c trace.c udb.c usersmtp.c util.c version.c ')
-PREPENDDEF(`confENVDEF', `confMAPDEF')
-bldPUSH_SMLIB(`sm')
-bldPUSH_SMLIB(`smutil')
-
-
-dnl hack: /etc/mail is not defined as "location of .cf" in the build system
-define(`bldTARGET_INST_DEP', ifdef(`confINST_DEP', `confINST_DEP',
-`${DESTDIR}/etc/mail/submit.cf ${DESTDIR}${MSPQ}'))dnl
-define(`bldTARGET_LINKS', ifdef(`confLINKS', `confLINKS',
-`${DESTDIR}${UBINDIR}/newaliases ${DESTDIR}${UBINDIR}/mailq ${DESTDIR}${UBINDIR}/hoststat ${DESTDIR}${UBINDIR}/purgestat')
-)dnl
-
-# location of sendmail statistics file (usually /etc/mail/ or /var/log)
-STDIR= ifdef(`confSTDIR', `confSTDIR', `/etc/mail')
-
-# statistics file name
-STFILE= ifdef(`confSTFILE', `confSTFILE', `statistics')
-MSPSTFILE=ifdef(`confMSP_STFILE', `confMSP_STFILE', `sm-client.st')
-
-# full path to installed statistics file (usually ${STDIR}/statistics)
-STPATH= ${STDIR}/${STFILE}
-
-# location of sendmail helpfile file (usually /etc/mail)
-HFDIR= ifdef(`confHFDIR', `confHFDIR', `/etc/mail')
-
-# full path to installed help file (usually ${HFDIR}/helpfile)
-HFFILE= ${HFDIR}/ifdef(`confHFFILE', `confHFFILE', `helpfile')
-
-ifdef(`confSMSRCADD', `APPENDDEF(`confSRCADD', `confSMSRCADD')')
-ifdef(`confSMOBJADD', `APPENDDEF(`confOBJADD', `confSMOBJADD')')
-
-bldPUSH_TARGET(`statistics')
-divert(bldTARGETS_SECTION)
-statistics:
- ${CP} /dev/null statistics
-
-${DESTDIR}/etc/mail/submit.cf:
- @echo "Please read INSTALL if anything fails while installing the binary."
- @echo "${DESTDIR}/etc/mail/submit.cf will be installed now."
- cd ${SRCDIR}/cf/cf && make install-submit-cf
-
-MSPQ=ifdef(`confMSP_QUEUE_DIR', `confMSP_QUEUE_DIR', `/var/spool/clientmqueue')
-
-${DESTDIR}${MSPQ}:
- @echo "Please read INSTALL if anything fails while installing the binary."
- @echo "You must have setup a new user ${MSPQOWN} and a new group ${GBINGRP}"
- @echo "as explained in sendmail/SECURITY."
- mkdir -p ${DESTDIR}${MSPQ}
- chown ${MSPQOWN} ${DESTDIR}${MSPQ}
- chgrp ${GBINGRP} ${DESTDIR}${MSPQ}
- chmod 0770 ${DESTDIR}${MSPQ}
-
-divert(0)
-
-ifdef(`confSETUSERID_INSTALL', `bldPUSH_INSTALL_TARGET(`install-set-user-id')')
-ifdef(`confMTA_INSTALL', `bldPUSH_INSTALL_TARGET(`install-sm-mta')')
-ifdef(`confNO_HELPFILE_INSTALL',, `bldPUSH_INSTALL_TARGET(`install-hf')')
-ifdef(`confNO_STATISTICS_INSTALL',, `bldPUSH_INSTALL_TARGET(`install-st')')
-divert(bldTARGETS_SECTION)
-
-install-set-user-id: bldCURRENT_PRODUCT ifdef(`confNO_HELPFILE_INSTALL',, `install-hf') ifdef(`confNO_STATISTICS_INSTALL',, `install-st') ifdef(`confNO_MAN_BUILD',, `install-docs')
- ${INSTALL} -c -o ${S`'BINOWN} -g ${S`'BINGRP} -m ${S`'BINMODE} bldCURRENT_PRODUCT ${DESTDIR}${M`'BINDIR}
- for i in ${sendmailTARGET_LINKS}; do \
- rm -f $$i; \
- ${LN} ${LNOPTS} ${M`'BINDIR}/sendmail $$i; \
- done
-
-define(`confMTA_LINKS', `${DESTDIR}${UBINDIR}/newaliases ${DESTDIR}${UBINDIR}/mailq ${DESTDIR}${UBINDIR}/hoststat ${DESTDIR}${UBINDIR}/purgestat')
-install-sm-mta: bldCURRENT_PRODUCT
- ${INSTALL} -c -o ${M`'BINOWN} -g ${M`'BINGRP} -m ${M`'BINMODE} bldCURRENT_PRODUCT ${DESTDIR}${M`'BINDIR}/sm-mta
- for i in confMTA_LINKS; do \
- rm -f $$i; \
- ${LN} ${LNOPTS} ${M`'BINDIR}/sm-mta $$i; \
- done
-
-install-hf:
- if [ ! -d ${DESTDIR}${HFDIR} ]; then mkdir -p ${DESTDIR}${HFDIR}; else :; fi
- ${INSTALL} -c -o ${UBINOWN} -g ${UBINGRP} -m 444 helpfile ${DESTDIR}${HFFILE}
-
-install-st: statistics
- if [ ! -d ${DESTDIR}${STDIR} ]; then mkdir -p ${DESTDIR}${STDIR}; else :; fi
- ${INSTALL} -c -o ${SBINOWN} -g ${UBINGRP} -m ifdef(`confSTMODE', `confSTMODE', `0600') statistics ${DESTDIR}${STPATH}
-
-install-submit-st: statistics ${DESTDIR}${MSPQ}
- ${INSTALL} -c -o ${MSPQOWN} -g ${GBINGRP} -m ifdef(`confSTMODE', `confSTMODE', `0600') statistics ${DESTDIR}${MSPQ}/${MSPSTFILE}
-
-divert(0)
-bldPRODUCT_END
-
-bldPRODUCT_START(`manpage', `sendmail')
-define(`bldSOURCES', `sendmail.8 aliases.5 mailq.1 newaliases.1')
-bldPRODUCT_END
-
-bldFINISH
diff --git a/contrib/sendmail/src/README b/contrib/sendmail/src/README
deleted file mode 100644
index 2039674..0000000
--- a/contrib/sendmail/src/README
+++ /dev/null
@@ -1,1850 +0,0 @@
-# Copyright (c) 1998-2004 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.
-#
-# 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: README,v 8.390 2006/11/13 22:27:27 ca Exp $
-#
-
-This directory contains the source files for sendmail(TM).
-
- *******************************************************************
- !! Read sendmail/SECURITY for important installation information !!
- *******************************************************************
-
- **********************************************************
- ** Read below for more details on building sendmail. **
- **********************************************************
-
-**************************************************************************
-** IMPORTANT: Read the appropriate paragraphs in the section on **
-** ``Operating System and Compile Quirks''. **
-**************************************************************************
-
-For detailed instructions, please read the document ../doc/op/op.me:
-
- cd ../doc/op ; make op.ps op.txt
-
-Sendmail is a trademark of Sendmail, Inc.
-
-
-+-------------------+
-| BUILDING SENDMAIL |
-+-------------------+
-
-By far, the easiest way to compile sendmail is to use the "Build"
-script:
-
- sh Build
-
-This uses the "uname" command to figure out what architecture you are
-on and creates a proper Makefile accordingly. It also creates a
-subdirectory per object format, so that multiarchitecture support is
-easy. In general this should be all you need. IRIX 6.x users should
-read the note below in the OPERATING SYSTEM AND COMPILE QUIRKS section.
-
-If you need to look at other include or library directories, use the
--I or -L flags on the command line, e.g.,
-
- sh Build -I/usr/sww/include -L/usr/sww/lib
-
-It's also possible to create local site configuration in the file
-site.config.m4 (or another file settable with the -f flag). This
-file contains M4 definitions for various compilation values; the
-most useful are:
-
-confMAPDEF -D flags to specify database types to be included
- (see below)
-confENVDEF -D flags to specify other environment information
-confINCDIRS -I flags for finding include files during compilation
-confLIBDIRS -L flags for finding libraries during linking
-confLIBS -l flags for selecting libraries during linking
-confLDOPTS other ld(1) linker options
-
-Others can be found by examining Makefile.m4. Please read
-../devtools/README for more information about the site.config.m4
-file.
-
-You can recompile from scratch using the -c flag with the Build
-command. This removes the existing compilation directory for the
-current platform and builds a new one. The -c flag must also
-be used if any site.*.m4 file in devtools/Site/ is changed.
-
-Porting to a new Unix-based system should be a matter of creating
-an appropriate configuration file in the devtools/OS/ directory.
-
-
-+----------------------+
-| DATABASE DEFINITIONS |
-+----------------------+
-
-There are several database formats that can be used for the alias files
-and for general maps. When used for alias files they interact in an
-attempt to be backward compatible.
-
-The options are:
-
-NEWDB The new Berkeley DB package. Some systems (e.g., BSD/OS and
- Digital UNIX 4.0) have some version of this package
- pre-installed. If your system does not have Berkeley DB
- pre-installed, or the version installed is not version 2.0
- or greater (e.g., is Berkeley DB 1.85 or 1.86), get the
- current version from http://www.sleepycat.com/. DO NOT
- use a version from any of the University of California,
- Berkeley "Net" or other distributions. If you are still
- 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 or libdb.so.
- See also OPERATING SYSTEM AND COMPILE QUIRKS about Berkeley
- DB versions, e.g., DB 4.1.x.
-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
- NIS support on your system.
-NISPLUS NIS+ (the revised NIS released with Solaris 2). You must
- have NIS+ support on your system to use this flag.
-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.
- BIND 8.X also includes Hesiod support.
-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.
-DNSMAP DNS map support. Requires NAMED_BIND.
-PH_MAP PH map support. You will need the libphclient library from
- the nph package (http://www-dev.cites.uiuc.edu/ph/nph/).
-MAP_NSD nsd map support (IRIX 6.5 and later).
-SOCKETMAP Support for a trivial query protocol over UNIX domain or TCP
- sockets.
-
->>> 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
->>> ndbm.o from libdb.a before you install it and DO NOT install ndbm.h;
->>> for Berkeley DB versions 2.0 through 2.3.14, remove dbm.o from libdb.a
->>> before you install it. If you don't delete these, there is absolutely
->>> no point to including -DNDBM, since it will just get you another
->>> (inferior) API to the same format database. These files OVERRIDE
->>> calls to ndbm routines -- in particular, if you leave ndbm.h in,
->>> you can find yourself using the new db package even if you don't
->>> define NEWDB. Berkeley DB versions later than 2.3.14 do not need
->>> to be modified. Please also consult the README in the top level
->>> directory of the sendmail distribution for other important information.
->>>
->>> Further note: DO NOT remove your existing /usr/include/ndbm.h --
->>> you need that one. But do not install an updated ndbm.h in
->>> /usr/include, /usr/local/include, or anywhere else.
-
-If NEWDB and NDBM are defined (but not NIS), then sendmail will read
-NDBM format alias files, but the next time a newaliases is run the
-format will be converted to NEWDB; that format will be used forever
-more. This is intended as a transition feature.
-
-If NEWDB, NDBM, and NIS are all defined and the name of the file includes
-the string "/yp/", sendmail will rebuild BOTH the NEWDB and NDBM format
-alias files. However, it will only read the NEWDB file; the NDBM format
-file is used only by the NIS subsystem. This is needed because the NIS
-maps on an NIS server are built directly from the NDBM files.
-
-If NDBM and NIS are defined (regardless of the definition of NEWDB),
-and the filename includes the string "/yp/", sendmail adds the special
-tokens "YP_LAST_MODIFIED" and "YP_MASTER_NAME", both of which are
-required if the NDBM file is to be used as an NIS map.
-
-All of these flags are normally defined in a confMAPDEF setting in your
-site.config.m4.
-
-If you define NEWDB or HESIOD you get the User Database (USERDB)
-automatically. Generally you do want to have NEWDB for it to do
-anything interesting. See above for getting the Berkeley DB
-package (i.e., NEWDB). There is no separate "user database"
-package -- don't bother searching for it on the net.
-
-Hesiod and LDAP require libraries that may not be installed with your
-system. These are outside of my ability to provide support. See the
-"Quirks" section for more information.
-
-The regex map can be used to see if an address matches a certain regular
-expression. For example, all-numerics local parts are common spam
-addresses, so "^[0-9]+$" would match this. By using such a map in a
-check_* rule-set, you can block a certain range of addresses that would
-otherwise be considered valid.
-
-The socket map uses a simple request/reply protocol over TCP or
-UNIX domain sockets to query an external server. Both requests and
-replies are text based and encoded as netstrings. The socket map
-uses the same syntax as milters the specify the remote endpoint,
-e.g.:
-
-Ksocket mySocketMap inet:12345@127.0.0.1
-
-See doc/op/op.me for details.
-
-+---------------+
-| COMPILE FLAGS |
-+---------------+
-
-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 devtools/OS subdirectory for the supported
-architectures.
-
-If you are a system to which sendmail has already been ported you
-should not have to touch the following symbols. But if you are porting,
-you may have to tweak the following compilation flags in conf.h in order
-to get it to compile and link properly:
-
-SYSTEM5 Adjust for System V (not necessarily Release 4).
-SYS5SIGNALS Use System V signal semantics -- the signal handler
- is automatically dropped when the signal is caught.
- If this is not set, use POSIX/BSD semantics, where the
- signal handler stays in force until an exec or an
- explicit delete. Implied by SYSTEM5.
-SYS5SETPGRP Use System V setpgrp() semantics. Implied by SYSTEM5.
-HASNICE Define this to zero if you lack the nice(2) system call.
-HASRRESVPORT Define this to zero if you lack the rresvport(3) system call.
-HASFCHMOD Define this to one if you have the fchmod(2) system call.
- This improves security.
-HASFCHOWN Define this to one if you have the fchown(2) system call.
- This is required for the TrustedUser option if sendmail
- must rebuild an (alias) map.
-HASFLOCK Set this if you prefer to use the flock(2) system call
- rather than using fcntl-based locking. Fcntl locking
- has some semantic gotchas, but many vendor systems
- also interface it to lockd(8) to do NFS-style locking.
- Unfortunately, may vendors implementations of fcntl locking
- is just plain broken (e.g., locks are never released,
- causing your sendmail to deadlock; when the kernel runs
- out of locks your system crashes). For this reason, I
- recommend always defining this unless you are absolutely
- certain that your fcntl locking implementation really works.
-HASUNAME Set if you have the "uname" system call. Implied by
- SYSTEM5.
-HASUNSETENV Define this if your system library has the "unsetenv"
- subroutine.
-HASSETSID Define this if you have the setsid(2) system call. This
- is implied if your system appears to be POSIX compliant.
-HASINITGROUPS Define this if you have the initgroups(3) routine.
-HASSETVBUF Define this if you have the setvbuf(3) library call.
- If you don't, setlinebuf will be used instead. This
- defaults on if your compiler defines __STDC__.
-HASSETREUID Define this if you have setreuid(2) ***AND*** root can
- use setreuid to change to an arbitrary user. This second
- condition is not satisfied on AIX 3.x. You may find that
- your system has setresuid(2), (for example, on HP-UX) in
- which case you will also have to #define setreuid(r, e)
- to be the appropriate call. Some systems (such as Solaris)
- have a compatibility routine that doesn't work properly,
- but may have "saved user ids" properly implemented so you
- can ``#define setreuid(r, e) seteuid(e)'' and have it work.
- The important thing is that you have a call that will set
- the effective uid independently of the real or saved uid
- and be able to set the effective uid back again when done.
- There's a test program in ../test/t_setreuid.c that will
- try things on your system. Setting this improves the
- security, since sendmail doesn't have to read .forward
- and :include: files as root. There are certain attacks
- that may be unpreventable without this call.
-USESETEUID Define this to 1 if you have a seteuid(2) system call that
- will allow root to set only the effective user id to an
- arbitrary value ***AND*** you have saved user ids. This is
- preferable to HASSETREUID if these conditions are fulfilled.
- These are the semantics of the to-be-released revision of
- Posix.1. The test program ../test/t_seteuid.c will try
- this out on your system. If you define both HASSETREUID
- and USESETEUID, the former is ignored.
-HASSETEGID Define this if you have setegid(2) and it can be
- used to set the saved gid. Please run t_dropgid in
- test/ if you are not sure whether the call works.
-HASSETREGID Define this if you have setregid(2) and it can be
- used to set the saved gid. Please run t_dropgid in
- test/ if you are not sure whether the call works.
-HASSETRESGID Define this if you have setresgid(2) and it can be
- used to set the saved gid. Please run t_dropgid in
- test/ if you are not sure whether the call works.
-HASLSTAT Define this if you have symbolic links (and thus the
- lstat(2) system call). This improves security. Unlike
- most other options, this one is on by default, so you
- need to #undef it in conf.h if you don't have symbolic
- links (these days everyone does).
-HASSETRLIMIT Define this to 1 if you have the setrlimit(2) syscall.
- You can define it to 0 to force it off. It is assumed
- if you are running a BSD-like system.
-HASULIMIT Define this if you have the ulimit(2) syscall (System V
- style systems). HASSETRLIMIT overrides, as it is more
- general.
-HASWAITPID Define this if you have the waitpid(2) syscall.
-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)).
-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.
-HASCLOSEFROM Define this if your system has closefrom(3).
-HASFDWALK Define this if your system has fdwalk(3).
-SM_CONF_GETOPT Define this as 0 if you need a reimplementation of getopt(3).
- On some systems, getopt does very odd things if called
- to scan the arguments twice. This flag will ask sendmail
- to compile in a local version of getopt that works
- properly. You may also need this if you build with
- another library that introduces a non-standard getopt(3).
-NEEDSTRTOL Define this if your standard C library does not define
- strtol(3). This will compile in a local version.
-NEEDFSYNC Define this if your standard C library does not define
- fsync(2). This will try to simulate the operation using
- fcntl(2); if that is not available it does nothing, which
- isn't great, but at least it compiles and runs.
-HASGETUSERSHELL Define this to 1 if you have getusershell(3) in your
- standard C library. If this is not defined, or is defined
- to be 0, sendmail will scan the /etc/shells file (no
- NIS-style support, defaults to /bin/sh and /bin/csh if
- that file does not exist) to get a list of unrestricted
- user shells. This is used to determine whether users
- are allowed to forward their mail to a program or a file.
-NEEDPUTENV Define this if your system needs am emulation of the
- putenv(3) call. Define to 1 to implement it in terms
- of setenv(3) or to 2 to do it in terms of primitives.
-NOFTRUNCATE Define this if you don't have the ftruncate(2) syscall.
- If you don't have this system call, there is an unavoidable
- race condition that occurs when creating alias databases.
-GIDSET_T The type of entries in a gidset passed as the second
- argument to getgroups(2). Historically this has been an
- int, so this is the default, but some systems (such as
- IRIX) pass it as a gid_t, which is an unsigned short.
- This will make a difference, so it is important to get
- this right! However, it is only an issue if you have
- group sets.
-SLEEP_T The type returned by the system sleep() function.
- Defaults to "unsigned int". Don't worry about this
- if you don't have compilation problems.
-ARBPTR_T The type of an arbitrary pointer -- defaults to "void *".
- If you are an very old compiler you may need to define
- this to be "char *".
-SOCKADDR_LEN_T The type used for the third parameter to accept(2),
- getsockname(2), and getpeername(2), representing the
- length of a struct sockaddr. Defaults to int.
-SOCKOPT_LEN_T The type used for the fifth parameter to getsockopt(2)
- and setsockopt(2), representing the length of the option
- buffer. Defaults to int.
-LA_TYPE The type of load average your kernel supports. These
- can be one of:
- LA_ZERO (1) -- it always returns the load average as
- "zero" (and does so on all architectures).
- LA_INT (2) to read /dev/kmem for the symbol avenrun and
- interpret as a long integer.
- LA_FLOAT (3) same, but interpret the result as a floating
- point number.
- LA_SHORT (6) to interpret as a short integer.
- LA_SUBR (4) if you have the getloadavg(3) routine in your
- system library.
- LA_MACH (5) to use MACH-style load averages (calls
- processor_set_info()),
- LA_PROCSTR (7) to read /proc/loadavg and interpret it
- as a string representing a floating-point
- number (Linux-style).
- LA_READKSYM (8) is an implementation suitable for some
- versions of SVr4 that uses the MIOC_READKSYM ioctl
- call to read /dev/kmem.
- LA_DGUX (9) is a special implementation for DG/UX that uses
- the dg_sys_info system call.
- LA_HPUX (10) is an HP-UX specific version that uses the
- pstat_getdynamic system call.
- LA_IRIX6 (11) is an IRIX 6.x specific version that adapts
- to 32 or 64 bit kernels; it is otherwise very similar
- to LA_INT.
- LA_KSTAT (12) uses the (Solaris-specific) kstat(3k)
- implementation.
- LA_DEVSHORT (13) reads a short from a system file (default:
- /dev/table/avenrun) and scales it in the same manner
- as LA_SHORT.
- LA_LONGLONG (17) to read /dev/kmem for the symbol avenrun and
- interpret as a long long integer (e.g., for 64 bit
- systems).
- LA_INT, LA_SHORT, LA_FLOAT, and LA_READKSYM have several
- other parameters that they try to divine: the name of your
- kernel, the name of the variable in the kernel to examine,
- the number of bits of precision in a fixed point load average,
- and so forth. LA_DEVSHORT uses _PATH_AVENRUN to find the
- device to be read to find the load average.
- In desperation, use LA_ZERO. The actual code is in
- conf.c -- it can be tweaked if you are brave.
-FSHIFT For LA_INT, LA_SHORT, and LA_READKSYM, this is the number
- of bits of load average after the binary point -- i.e.,
- the number of bits to shift right in order to scale the
- integer to get the true integer load average. Defaults to 8.
-_PATH_UNIX The path to your kernel. Needed only for LA_INT, LA_SHORT,
- and LA_FLOAT. Defaults to "/unix" on System V, "/vmunix"
- everywhere else.
-LA_AVENRUN For LA_INT, LA_SHORT, and LA_FLOAT, the name of the kernel
- variable that holds the load average. Defaults to "avenrun"
- on System V, "_avenrun" everywhere else.
-SFS_TYPE Encodes how your kernel can locate the amount of free
- space on a disk partition. This can be set to SFS_NONE
- (0) if you have no way of getting this information,
- SFS_USTAT (1) if you have the ustat(2) system call,
- SFS_4ARGS (2) if you have a four-argument statfs(2)
- system call (and the include file is <sys/statfs.h>),
- SFS_VFS (3), SFS_MOUNT (4), SFS_STATFS (5) if you have
- the two-argument statfs(2) system call with includes in
- <sys/vfs.h>, <sys/mount.h>, or <sys/statfs.h> respectively,
- or SFS_STATVFS (6) if you have the two-argument statvfs(2)
- call. The default if nothing is defined is SFS_NONE.
-SFS_BAVAIL with SFS_4ARGS you can also set SFS_BAVAIL to the field name
- in the statfs structure that holds the useful information;
- this defaults to f_bavail.
-SPT_TYPE Encodes how your system can display what a process is doing
- on a ps(1) command (SPT stands for Set Process Title). Can
- be set to:
- SPT_NONE (0) -- Don't try to set the process title at all.
- SPT_REUSEARGV (1) -- Pad out your argv with the information;
- this is the default if none specified.
- SPT_BUILTIN (2) -- The system library has setproctitle.
- SPT_PSTAT (3) -- Use the PSTAT_SETCMD option to pstat(2)
- to set the process title; this is used by HP-UX.
- SPT_PSSTRINGS (4) -- Use the magic PS_STRINGS pointer (4.4BSD).
- SPT_SYSMIPS (5) -- Use sysmips() supported by NEWS-OS 6.
- SPT_SCO (6) -- Write kernel u. area.
- SPT_CHANGEARGV (7) -- Write pointers to our own strings into
- the existing argv vector.
-SPT_PADCHAR Character used to pad the process title; if undefined,
- the space character (0x20) is used. This is ignored if
- SPT_TYPE != SPT_REUSEARGV
-ERRLIST_PREDEFINED
- If set, assumes that some header file defines sys_errlist.
- This may be needed if you get type conflicts on this
- variable -- otherwise don't worry about it.
-WAITUNION The wait(2) routine takes a "union wait" argument instead
- of an integer argument. This is for compatibility with
- old versions of BSD.
-SCANF You can set this to extend the F command to accept a
- scanf string -- this gives you a primitive parser for
- class definitions -- BUT it can make you vulnerable to
- core dumps if the target file is poorly formed.
-SYSLOG_BUFSIZE You can define this to be the size of the buffer that
- syslog accepts. If it is not defined, it assumes a
- 1024-byte buffer. If the buffer is very small (under
- 256 bytes) the log message format changes -- each
- e-mail message will log many more messages, since it
- will log each piece of information as a separate line
- in syslog.
-BROKEN_RES_SEARCH
- On Ultrix (and maybe other systems?) if you use the
- res_search routine with an unknown host name, it returns
- -1 but sets h_errno to 0 instead of HOST_NOT_FOUND. If
- you set this, sendmail considers 0 to be the same as
- HOST_NOT_FOUND.
-NAMELISTMASK If defined, values returned by nlist(3) are masked
- against this value before use -- a common value is
- 0x7fffffff to strip off the top bit.
-BSD4_4_SOCKADDR If defined, socket addresses have an sa_len field that
- defines the length of this address.
-SAFENFSPATHCONF Set this to 1 if and only if you have verified that a
- pathconf(2) call with _PC_CHOWN_RESTRICTED argument on an
- NFS filesystem where the underlying system allows users to
- give away files to other users returns <= 0. Be sure you
- try both on NFS V2 and V3. Some systems assume that their
- local policy apply to NFS servers -- this is a bad
- assumption! The test/t_pathconf.c program will try this
- for you -- you have to run it in a directory that is
- mounted from a server that allows file giveaway.
-SIOCGIFCONF_IS_BROKEN
- Set this if your system has an SIOCGIFCONF ioctl defined,
- but it doesn't behave the same way as "most" systems (BSD,
- Solaris, SunOS, HP-UX, etc.)
-SIOCGIFNUM_IS_BROKEN
- Set this if your system has an SIOCGIFNUM ioctl defined,
- but it doesn't behave the same way as "most" systems
- (Solaris, HP-UX).
-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.
-NEEDSGETIPNODE Set this if your system supports IPv6 but doesn't include
- the getipnodeby{name,addr}() functions. Set automatically
- for Linux's glibc.
-PIPELINING Support SMTP PIPELINING (set by default).
-USING_NETSCAPE_LDAP
- Deprecated in favor of SM_CONF_LDAP_MEMFREE. See
- libsm/README.
-NEEDLINK Set this if your system doesn't have a link() call. It
- will create a copy of the file instead of a hardlink.
-USE_ENVIRON Set this to 1 to access process environment variables from
- the external variable environ instead of the third
- parameter of main().
-USE_DOUBLE_FORK By default this is on (1). Set it to 0 to suppress the
- extra fork() used to avoid intermediate zombies.
-ALLOW_255 Do not convert (char)0xff to (char)0x7f in headers etc.
- This can also be done at runtime with the command line
- option -d82.101.
-NEEDINTERRNO Set this if <errno.h> does not declare errno, i.e., if an
- application needs to use
- extern int errno;
-USE_TTYPATH Set this to 1 to enable ErrorMode=write.
-USESYSCTL Use sysctl(3) to determine the number of CPUs in a system.
-HASSNPRINTF Set this to 1 if your OS has a working snprintf(3), i.e.,
- it properly obeys the size of the buffer and returns the
- number of characters that would have been printed if the
- size were unlimited.
-LDAP_REFERRALS Set this if you want to use the -R flag (do not auto chase
- referrals) for LDAP maps (requires -DLDAPMAP).
-MILTER_NO_NAGLE Turn off Nagle algorithm for communication with libmilter
- ("cork" on Linux). On some operating systems this may
- improve the interprocess communication performance.
-
-
-+-----------------------+
-| COMPILE-TIME FEATURES |
-+-----------------------+
-
-There are a bunch of features that you can decide to compile in, such
-as selecting various database packages and special protocol support.
-Several are assumed based on other compilation flags -- if you want to
-"un-assume" something, you probably need to edit conf.h. Compilation
-flags that add support for special features include:
-
-NDBM Include support for "new" DBM library for aliases and maps.
- Normally defined in the Makefile.
-NEWDB Include support for Berkeley DB package (hash & btree)
- for aliases and maps. Normally defined in the Makefile.
- If the version of NEWDB you have is the old one that does
- not include the "fd" call (this call was added in version
- 1.5 of the Berkeley DB code), you must upgrade to the
- current version of Berkeley DB.
-NIS Define this to get NIS (YP) support for aliases and maps.
- Normally defined in the Makefile.
-NISPLUS Define this to get NIS+ support for aliases and maps.
- Normally defined in the Makefile.
-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.
-IDENTPROTO Define this as 1 to get IDENT (RFC 1413) protocol support.
- This is assumed unless you are running on Ultrix or
- HP-UX, both of which have a problem in the UDP
- 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 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
- most systems, but some (e.g., Ultrix) apparently have a
- broken version of getsockopt that doesn't properly
- support the IP_OPTIONS call. You probably want this if
- your OS can cope with it. Symptoms of failure will be that
- it won't compile properly (that is, no support for fetching
- IP_OPTIONs), or it compiles but source-routed TCP connections
- either refuse to open or open and hang for no apparent reason.
- Ultrix and AIX3 are known to fail this way.
-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.
-NAMED_BIND If non-zero, include DNS (name daemon) support, including
- MX support. The specs say you must use this if you run
- SMTP. You don't have to be running a name server daemon
- on your machine to need this -- any use of the DNS resolver,
- including remote access to another machine, requires this
- option. Defined by default in conf.h. Define it to zero
- ONLY on machines that do not use DNS in any way.
-MATCHGECOS Permit fuzzy matching of user names against the full
- name (GECOS) field in the /etc/passwd file. This should
- probably be on, since you can disable it from the config
- file if you want to. Defined by default in conf.h.
-MIME8TO7 If non-zero, include 8 to 7 bit MIME conversions. This
- also controls advertisement of 8BITMIME in the ESMTP
- startup dialogue.
-MIME7TO8_OLD If 0 then use an algorithm for MIME 7-bit quoted-printable
- or base64 encoding to 8-bit text that has been introduced
- in 8.12.3. There are some examples where that code fails,
- but the old code works. If you have an example of improper
- 7 to 8 bit conversion please send it to sendmail-bugs.
-MIME7TO8 If non-zero, include 7 to 8 bit MIME conversions.
-HES_GETMAILHOST Define this to 1 if you are using Hesiod with the
- hes_getmailhost() routine. This is included with the MIT
- Hesiod distribution, but not with the DEC Hesiod distribution.
-XDEBUG Do additional internal checking. These don't cost too
- much; you might as well leave this on.
-TCPWRAPPERS Turns on support for the TCP wrappers library (-lwrap).
- See below for further information.
-SECUREWARE Enable calls to the SecureWare luid enabling/changing routines.
- SecureWare is a C2 security package added to several UNIX's
- (notably ConvexOS) to get a C2 Secure system. This
- option causes mail delivery to be done with the luid of the
- recipient.
-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://egd.sourceforge.net/ . 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/); use OpenSSL 0.9.5a or later
- (if compatible with this version), do not use 0.9.3.
- See STARTTLS COMPILATION AND CONFIGURATION for further
- information.
-TLS_NO_RSA Turn off support for RSA algorithms in STARTTLS.
-MILTER Turn on support for external filters using the Milter API;
- this option is set by default, to turn it off use
- APPENDDEF(`conf_sendmail_ENVDEF', `-DMILTER=0')
- in devtools/Site/site.config.m4 (see devtools/README).
- See libmilter/README for more information about milter.
-REQUIRES_DIR_FSYNC Turn on support for file systems that require to
- call fsync() for a directory if the meta-data in it has
- been changed. This should be turned on at least for older
- versions of ReiserFS; it is enabled by default for Linux.
- According to some information this flag is not needed
- anymore for kernel 2.4.16 and newer. We would appreciate
- feedback about the semantics of the various file systems
- available for Linux.
- An alternative to this compile time flag is to mount the
- queue directory without the -async option, or using
- chattr +S on Linux.
-DBMMODE The default file permissions to use when creating new
- database files for maps and aliases. Defaults to 0640.
-
-Generic notice: If you enable a compile time option that needs
-libraries or include files that don't come with sendmail or are
-installed in a location that your C compiler doesn't use by default
-you should set confINCDIRS and confLIBDIRS as explained in the
-first section: BUILDING SENDMAIL.
-
-
-+---------------------+
-| DNS/RESOLVER ISSUES |
-+---------------------+
-
-Many systems have old versions of the resolver library. At a minimum,
-you should be running BIND 4.8.3; older versions may compile, but they
-have known bugs that should give you pause.
-
-Common problems in old versions include "undefined" errors for
-dn_skipname.
-
-Some people have had a problem with BIND 4.9; it uses some routines
-that it expects to be externally defined such as strerror(). It may
-help to link with "-l44bsd" to solve this problem. This has apparently
-been fixed in later versions of BIND, starting around 4.9.3. In other
-words, if you use 4.9.0 through 4.9.2, you need -l44bsd; for earlier or
-later versions, you do not.
-
-!PLEASE! be sure to link with the same version of the resolver as
-the header files you used -- some people have used the 4.9 headers
-and linked with BIND 4.8 or vice versa, and it doesn't work.
-Unfortunately, it doesn't fail in an obvious way -- things just
-subtly don't work.
-
-WILDCARD MX RECORDS ARE A BAD IDEA! The only situation in which they
-work reliably is if you have two versions of DNS, one in the real world
-which has a wildcard pointing to your firewall, and a completely
-different version of the database internally that does not include
-wildcard MX records that match your domain. ANYTHING ELSE WILL GIVE
-YOU HEADACHES!
-
-When attempting to canonify a hostname, some broken name servers will
-return SERVFAIL (a temporary failure) on T_AAAA (IPv6) lookups. If you
-want to excuse this behavior, include WorkAroundBrokenAAAA in
-ResolverOptions. However, instead, we recommend catching the problem and
-reporting it to the name server administrator so we can rid the world of
-broken name servers.
-
-
-+----------------------------------------+
-| STARTTLS COMPILATION AND CONFIGURATION |
-+----------------------------------------+
-
-Please read the documentation accompanying the OpenSSL library. You
-have to compile and install the OpenSSL libraries before you can compile
-sendmail. See devtools/README how to set the correct compile time
-parameters; you should at least set the following variables:
-
-APPENDDEF(`conf_sendmail_ENVDEF', `-DSTARTTLS')
-APPENDDEF(`conf_sendmail_LIBS', `-lssl -lcrypto')
-
-If you have installed the OpenSSL libraries and include files in
-a location that your C compiler doesn't use by default you should
-set confINCDIRS and confLIBDIRS as explained in the first section:
-BUILDING SENDMAIL.
-
-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.
-
-From: Garrett Wollman <wollman@lcs.mit.edu>
-
- If your certificate authority is hierarchical, and you only include
- the top-level CA certificate in the CACertFile file, some mail clients
- may be unable to infer the proper certificate chain when selecting a
- client certificate. Including the bottom-level CA certificate(s) in
- the CACertFile file will allow these clients to work properly. This
- is not necessary if you are not using client certificates for
- authentication, or if all your clients are running Sendmail or other
- programs using the OpenSSL library (which get it right automatically).
- In addition, some mail clients are totally incapable of using
- certificate authentication -- even some of those which already support
- SSL/TLS for confidentiality.
-
-Further information can be found via:
-http://www.sendmail.org/tips/
-
-
-+------------------------------------+
-| SASL COMPILATION AND CONFIGURATION |
-+------------------------------------+
-
-Please read the documentation accompanying the Cyrus SASL library
-(INSTALL and README). If you use Berkeley DB for Cyrus SASL then
-you must compile sendmail with the same version of Berkeley DB.
-See devtools/README for how to set the correct compile time parameters;
-you should at least set the following variables:
-
-APPENDDEF(`conf_sendmail_ENVDEF', `-DSASL')
-APPENDDEF(`conf_sendmail_LIBS', `-lsasl')
-
-If you have installed the Cyrus SASL library and include files in
-a location that your C compiler doesn't use by default you should
-set confINCDIRS and confLIBDIRS as explained in the first section:
-BUILDING SENDMAIL.
-
-You have to select and install authentication mechanisms and tell
-sendmail where to find the sasl library and the include files (see
-devtools/README for the parameters to set). Set up the required
-users and passwords as explained in the SASL documentation. See
-also cf/README for authentication related options (especially
-DefaultAuthInfo if you want authentication between MTAs).
-
-To perform an initial test, connect to your sendmail daemon
-(telnet localhost 25) and issue a EHLO localhost and see whether
-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 |
-+-------------------------------------+
-
-GCC problems
- When compiling with "gcc -O -Wall" specify "-DSM_OMIT_BOGUS_WARNINGS"
- too (see include/sm/cdefs.h for more info).
-
- *****************************************************************
- ** IMPORTANT: DO NOT USE OPTIMIZATION (``-O'') IF YOU ARE **
- ** RUNNING GCC 2.4.x or 2.5.x. THERE IS A BUG IN THE GCC **
- ** OPTIMIZER THAT CAUSES SENDMAIL COMPILES TO FAIL MISERABLY. **
- *****************************************************************
-
- Jim Wilson of Cygnus believes he has found the problem -- it will
- probably be fixed in GCC 2.5.6 -- but until this is verified, be
- very suspicious of gcc -O. This problem is reported to have been
- fixed in gcc 2.6.
-
- A bug in gcc 2.5.5 caused problems compiling sendmail 8.6.5 with
- optimization on a Sparc. If you are using gcc 2.5.5, youi should
- upgrade to the latest version of gcc.
-
- Apparently GCC 2.7.0 on the Pentium processor has optimization
- problems. I recommend against using -O on that architecture. This
- has been seen on FreeBSD 2.0.5 RELEASE.
-
- Solaris 2.X users should use version 2.7.2.3 over 2.7.2.
-
- We have been told there are problems with gcc 2.8.0. If you are
- using this version, you should upgrade to 2.8.1 or later.
-
-Berkeley DB
- Berkeley DB 4.1.x with x <= 24 does not work with sendmail.
- You need at least 4.1.25.
-
-GDBM GDBM does not work with sendmail because the additional
- security checks and file locking cause problems. Unfortunately,
- gdbm does not provide a compile flag in its version of ndbm.h so
- the code can adapt. Until the GDBM authors can fix these problems,
- GDBM will not be supported. Please use Berkeley DB instead.
-
-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 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
- are STRONGLY urged to use symbolic links if you want to use the
- 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.
-
-HP MPE/iX
- The MPE-specific code within sendmail emulates a set-user-id root
- environment for the sendmail binary. But there is no root uid 0 on
- MPE, nor is there any support for set-user-id programs. Even when
- sendmail thinks it is running as uid 0, it will still have the file
- access rights of the underlying non-zero uid, but because sendmail is
- an MPE priv-mode program it will still be able to call setuid() to
- successfully switch to a new uid.
-
- MPE setgid() semantics don't quite work the way sendmail expects, so
- special emulation is done here also.
-
- This uid/gid emulation is enabled via the setuid/setgid file mode bits
- which are not currently used by MPE. Code in libsm/mpeix.c examines
- these bits and enables emulation if they have been set, i.e.,
- chmod u+s,g+s /SENDMAIL/CURRENT/SENDMAIL.
-
-SunOS 4.x (Solaris 1.x)
- You may have to use -lresolv on SunOS. However, beware that
- this links in a new version of gethostbyname that does not
- understand NIS, so you must have all of your hosts in DNS.
-
- Some people have reported problems with the SunOS version of
- -lresolv and/or in.named, and suggest that you get a newer
- version. The symptoms are delays when you connect to the
- SMTP server on a SunOS machine or having your domain added to
- addresses inappropriately. There is a version of BIND
- version 4.9 on gatekeeper.DEC.COM in pub/BSD/bind/4.9.
-
- There is substantial disagreement about whether you can make
- this work with resolv+, which allows you to specify a search-path
- of services. Some people report that it works fine, others
- claim it doesn't work at all (including causing sendmail to
- drop core when it tries to do multiple resolv+ lookups for a
- single job). I haven't tried resolv+, as we use DNS exclusively.
-
- Should you want to try resolv+, it is on ftp.uu.net in
- /networking/ip/dns.
-
- Apparently getservbyname() can fail under moderate to high
- load under some circumstances. This will exhibit itself as
- the message ``554 makeconnection: service "smtp" unknown''.
- The problem has been traced to one or more blank lines in
- /etc/services on the NIS server machine. Delete these
- 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
-
- Sendmail 8.7.Beta.12 compiles and runs nearly out of the box with the
- following changes:
- * 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
- 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.
-
-SunOS 4.1.3, 4.1.3_U1
- Sendmail causes crashes on SunOS 4.1.3 and 4.1.3_U1. According
- to Sun bug number 1077939:
-
- If an application does a getsockopt() on a SOCK_STREAM (TCP) socket
- after the other side of the connection has sent a TCP RESET for
- the stream, the kernel gets a Bus Trap in the tcp_ctloutput() or
- ip_ctloutput() routine.
-
- For 4.1.3, this is fixed in patch 100584-08, available on the
- Sunsolve 2.7.1 or later CDs. For 4.1.3_U1, this was fixed in patch
- 101790-01 (SunOS 4.1.3_U1: TCP socket and reset problems), later
- obsoleted by patch 102010-05.
-
- Sun patch 100584-08 is not currently publicly available on their
- ftp site but a user has reported it can be found at other sites
- using a web search engine.
-
-Solaris 2.x (SunOS 5.x)
- To compile for Solaris, the Makefile built by Build must
- include a SOLARIS definition which reflects the Solaris version
- (i.e. -DSOLARIS=20400 for 2.4 or -DSOLARIS=20501 for 2.5.1).
- If you are using gcc, make sure -I/usr/include is not used (or
- it might complain about TopFrame). If you are using Sun's cc,
- make sure /opt/SUNWspro/bin/cc is used instead of /usr/ucb/cc
- (or it might complain about tm_zone).
-
- The Solaris 2.x (x <= 3) "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 that fix this problem: the patch ids are:
-
- Solaris 2.1 100834
- Solaris 2.2 100999
- Solaris 2.3 101318
-
- Be sure you have the appropriate patch installed or you won't
- see system logging.
-
-Solaris 2.4 (SunOS 5.4)
- If you include /usr/lib at the end of your LD_LIBRARY_PATH you run
- the risk of getting the wrong libraries under some circumstances.
- This is because of a new feature in Solaris 2.4, described by
- Rod.Evans@Eng.Sun.COM:
-
- >> Prior to SunOS 5.4, any LD_LIBRARY_PATH setting was ignored by the
- >> runtime linker if the application was setxid (secure), thus your
- >> applications search path would be:
- >>
- >> /usr/local/lib LD_LIBRARY_PATH component - IGNORED
- >> /usr/lib LD_LIBRARY_PATH component - IGNORED
- >> /usr/local/lib RPATH - honored
- >> /usr/lib RPATH - honored
- >>
- >> the effect is that path 3 would be the first used, and this would
- >> satisfy your resolv.so lookup.
- >>
- >> In SunOS 5.4 we made the LD_LIBRARY_PATH a little more flexible.
- >> People who developed setxid applications wanted to be able to alter
- >> the library search path to some degree to allow for their own
- >> testing and debugging mechanisms. It was decided that the only
- >> secure way to do this was to allow a `trusted' path to be used in
- >> LD_LIBRARY_PATH. The only trusted directory we presently define
- >> is /usr/lib. Thus a set-user-ID root developer could play with some
- >> alternative shared object implementations and place them in
- >> /usr/lib (being root we assume they'ed have access to write in this
- >> directory). This change was made as part of 1155380 - after a
- >> *huge* amount of discussion regarding the security aspect of things.
- >>
- >> So, in SunOS 5.4 your applications search path would be:
- >>
- >> /usr/local/lib from LD_LIBRARY_PATH - IGNORED (untrustworthy)
- >> /usr/lib from LD_LIBRARY_PATH - honored (trustworthy)
- >> /usr/local/lib from RPATH - honored
- >> /usr/lib from RPATH - honored
- >>
- >> here, path 2 would be the first used.
-
-Solaris 2.5.1 (SunOS 5.5.1) and 2.6 (SunOS 5.6)
- Apparently Solaris 2.5.1 patch 103663-01 installs a new
- /usr/include/resolv.h file that defines the __P macro without
- checking to see if it is already defined. This new resolv.h is also
- included in the Solaris 2.6 distribution. This causes compile
- warnings such as:
-
- In file included from daemon.c:51:
- /usr/include/resolv.h:208: warning: `__P' redefined
- cdefs.h:58: warning: this is the location of the previous definition
-
- These warnings can be safely ignored or you can create a resolv.h
- file in the obj.SunOS.5.5.1.* or obj.SunOS.5.6.* directory that reads:
-
- #undef __P
- #include "/usr/include/resolv.h"
-
- This problem was fixed in Solaris 7 (Sun bug ID 4081053).
-
-Solaris 7 (SunOS 5.7)
- Solaris 7 includes LDAP libraries but the implementation was
- 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.
-
-Solaris 8 and later (SunOS 5.8 and later)
- Solaris 8 and later can optionally install LDAP support. If you
- have installed the Entire Distribution meta-cluster, you can use
- the following in devtools/Site/site.SunOS.5.8.m4 (or other
- appropriately versioned file) to enable LDAP:
-
- APPENDDEF(`confMAPDEF', `-DLDAPMAP')
- APPENDDEF(`confLIBS', `-lldap')
-
-Solaris 9 and later (SunOS 5.9 and later)
- Solaris 9 and later have a revised LDAP library, libldap.so.5,
- which is derived from a Netscape implementation, thus requiring
- that SM_CONF_LDAP_MEMFREE be defined in conjunction with LDAPMAP:
-
- APPENDDEF(`confMAPDEF', `-DLDAPMAP')
- APPENDDEF(`confENVDEF', `-DSM_CONF_LDAP_MEMFREE')
- APPENDDEF(`confLIBS', `-lldap')
-
-Solaris
- If you are using dns for hostname resolution on Solaris, make sure
- that the 'dns' entry is last on the hosts line in
- '/etc/nsswitch.conf'. For example, use:
-
- hosts: nisplus files dns
-
- Do not use:
-
- hosts: nisplus dns [NOTFOUND=return] files
-
- Note that 'nisplus' above is an illustration. The same comment
- applies no matter what naming services you are using. If you have
- anything other than dns last, even after "[NOTFOUND=return]",
- sendmail may not be able to determine whether an error was
- temporary or permanent. The error returned by the solaris
- gethostbyname() is the error for the last lookup used, and other
- naming services do not have the same concept of temporary failure.
-
-Ultrix
- By default, the IDENT protocol is turned off on Ultrix. If you
- are running Ultrix 4.4 or later, or if you have included patch
- 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.
-
- 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.
-
- On DEC OSF/1 3.2 or earlier, the MatchGECOS option doesn't work
- properly due to a bug in the getpw* routines. If you want to use
- this, use -DDEC_OSF_BROKEN_GETPWENT=1. The problem is fixed in 3.2C.
-
- Digital's mail delivery agent, /bin/mail (aka /bin/binmail), will
- only preserve the envelope sender in the "From " header if
- DefaultUserID is set to daemon. Setting this to mailnull will
- cause all mail to have the header "From mailnull ...". To use
- a different DefaultUserID, you will need to use a different mail
- delivery agent (such as mail.local found in the sendmail
- distribution).
-
- On Digital UNIX 4.0 and later, Berkeley DB 1.85 is included with the
- operating system and already has the ndbm.o module removed. However,
- Digital has modified the original Berkeley DB db.h include file.
- This results in the following warning while compiling map.c and udb.c:
-
- cc: Warning: /usr/include/db.h, line 74: The redefinition of the macro
- "__signed" conflicts with a current definition because the replacement
- lists differ. The redefinition is now in effect.
- #define __signed signed
- ------------------------^
-
- This warning can be ignored.
-
- Digital UNIX's linker checks /usr/ccs/lib/ before /usr/lib/.
- If you have installed a new version of BIND in /usr/include
- and /usr/lib, you will experience difficulties as Digital ships
- libresolv.a in /usr/ccs/lib/ as well. Be sure to replace both
- copies of libresolv.a.
-
-IRIX
- The header files on SGI IRIX are completely prototyped, and as
- a result you can sometimes get some warning messages during
- compilation. These can be ignored. There are two errors in
- deliver only if you are using gcc, both of the form ``warning:
- passing arg N of `execve' from incompatible pointer type''.
- Also, if you compile with -DNIS, you will get a complaint
- about a declaration of struct dom_binding in a prototype
- when compiling map.c; this is not important because the
- function being prototyped is not used in that file.
-
- In order to compile sendmail you will have had to install
- the developers' option in order to get the necessary include
- files.
-
- If you compile with -lmalloc (the fast memory allocator), you may
- get warning messages such as the following:
-
- ld32: WARNING 85: definition of _calloc in /usr/lib32/libmalloc.so
- preempts that definition in /usr/lib32/mips3/libc.so.
- ld32: WARNING 85: definition of _malloc in /usr/lib32/libmalloc.so
- preempts that definition in /usr/lib32/mips3/libc.so.
- ld32: WARNING 85: definition of _realloc in /usr/lib32/libmalloc.so
- preempts that definition in /usr/lib32/mips3/libc.so.
- ld32: WARNING 85: definition of _free in /usr/lib32/libmalloc.so
- preempts that definition in /usr/lib32/mips3/libc.so.
- ld32: WARNING 85: definition of _cfree in /usr/lib32/libmalloc.so
- preempts that definition in /usr/lib32/mips3/libc.so.
-
- These are unavoidable and innocuous -- just ignore them.
-
- According to Dave Sill <de5@ornl.gov>, there is a version of the
- Berkeley DB library patched to run on Irix 6.2 available from
- http://reality.sgi.com/ariel/freeware/#db .
-
-IRIX 6.x
- If you are using XFS filesystem, avoid using the -32 ABI switch to
- the cc compiler if possible.
-
- Broken inet_aton and inet_ntoa on IRIX using gcc: There's
- a problem with gcc on IRIX, i.e., gcc can't pass structs
- less than 16 bits long unless they are 8 bits; IRIX 6.2 has
- some other sized structs. See
- http://www.bitmechanic.com/mail-archives/mysql/current/0418.html
- This problem seems to be fixed by gcc v2.95.2, gcc v2.8.1
- is reported as broken. Check your gcc version for this bug
- before installing sendmail.
-
-IRIX 6.4
- The IRIX 6.5.4 version of /bin/m4 does not work properly with
- 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,
- Berkeley DB does not currently run on NEXTSTEP.
-
- If you are compiling on NEXTSTEP, you will have to create an
- empty file "unistd.h" and create a file "dirent.h" containing:
-
- #include <sys/dir.h>
- #define dirent direct
-
- (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
- message "SYSERR: service "smtp" unknown" logged. You should
- be able to work around this by including the line:
-
- OOPort=25
-
- in your .cf file.
-
-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.
-
- The M4 shipped in FreeBSD and NetBSD 0.9 don't handle the config
- files properly. One must use either GNU m4 1.1 or the PD-M4
- recently posted in comp.os.386bsd.bugs (and maybe others).
- NetBSD-current includes the PD-M4 (as stated in the NetBSD file
- CHANGES).
-
- FreeBSD 1.0 RELEASE has uname(2) now. Use -DUSEUNAME in order to
- use it (look into devtools/OS/FreeBSD). NetBSD-current may have
- 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 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
- a very old resolver and be missing some header files. The
- header files are simple -- create empty versions and everything
- will work fine. For the resolver you should really port a new
- version (4.8.3 or later) of the resolver; 4.9 is available on
- gatekeeper.DEC.COM in pub/BSD/bind/4.9. If you are really
- determined to continue to use your old, buggy version (or as
- a shortcut to get sendmail working -- I'm sure you have the
- best intentions to port a modern version of BIND), you can
- copy ../contrib/oldbind.compat.c into sendmail and add the
- following to devtools/Site/site.config.m4:
-
- APPENDDEF(`confOBJADD', `oldbind.compat.o')
-
-OpenBSD (up to 2.9 Release), NetBSD, FreeBSD (up to 4.3-RELEASE)
- m4 from *BSD won't handle libsm/Makefile.m4 properly, since the
- maximum length for strings is too short. You need to use GNU m4
- or patch m4, see for example:
- http://FreeBSD.org/cgi/cvsweb.cgi/src/usr.bin/m4/eval.c.diff?r1=1.11&r2=1.12
-
-A/UX
- Date: Tue, 12 Oct 1993 18:28:28 -0400 (EDT)
- From: "Eric C. Hagberg" <hagberg@med.cornell.edu>
- Subject: Fix for A/UX ndbm
-
- I guess this isn't really a sendmail bug, however, it is something
- that A/UX users should be aware of when compiling sendmail 8.6.
-
- Apparently, the calls that sendmail is using to the ndbm routines
- in A/UX 3.0.x contain calls to "broken" routines, in that the
- aliases database will break when it gets "just a little big"
- (sorry I don't have exact numbers here, but it broke somewhere
- around 20-25 aliases for me.), making all aliases non-functional
- after exceeding this point.
-
- What I did was to get the gnu-dbm-1.6 package, compile it, and
- then re-compile sendmail with "-lgdbm", "-DNDBM", and using the
- ndbm.h header file that comes with the gnu-package. This makes
- things behave properly.
- [NOTE: see comment above about GDBM]
-
- I suppose porting the New Berkeley DB package is another route,
- however, I made a quick attempt at it, and found it difficult
- (not easy at least); the gnu-dbm package "configured" and
- compiled easily.
-
- [NOTE: Berkeley DB version 2.X runs on A/UX and can be used for
- database maps.]
-
-SCO Unix
- From: Thomas Essebier <tom@stallion.oz.au>
- Organisation: Stallion Technologies Pty Ltd.
-
- It will probably help those who are trying to configure sendmail 8.6.9
- to know that if they are on SCO, they had better set
- OI-dnsrch
- or they will core dump as soon as they try to use the resolver.
- i.e., although SCO has _res.dnsrch defined, and is kinda BIND 4.8.3,
- it does not inititialise it, nor does it understand 'search' in
- /etc/named.boot.
- - sigh -
-
- According to SCO, the m4 which ships with UnixWare 2.1.2 is broken.
- We recommend installing GNU m4 before attempting to build sendmail.
-
- On some versions a bogus error value is listed if connections
- time out (large negative number). To avoid this explicitly set
- Timeout.connect to a reasonable value (several minutes).
-
-DG/UX
- Doug Anderson <dlander@afterlife.ncsc.mil> has successfully run
- V8 on the DG/UX 5.4.2 and 5.4R3.x platforms under heavy usage.
- Originally, the DG /bin/mail program wasn't compatible with
- the V8 sendmail, since the DG /bin/mail requires the environment
- variable "_FORCE_MAIL_LOCAL_=yes" be set. Version 8.7 now includes
- this in the environment before invoking the local mailer. Some
- have used procmail to avoid this problem in the past. It works
- but some have experienced file locking problems with their DG/UX
- ports of procmail.
-
-Apollo DomainOS
- If you are compiling on Apollo, you will have to create an empty
- file "unistd.h" (for DomainOS 10.3 and earlier) and create a file
- "dirent.h" containing:
-
- #include <sys/dir.h>
- #define dirent direct
-
- (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
- From: Kimmo Suominen <Kimmo.Suominen@lut.fi>
- Subject: 8.6.5 w/ HP-UX 8.00 on s300
-
- Just compiled and fought with sendmail 8.6.5 on a HP9000/360 (i.e.,
- a series 300 machine) running HP-UX 8.00.
-
- I was getting segmentation fault when delivering to a local user.
- With debugging I saw it was faulting when doing _free@libc... *sigh*
- It seems the new implementation of malloc on s300 is buggy as of 8.0,
- so I tried out the one in -lmalloc (malloc(3X)). With that it seems
- to work just dandy.
-
- When linking, you will get the following error:
-
- ld: multiply defined symbol _freespace in file /usr/lib/libmalloc.a
-
- but you can just ignore it. You might want to add this info to the
- README file for the future...
-
-Linux
- Something broke between versions 0.99.13 and 0.99.14 of Linux: the
- flock() system call gives errors. If you are running .14, you must
- not use flock. You can do this with -DHASFLOCK=0. We have also
- been getting complaints since version 2.4.X was released.
- sendmail 8.13 has changed the default locking method to fcntl()
- for Linux kernel version 2.4 and later. Be sure to update other
- sendmail related programs to match locking techniques (some
- examples, besides makemap and mail.local, include procmail, mailx,
- mutt, elm, etc).
-
- Around the inclusion of bind-4.9.3 & Linux libc-4.6.20, the
- initialization of the _res structure changed. If /etc/hosts.conf
- was configured as "hosts, bind" the resolver code could return
- "Name server failure" errors. This is supposedly fixed in
- later versions of libc (>= 4.6.29?), and later versions of
- sendmail (> 8.6.10) try to work around the problem.
-
- Some older versions (< 4.6.20?) of the libc/include files conflict
- with sendmail's version of cdefs.h. Deleting sendmail's version
- on those systems should be non-harmful, and new versions don't care.
-
- NOTE ON LINUX & BIND: By default, the Makefile generated for Linux
- includes header files in /usr/local/include and libraries in
- /usr/local/lib. If you've installed BIND on your system, the header
- files typically end up in the search path and you need to add
- "-lresolv" to the LIBS line in your Makefile. Really old versions
- may need to include "-l44bsd" as well (particularly if the link phase
- complains about missing strcasecmp, strncasecmp or strpbrk).
- Complaints about an undefined reference to `__dn_skipname' in
- domain.o are a sure sign that you need to add -lresolv to LIBS.
- Newer versions of Linux are basically threaded BIND, so you may or
- may not see complaints if you accidentally mix BIND
- headers/libraries with virginal libc. If you have BIND headers in
- /usr/local/include (resolv.h, etc) you *should* be adding -lresolv
- to LIBS. Data structures may change and you'd be asking for a
- core dump.
-
- A number of problems have been reported regarding the Linux 2.2.0
- kernel. So far, these problems have been tracked down to syslog()
- and DNS resolution. We believe the problem is with the poll()
- implementation in the Linux 2.2.0 kernel and poll()-aware versions
- of glib (at least up to 2.0.111).
-
-glibc
- glibc 2.2.1 (and possibly other versions) changed the value of
- __RES in resolv.h but failed to actually provide the IPv6 API
- changes that the change implied. Therefore, compiling with
- -DNETINET6 fails.
-
- Workarounds:
- 1) Compile without -DNETINET6
- 2) Build against a real BIND 8.2.2 include/lib tree
- 3) Wait for glibc to fix it
-
-AIX 4.X
- The AIX 4.X linker uses library paths specified during compilation
- using -L for run-time shared library searches. Therefore, it is
- 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
- appropriately. 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.X If the test program t-event (and most others) in libsm fails,
- check your compiler settings. It seems that the flags -qnoro or
- -qnoroconst on some AIX versions trigger a compiler bug. Check
- your compiler settings or use cc instead of xlc.
-
-AIX 4.0-4.2, maybe some AIX 4.3 versions
- The AIX m4 implements a different mechanism for ifdef which is
- inconsistent with other versions of m4. Therefore, it will not
- work properly with the sendmail Build architecture or m4
- configuration method. To work around this problem, please use
- GNU m4 from ftp://ftp.gnu.org/pub/gnu/.
- The problem seems to be solved in AIX 4.3.3 at least.
-
-AIX 4.3.3
- From: Valdis.Kletnieks@vt.edu
- Date: Sun, 02 Jul 2000 03:58:02 -0400
-
- Under AIX 4.3.3, after applying bos.adt.include 4.3.3.12 to close the
- BIND 8.2.2 security holes, you can no longer build with -DNETINET6
- because they changed the value of __RES in resolv.h but failed to
- actually provide the API changes that the change implied.
-
- Workarounds:
- 1) Compile without -DNETINET6
- 2) Build against a real BIND 8.2.2 include/lib tree
- 3) Wait for IBM to fix it
-
-AIX 3.x
- This version of sendmail does not support MB, MG, and MR resource
- records, which are supported by AIX sendmail.
-
- Several people have reported that the IBM-supplied named returns
- fairly random results -- the named should be replaced. It is not
- necessary to replace the resolver, which will simplify installation.
- A new BIND resolver can be found at http://www.isc.org/isc/.
-
-AIX 3.1.x
- The supplied load average code only works correctly for AIX 3.2.x.
- For 3.1, use -DLA_TYPE=LA_SUBR and get the latest ``monitor''
- package by Jussi Maki <jmaki@hut.fi> from ftp.funet.fi in the
- directory pub/unix/AIX/rs6000/monitor-1.12.tar.Z; use the loadavgd
- daemon, and the getloadavg subroutine supplied with that package.
- If you don't care about load average throttling, just turn off
- load average checking using -DLA_TYPE=LA_ZERO.
-
-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 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 devtools/Site/site.config.m4
- file.
-
- It's been tested on Dell Issue 2.2.
-
-DELL SVR4
- Date: Mon, 06 Dec 1993 10:42:29 EST
- From: "Kimmo Suominen" <kim@grendel.lut.fi>
- Message-ID: <2d0352f9.lento29@lento29.UUCP>
- To: eric@cs.berkeley.edu
- Cc: sendmail@cs.berkeley.edu
- Subject: Notes for DELL SVR4
-
- Eric,
-
- Here are some notes for compiling Sendmail 8.6.4 on DELL SVR4. I ran
- across these things when helping out some people who contacted me by
- e-mail.
-
- 1) Use gcc 2.4.5 (or later?). Dell distributes gcc 2.1 with their
- Issue 2.2 Unix. It is too old, and gives you problems with
- clock.c, because sigset_t won't get defined in <sys/signal.h>.
- This is due to a problematic protection rule in there, and is
- fixed with gcc 2.4.5.
-
- 2) If you don't use the new Berkeley DB (-DNEWDB), then you need
- to add "-lc -lucb" to the libraries to link with. This is because
- the -ldbm distributed by Dell needs the bcopy, bcmp and bzero
- functions. It is important that you specify both libraries in
- the given order to be sure you only get the BSTRING functions
- from the UCB library (and not the signal routines etc.).
-
- 3) Don't leave out "-lelf" even if compiling with "-lc -lucb".
- The UCB library also has another copy of the nlist routines,
- but we do want the ones from "-lelf".
-
- If anyone needs a compiled gcc 2.4.5 and/or a ported DB library, they
- can use anonymous ftp to fetch them from lut.fi in the /kim directory.
- They are copies of what I use on grendel.lut.fi, and offering them
- does not imply that I would also support them. I have sent the DB
- port for SVR4 back to Keith Bostic for inclusion in the official
- distribution, but I haven't heard anything from him as of today.
-
- - gcc-2.4.5-svr4.tar.gz (gcc 2.4.5 and the corresponding libg++)
- - db-1.72.tar.gz (with source, objects and a installed copy)
-
- Cheers
- + Kim
- --
- * Kimmo.Suominen@lut.fi * SysVr4 enthusiast at GRENDEL.LUT.FI *
- * KIM@FINFILES.BITNET * Postmaster and Hostmaster at LUT.FI *
- * + 358 200 865 718 * Unix area moderator at NIC.FUNET.FI *
-
-ConvexOS 10.1 and below
- In order to use the name server, you must create the file
- /etc/use_nameserver. If this file does not exist, the call
- to res_init() will fail and you will have absolutely no
- access to DNS, including MX records.
-
-Amdahl UTS 2.1.5
- In order to get UTS to work, you will have to port BIND 4.9.
- The vendor's BIND is reported to be ``totally inadequate.''
- See sendmail/contrib/AmdahlUTS.patch for the patches necessary
- to get BIND 4.9 compiled for UTS.
-
-UnixWare
- According to Alexander Kolbasov <sasha@unitech.gamma.ru>,
- the m4 on UnixWare 2.0 (still in Beta) will core dump on the
- config files. GNU m4 and the m4 from UnixWare 1.x both work.
-
- 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.
-
- I had a LOCAL_RULE_0 that wound up AFTER the
- SBasic_check_rcpt rules using the SCO supplied M4.
- GNU M4 works fine.
-
-UNICOS 8.0.3.4
- Some people have reported that the -O flag on UNICOS can cause
- problems. You may want to turn this off if you have problems
- running sendmail. Reported by Jerry G. DeLapp <jgd@acl.lanl.gov>.
-
-Darwin/Mac OS X (10.X.X)
- The linker errors produced regarding getopt() and its associated
- variables can safely be ignored.
-
- From Mike Zimmerman <zimmy@torrentnet.com>:
-
- From scratch here is what Darwin users need to do to the standard
- 10.0.0, 10.0.1 install to get sendmail working.
- From http://www.macosx.com/forums/showthread.php?s=6dac0e9e1f3fd118a4870a8a9b559491&threadid=2242:
- 1. chmod g-w / /private /private/etc
- 2. Properly set HOSTNAME in /etc/hostconfig to your FQDN:
- HOSTNAME=-my.domain.com-
- 3. Edit /etc/rc.boot:
- hostname my.domain.com
- domainname domain.com
- 4. Edit /System/Library/StartupItems/Sendmail/Sendmail:
- Remove the "&" after the sendmail command:
- /usr/sbin/sendmail -bd -q1h
-
- From Carsten Klapp <carsten.klapp@home.com>:
-
- The easiest workaround is to remove the group-writable permission
- for the root directory and the symbolic /etc inherits this
- change. While this does fix sendmail, the unfortunate side-effect
- is the OS X admin will no longer be able to manipulate icons in the
- top level of the Startup disk unless logged into the GUI as the
- superuser.
-
- In applying the alternate workaround, care must be taken while
- swapping the symlink /etc with the directory /private/etc. In all
- likelihood any admin who is concerned with this sendmail error has
- enough experience to not accidentally harm anything in the process.
-
- a. Swap the /etc symlink with /private/etc (as superuser):
- rm /etc
- mv /private/etc /etc
- ln -s /etc /private/etc
-
- b. Set / to group unwritable (as superuser):
- chmod g-w /
-
-Darwin/Mac OS X (10.1.5)
- Apple's upgrade to sendmail 8.12 is incorrectly configured. You
- will need to manually fix it up by doing the following:
-
- 1. chown smmsp:smmsp /var/spool/clientmqueue
- 2. chmod 2770 /var/spool/clientmqueue
- 3. chgrp smmsp /usr/sbin/sendmail
- 4. chmod g+s /usr/sbin/sendmail
-
- From Daniel J. Luke <dluke@geeklair.net>:
-
- It appears that setting the sendmail.cf property in
- /locations/sendmail in NetInfo on Mac OS X 10.1.5 with sendmail
- 8.12.4 causes 'bad things' to happen.
-
- Specifically sendmail instances that should be getting their config
- from /etc/mail/submit.cf don't (so mail/mutt/perl scripts which
- open pipes to sendmail stop working as sendmail tries to write to
- /var/spool/mqueue and cannot as sendmail is no longer suid root).
-
- Removing the entry from NetInfo fixes this problem.
-
-GNU getopt
- I'm told that GNU getopt has a problem in that it gets confused
- by the double call. Use the version in conf.c instead.
-
-BIND 4.9.2 and Ultrix
- If you are running on Ultrix, be sure you read conf/Info.Ultrix
- in the BIND distribution very carefully -- there is information
- in there that you need to know in order to avoid errors of the
- form:
-
- /lib/libc.a(gethostent.o): sethostent: multiply defined
- /lib/libc.a(gethostent.o): endhostent: multiply defined
- /lib/libc.a(gethostent.o): gethostbyname: multiply defined
- /lib/libc.a(gethostent.o): gethostbyaddr: multiply defined
-
- 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
- has this problem, you will get an error in srvrsmtp.c on the
- code:
-
- # ifdef defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
- e->e_msgsize = strtoul(vp, (char **) NULL, 10);
- # else
- e->e_msgsize = strtol(vp, (char **) NULL, 10);
- # endif
-
- You can use -DBROKEN_ANSI_LIBRARY to get around this problem.
-
-Listproc 6.0c
- Date: 23 Sep 1995 23:56:07 GMT
- Message-ID: <95925101334.~INN-AUMa00187.comp-news@dl.ac.uk>
- From: alansz@mellers1.psych.berkeley.edu (Alan Schwartz)
- Subject: Listproc 6.0c + Sendmail 8.7 [Helpful hint]
-
- Just upgraded to sendmail 8.7, and discovered that listproc 6.0c
- breaks, because it, by default, sends a blank "HELO" rather than
- a "HELO hostname" when using the 'system' or 'telnet' mail method.
-
- The fix is to include -DZMAILER in the compilation, which will
- cause it to use "HELO hostname" (which Z-mail apparently requires
- as well. :)
-
-OpenSSL
- OpenSSL versions prior to 0.9.6 use a macro named Free which
- conflicts with existing macro names on some platforms, such as
- AIX.
- Do not use 0.9.3, but OpenSSL 0.9.5a or later if compatible with
- 0.9.5a.
-
-PH
- PH support is provided by Mark Roth <roth@uiuc.edu>. The map is
- described at http://www-dev.cites.uiuc.edu/sendmail/ .
-
- NOTE: The "spacedname" pseudo-field which was used by earlier
- versions of the PH map code is no longer supported! See the URL
- listed above for more information.
-
- Please contact Mark Roth for support and questions regarding the
- map.
-
-TCP Wrappers
- If you are using -DTCPWRAPPERS to get TCP Wrappers support you will
- also need to install libwrap.a and modify your site.config.m4 file
- or the generated Makefile to include -lwrap in the LIBS line
- (make sure that INCDIRS and LIBDIRS point to where the tcpd.h and
- libwrap.a can be found).
-
- 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
- you reject will connect to your site, fail, and move on to the next
- MX site, which will accept the mail for you and forward it on to you.
-
-Regular Expressions (MAP_REGEX)
- If sendmail linking fails with:
-
- undefined reference to 'regcomp'
-
- or sendmail gives an error about a regular expression with:
-
- 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.
-
-Fedora Core 5, 64 bit version
- If the ld stage fails with undefined functions like
- __res_querydomain, __dn_expand
- then add these lines to devtools/Site/site.config.m4
-
- APPENDDEF(`confLIBDIRS', `-L/usr/lib64')
- APPENDDEF(`confINCDIRS', `-I/usr/include/bind9')
-
- and rebuild (sh ./Build -c).
-
- Problem noted by Daniel Krones, solution suggested by
- Anthony Howe.
-
-+--------------+
-| MANUAL PAGES |
-+--------------+
-
-The manual pages have been written against the -man macros, and
-should format correctly with any reasonable *roff.
-
-
-+-----------------+
-| DEBUGGING HOOKS |
-+-----------------+
-
-As of 8.6.5, sendmail daemons will catch a SIGUSR1 signal and log
-some debugging output (logged at LOG_DEBUG severity). The
-information dumped is:
-
- * The value of the $j macro.
- * A warning if $j is not in the set $=w.
- * A list of the open file descriptors.
- * The contents of the connection cache.
- * If ruleset 89 is defined, it is evaluated and the results printed.
-
-This allows you to get information regarding the runtime state of the
-daemon on the fly. This should not be done too frequently, since
-the process of rewriting may lose memory which will not be recovered.
-Also, ruleset 89 may call non-reentrant routines, so there is a small
-non-zero probability that this will cause other problems. It is
-really only for debugging serious problems.
-
-A typical formulation of ruleset 89 would be:
-
- R$* $@ $>0 some test address
-
-
-+-----------------------------+
-| DESCRIPTION OF SOURCE FILES |
-+-----------------------------+
-
-The following list describes the files in this directory:
-
-Build Shell script for building sendmail.
-Makefile A convenience for calling ./Build.
-Makefile.m4 A template for constructing a makefile based on the
- information in the devtools directory.
-README This file.
-TRACEFLAGS My own personal list of the trace flags -- not guaranteed
- to be particularly up to date.
-alias.c Does name aliasing in all forms.
-aliases.5 Man page describing the format of the aliases file.
-arpadate.c A subroutine which creates ARPANET standard dates.
-bf.c Routines to implement memory-buffered file system using
- hooks provided by libsm now (formerly Torek stdio library).
-bf.h Buffered file I/O function declarations and
- data structure and function declarations for bf.c.
-collect.c The routine that actually reads the mail into a temp
- file. It also does a certain amount of parsing of
- the header, etc.
-conf.c The configuration file. This contains information
- that is presumed to be quite static and non-
- controversial, or code compiled in for efficiency
- reasons. Most of the configuration is in sendmail.cf.
-conf.h Configuration that must be known everywhere.
-control.c Routines to implement control socket.
-convtime.c A routine to sanely process times.
-daemon.c Routines to implement daemon mode.
-deliver.c Routines to deliver mail.
-domain.c Routines that interface with DNS (the Domain Name
- System).
-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.
-sasl.c Routines to interact with Cyrys-SASL.
-savemail.c Routines which save the letter on processing errors.
-sendmail.8 Man page for the sendmail command.
-sendmail.h Main header file for sendmail.
-sfsasl.c I/O interface between SASL/TLS and the MTA.
-sfsasl.h Header file for sfsasl.c.
-shmticklib.c Routines for shared memory counters.
-sm_resolve.c Routines for DNS lookups (for DNS map type).
-sm_resolve.h Header file for sm_resolve.c.
-srvrsmtp.c Routines to implement server SMTP.
-stab.c Routines to manage the symbol table.
-stats.c Routines to collect and post the statistics.
-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.
-tls.c Routines for TLS.
-trace.c The trace package. These routines allow setting and
- testing of trace flags with a high granularity.
-udb.c The user database interface module.
-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.
-
-(Version $Revision: 8.390 $, last update $Date: 2006/11/13 22:27:27 $ )
diff --git a/contrib/sendmail/src/SECURITY b/contrib/sendmail/src/SECURITY
deleted file mode 100644
index 0445e44..0000000
--- a/contrib/sendmail/src/SECURITY
+++ /dev/null
@@ -1,203 +0,0 @@
-# Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
-# All rights reserved.
-#
-# By using this file, you agree to the terms and conditions set
-# forth in the LICENSE file which can be found at the top level of
-# the sendmail distribution.
-#
-# $Id: SECURITY,v 1.51 2002/09/23 21:29:18 ca Exp $
-#
-
-This file gives some hints how to configure and run sendmail for
-people who are very security conscious (you should be...).
-
-Even though sendmail goes through great lengths to assure that it
-can't be compromised even if the system it is running on is
-incorrectly or insecurely configured, it can't work around everything.
-This has been demonstrated by recent OS problems which have
-subsequently been used to compromise the root account using sendmail
-as a vector. One way to minimize the possibility of such problems
-is to install sendmail without set-user-ID root, which avoids local
-exploits. This configuration, which is the default starting with
-8.12, is described in the first section of this security guide.
-
-
-*****************************************************
-** sendmail configuration without set-user-ID root **
-*****************************************************
-
-sendmail needs to run as root for several purposes:
-
-- bind to port 25
-- call the local delivery agent (LDA) as root (or other user) if the LDA
- isn't set-user-ID root (unless some other method of storing e-mail in
- local mailboxes is used).
-- read .forward files
-- write e-mail submitted via the command line to the queue directory.
-
-Only the last item requires a set-user-ID/set-group-ID program to
-avoid problems with a world-writable directory. It is however
-sufficient to have a set-group-ID program and a group-writable
-queue directory. The other requirements listed above can be
-fulfilled by a sendmail daemon that is started by root. Hence this
-section explains how to use two sendmail configurations to accomplish
-the goal to have a sendmail binary that is not set-user-ID root,
-and hence is not open to system configuration/OS problems or at
-least less problematic in presence of those.
-
-The default configuration starting with sendmail 8.12 uses one
-sendmail binary which acts differently based on operation mode and
-supplied options.
-
-sendmail must be a set-group-ID (default group: smmsp, recommended
-gid: 25) program to allow for queueing mail in a group-writable
-directory. Two .cf files are required: sendmail.cf for the daemon
-and submit.cf for the submission program. The following permissions
-should be used:
-
--r-xr-sr-x root smmsp ... /PATH/TO/sendmail
-drwxrwx--- smmsp smmsp ... /var/spool/clientmqueue
-drwx------ root wheel ... /var/spool/mqueue
--r--r--r-- root wheel ... /etc/mail/sendmail.cf
--r--r--r-- root wheel ... /etc/mail/submit.cf
-
-[Notice: On some OS "wheel" is not used but "bin" or "root" instead,
-however, this is not important here.]
-
-That is, the owner of sendmail is root, the group is smmsp, and
-the binary is set-group-ID. The client mail queue is owned by
-smmsp with group smmsp and is group writable. The client mail
-queue directory must be writable by smmsp, but it must not be
-accessible for others. That is, do not use world read or execute
-permissions. In submit.cf the option UseMSP must be set, and
-QueueFileMode must be set to 0660. submit.cf is available in
-cf/cf/, which has been built from cf/cf/submit.mc. The file can
-be used as-is, if you want to add more options, use cf/cf/submit.mc
-as starting point and read cf/README: MESSAGE SUBMISSION PROGRAM
-carefully.
-
-The .cf file is chosen based on the operation mode. For -bm (default),
--bs, and -t it is submit.cf (if it exists) for all others it is
-sendmail.cf. This selection can be changed by -Ac or -Am (alternative
-.cf file: client or mta).
-
-The daemon must be started by root as usual, e.g.,
-
-/PATH/TO/sendmail -L sm-mta -bd -q1h
-
-(replace /PATH/TO with the right path for your OS, e.g.,
-/usr/sbin or /usr/lib).
-
-Notice: if you run sendmail from inetd (which in general is not a
-good idea), you must specify -Am in addition to -bs.
-
-Mail will end up in the client queue if the daemon doesn't accept
-connections or if an address is temporarily not resolvable. The
-latter problem can be minimized by using
-
- FEATURE(`nocanonify', `canonify_hosts')
- define(`confDIRECT_SUBMISSION_MODIFIERS', `C')
-
-which, however, may have undesired side effects. See cf/README for
-a discussion. In general it is necessary to clean the queue either
-via a cronjob or by running a daemon, e.g.,
-
-/PATH/TO/sendmail -L sm-msp-queue -Ac -q30m
-
-If the option UseMSP is not set, sendmail will complain during
-queue runs about bogus file permission. If you want a queue runner
-for the client queue, you probably have to change OS specific
-scripts to accomplish this (check the man pages of your OS for more
-information.) You can start this program as root, it will change
-its user id to RunAsUser (smmsp by default, recommended uid: 25).
-This way smmsp does not need a valid shell.
-
-Summary
--------
-
-This is a brief summary how the two configuration files are used:
-
-sendmail.cf For the MTA (mail transmission agent)
- The MTA is started by root as daemon:
-
- /PATH/TO/sendmail -L sm-mta -bd -q1h
-
- it accepts SMTP connections (on ports 25 and 587 by default);
- it runs the main queue (/var/spool/mqueue by default).
-
-submit.cf For the MSP (mail submission program)
- The MSP is used to submit e-mails, hence it is invoked
- by programs (and maybe users); it does not run as SMTP
- daemon; it uses /var/spool/clientmqueue by default; it
- can be started to run that queue periodically:
-
- /PATH/TO/sendmail -L sm-msp-queue -Ac -q30m
-
-
-Hints and Troubleshooting
--------------------------
-
-RunAsUser: FEATURE(`msp') sets the option RunAsUser to smmsp.
-This user must have the group smmsp, i.e., the same group as the
-clientmqueue directory. If you specify a user whose primary group
-is not the same as that of the clientmqueue directory, then you
-should explicitly set the group, e.g.,
-
- FEATURE(`msp')
- define(`confRUN_AS_USER', `mailmsp:smmsp')
-
-STARTTLS: If sendmail is compiled with STARTTLS support on a platform
-that does not have HASURANDOMDEV defined, you either need to specify
-the RandFile option (as for the MTA), or you have to turn off
-STARTTLS in the MSP, e.g.,
-
- DAEMON_OPTIONS(`Name=NoMTA, Addr=127.0.0.1, M=S')
- FEATURE(`msp')
- CLIENT_OPTIONS(`Family=inet, Address=0.0.0.0, M=S')
-
-The first option is used to turn off STARTTLS when the MSP is
-invoked with -bs as some MUAs do.
-
-
-What doesn't work anymore
--------------------------
-
-Normal users can't use mailq anymore to see the MTA mail queue.
-There are several ways around it, e.g., changing QueueFileMode
-or giving users access via a program like sudo.
-
-sendmail -bv may give misleading output for normal users since it
-may not be able to access certain files, e.g., .forward files of
-other users.
-
-
-Alternative
------------
-
-Instead of having one set-group-ID binary, it is possible to use
-two with different permissions: one for message submission
-(set-group-ID), one acting as daemon etc, which is only executable
-by root. In that case it is possible to remove features from
-the message submission program to have a smaller binary.
-You can use
-
- sh ./Build install-sm-mta
-
-to install a sendmail program to act as daemon etc under the name
-sm-mta.
-
-Set-User-Id
------------
-
-If you really have to install sendmail set-user-ID root, first build
-the sendmail package normally using
-
- sh ./Build
-
-Then you can use
-
- sh ./Build install-set-user-id
-
-to install the package in the old (pre-8.12) way. Make sure that
-no submit.cf file is installed. See devtools/README about
-confSETUSERID_INSTALL which you need to define.
diff --git a/contrib/sendmail/src/TRACEFLAGS b/contrib/sendmail/src/TRACEFLAGS
deleted file mode 100644
index a6249fd..0000000
--- a/contrib/sendmail/src/TRACEFLAGS
+++ /dev/null
@@ -1,102 +0,0 @@
-# $Id: TRACEFLAGS,v 8.47 2006/09/11 22:36:32 ca Exp $
-0, 4 main.c main canonical name, UUCP node name, a.k.a.s
-0, 15 main.c main print configuration
-0, 44 util.c printav print address of each string
-0, 101 main.c main print version and exit
-1 main.c main print from person
-2 main.c finis
-3 conf.c getla, shouldqueue
-4 conf.c enoughspace
-5 clock.c setevent, clrevent, tick
-6 savemail.c savemail, returntosender
-7 queue.c queuename
-8 domain.c getmxrr, getcanonname
-9 daemon.c getauthinfo IDENT protocol
-9 daemon.c maphostname
-10 deliver.c deliver
-11 deliver.c openmailer, mailfile
-12 parseaddr.c remotename
-13 deliver.c sendall, sendenvelope
-14 headers.c commaize
-15 daemon.c getrequests
-16 daemon.c makeconnection
-17 deliver.c hostsignature
-17 domain.c mxrand
-18 usersmtp.c reply, smtpmessage, smtpinit, smtpmailfrom, smtpdata
-19 srvrsmtp.c smtp
-20 parseaddr.c parseaddr
-21 parseaddr.c rewrite
-22 parseaddr.c prescan
-23 main.c testmodeline
-24 parseaddr.c buildaddr, allocaddr
-25 recipient.c sendtolist
-26 recipient.c recipient
-27 alias.c alias
-27 alias.c readaliases
-27 alias.c forward
-27 recipient.c include
-28 udb.c udbexpand, udbsender
-29 parseaddr.c maplocaluser
-29 recipient.c recipient (local users), finduser
-30 collect.c collect
-30 collect.c eatfrom
-31 headers.c chompheader
-32 headers.c eatheader
-33 headers.c crackaddr
-34 headers.c putheader
-35 macro.c expand, define
-36 stab.c stab
-37 readcf.c (many)
-38 map.c initmaps, setupmaps (bogus map)
-39 map.c map_rewrite
-40 queue.c queueup, orderq, dowork
-41 queue.c orderq
-42 mci.c mci_get
-43 mime.c mime8to7
-44 recipient.c writable
-44 safefile.c safefile, safedirpath, filechanged
-45 envelope.c setsender
-46 envelope.c openxscript
-47 main.c drop_privileges
-48 parseaddr.c rscheck
-48 conf.c validate_connection
-49 conf.c checkcompat
-50 envelope.c dropenvelope
-51 queue.c unlockqueue
-52 main.c disconnect
-53 util.c xfclose
-54 err.c putoutmsg
-55 conf.c lockfile
-56 mci.c persistent host status
-57 util.c snprintf
-58 bf.c bf* routines
-59 parseaddr.c cataddr
-60 map.c
-61 conf.c sm_gethostbyname
-62 multiple file descriptor checking
-63 queue.c runqueue process watching
-64 multiple Milter
-65 main.c permission checks
-#if _FFR_ADAPTIVE_EOL
-66 srvrsmtp.c conformance checks
-#endif /* _FFR_ADAPTIVE_EOL */
-#if _FFR_QUEUE_SCHED_DBG
-69 queue.c scheduling
-#endif /* _FFR_QUEUE_SCHED_DBG */
-70 queue.c quarantining
-71,>99 milter.c quarantine on errors
-73 queue.c shared memory updates
-80 content length
-81 sun remote mode
-83 collect.c timeout
-84 deliver.c timeout
-85 map.c dprintf map
-91 mci.c syslogging of MCI cache information
-93,>99 * Prevent daemon connection fork for profiling/debugging
-94,>99 srvrsmtp.c cause commands to fail (for protocol testing)
-95 srvrsmtp.c AUTH
-95 usersmtp.c AUTH
-96 tls.c Activate SSL_CTX_set_info_callback()
-97 srvrsmtp.c Trace automode settings for I/O
-98 * timers
-99 main.c avoid backgrounding (no printed output)
diff --git a/contrib/sendmail/src/TUNING b/contrib/sendmail/src/TUNING
deleted file mode 100644
index fe9e694..0000000
--- a/contrib/sendmail/src/TUNING
+++ /dev/null
@@ -1,230 +0,0 @@
-# Copyright (c) 2001-2003 Sendmail, Inc. and its suppliers.
-# All rights reserved.
-#
-# By using this file, you agree to the terms and conditions set
-# forth in the LICENSE file which can be found at the top level of
-# the sendmail distribution.
-#
-# $Id: TUNING,v 1.21 2006/09/25 16:45:05 ca Exp $
-#
-
-********************************************
-** This is a DRAFT, comments are welcome! **
-********************************************
-
-
-If the default configuration of sendmail does not achieve the
-required performance, there are several configuration options that
-can be changed to accomplish higher performance. However, before
-those options are changed it is necessary to understand why the
-performance is not as good as desired. This may also involve hardware
-and software (OS) configurations which are not extensively explored
-in this document. We assume that your system is not limited by
-network bandwidth because optimizing for this situation is beyond
-the scope of this guide. In almost all other cases performance will
-be limited by disk I/O.
-
-
-This text assumes that all options which are mentioned here are
-familiar to the reader, they are explained in the Sendmail Installation
-and Operations Guide; doc/op/op.txt.
-
-There are basically three different scenarios which are treated
-in the following:
-* Mailing Lists and Large Aliases (1-n Mailing)
-* 1-1 Mass Mailing
-* High Volume Mail
-
-Depending on your requirements, these may need different options
-to optimize sendmail for the particular purpose. It is also possible
-to configure sendmail to achieve good performance in all cases, but
-it will not be optimal for any specific purpose. For example, it
-is non-trivial to combine low latency (fast delivery of incoming
-mail) with high overall throughput.
-
-Before we explore the different scenarios, a basic discussion about
-disk I/O, delivery modes, and queue control is required.
-
-
-* Disk I/O
------------------------------------------------
-
-In general mail will be written to disk before a delivery attempt
-is made. This is required for reliability and should only be changed
-in a few specific cases that are mentioned later on. To achieve
-better disk I/O performance the queue directories can be spread
-over several disks to distribute the load. This is some basic tuning
-that should be done in all cases where the I/O speed of a single
-disk is exceeded, which is true for almost every high-volume
-situation except if a special disk subsystem with large (NV)RAM
-buffer is used.
-
-Depending on your OS there might be ways to speed up I/O, e.g.,
-using softupdates or turning on the noatime mount option. If this
-is done make sure the filesystem is still reliable, i.e., if fsync()
-returns without an error, the file has really been committed to
-disk.
-
-
-* Queueing Strategies and DeliveryMode
------------------------------------------------
-
-There are basically three delivery modes:
-
-background: incoming mail will be immediately delivered by a new process
-interactive: incoming mail will be immediately delivered by the same process
-queue: incoming mail will be queued and delivered by a queue runner later on
-
-The first offers the lowest latency without the disadvantage of the
-second, which keeps the connection from the sender open until the
-delivery to the next hop succeeded or failed. However, it does not
-allow for a good control over the number of delivery processes other
-than limiting the total number of direct children of the daemon
-processes (MaxChildren) or by load control options (RefuseLA,
-DelayLA). Moreover, it can't make as good use as 'queue' mode can
-for connection caching.
-
-Interactive DeliveryMode should only be used in rare cases, e.g.,
-if the delivery time to the next hop is a known quantity or if the
-sender is under local control and it does not matter if it has to
-wait for delivery.
-
-Queueing up e-mail before delivery is done by a queue runner allows
-the best load control but does not achieve as low latency as the
-other two modes. However, this mode is probably also best for
-concurrent delivery since the number of queue runners can be specified
-on a queue group basis. Persistent queue runners (-qp) can be used
-to minimize the overhead for creating processes because they just
-sleep for the specified interval (which should be short) instead of
-exiting after a queue run.
-
-
-* Queue Groups
------------------------------------------------
-
-In most situations disk I/O is a bottleneck which can be mitigated
-by spreading the load over several disks. This can easily be achieved
-with different queue directories. sendmail 8.12 introduces queue
-groups which are collections of queue directories with similar
-properties, i.e., number of processes to run the queues in the
-group, maximum number of recipients within an e-mail (envelope),
-etc. Queue groups allow control over the behaviour of different
-queues. Depending on the setup, it is usually possible to have
-several queue runners delivering mails concurrently which should
-increase throughput. The number of queue runners can be controlled
-per queue group (Runner=) and overall (MaxQueueChildren).
-
-
-* DNS Lookups
------------------------------------------------
-
-sendmail performs by default host name canonifications by using
-host name lookups. This process is meant to replace unqualified
-host name with qualified host names, and CNAMEs with the non-aliased
-name. However, these lookups can take a while for large address
-lists, e.g., mailing lists. If you can assure by other means that
-host names are canonical, you should use
-
- FEATURE(`nocanonify', `canonify_hosts')
-
-in your .mc file. For further information on this feature and
-additional options see cf/README. If sendmail is invoked directly
-to send e-mail then either the -G option should be used or
-
- define(`confDIRECT_SUBMISSION_MODIFIERS', `C')
-
-should be added to the .mc file.
-
-
-* Mailing Lists and Large Aliases (1-n Mailing)
------------------------------------------------
-
-Before 8.12 sendmail would deliver an e-mail sequentially to all its
-recipients. For mailing lists or large aliases the overall delivery
-time can be substantial, especially if some of the recipients are
-located at hosts that are slow to accept e-mail. Some mailing list
-software therefore "split" up e-mails into smaller pieces with
-fewer recipients. sendmail 8.12 can do this itself, either across
-queue groups or within a queue directory. The latter is controlled
-by the 'r=' field of a queue group declaration.
-
-Let's assume a simple example: a mailing list where most of the
-recipients are at three domains: the local one (local.domain) and
-two remotes (one.domain, two.domain) and the rest is splittered
-over several other domains. For this case it is useful to specify
-three queue groups:
-
-QUEUE_GROUP(`local', `P=/var/spool/mqueue/local, F=f, R=2, I=1m')dnl
-QUEUE_GROUP(`one', `P=/var/spool/mqueue/one, F=f, r=50, R=3')dnl
-QUEUE_GROUP(`two', `P=/var/spool/mqueue/two, F=f, r=30, R=4')dnl
-QUEUE_GROUP(`remote', `P=/var/spool/mqueue/remote, F=f, r=5, R=8, I=2m')dnl
-define(`ESMTP_MAILER_QGRP', `remote')dnl
-define(`confDELIVERY_MODE', `q')dnl
-define(`confMAX_QUEUE_CHILDREN', `50')dnl
-define(`confMIN_QUEUE_AGE', `27m')dnl
-
-and specify the queuegroup ruleset as follows:
-
-LOCAL_RULESETS
-Squeuegroup
-R$* @ local.domain $# local
-R$* @ $* one.domain $# one
-R$* @ $* two.domain $# two
-R$* @ $* $# remote
-R$* $# mqueue
-
-Now it is necessary to control the number of queue runners, which
-is done by MaxQueueChildren. Starting the daemon with the option
--q5m assures that the first delivery attempt for each e-mail is
-done within 5 minutes, however, there are also individual queue
-intervals for the queue groups as specified above. MinQueueAge
-is set to 27 minutes to avoid that entries are run too often.
-
-Notice: if envelope splitting happens due to alias expansion, and
-DeliveryMode is not 'i'nteractive, then only one envelope is sent
-immediately. The rest (after splitting) are queued up and queue
-runners must come along and take care of them. Hence it is essential
-that the queue interval is very short.
-
-
-* 1-1 Mass Mailing
------------------------------------------------
-
-In this case some program generates e-mails which are sent to
-individual recipients (or at most very few per e-mail). A simple
-way to achieve high throughput is to set the delivery mode to
-'interactive', turn off the SuperSafe option and make sure that the
-program that generates the mails can deal with mail losses if the
-server loses power. In no other case should SuperSafe be set to
-'false'. If these conditions are met, sendmail does not need to
-commit mails to disk but can buffer them in memory which will greatly
-enhance performance, especially compared to normal disk subsystems, e.g.,
-non solid-state disks.
-
-
-* High Volume Mail
------------------------------------------------
-
-For high volume mail it is necessary to be able to control the load
-on the system. Therefore the 'queue' delivery mode should be used,
-and all options related to number of processes and the load should
-be set to reasonable values. It is important not to accept mail
-faster than it can be delivered; otherwise the system will be
-overwhelmed. Hence RefuseLA should be lower than QueueLA, the number
-of daemon children should probably be lower than the number of queue
-runners (MaxChildren vs. MaxQueueChildren). DelayLA is a new option
-in 8.12 which allows delaying connections instead of rejecting them.
-This may result in a smoother load distribution depending on how
-the mails are submitted to sendmail.
-
-
-* Miscellaneous
------------------------------------------------
-
-Other options that are interesting to tweak performance are
-(in no particular order):
-
-SuperSafe: if interactive DeliveryMode is used, then this can
-be set to the new value "interactive" in 8.12 to save some disk
-synchronizations which are not really necessary in that mode.
-
diff --git a/contrib/sendmail/src/alias.c b/contrib/sendmail/src/alias.c
deleted file mode 100644
index 3eae4ba..0000000
--- a/contrib/sendmail/src/alias.c
+++ /dev/null
@@ -1,1015 +0,0 @@
-/*
- * Copyright (c) 1998-2003 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: alias.c,v 8.219 2006/10/24 18:04:09 ca Exp $")
-
-#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 *, char *));
-
-/*
-** ALIAS -- Compute aliases.
-**
-** Scans the alias file for an alias for the given address.
-** If found, it arranges to deliver to the alias list instead.
-** Uses libdbm database if -DDBM.
-**
-** Parameters:
-** a -- address to alias.
-** sendq -- a pointer to the head of the send queue
-** to put the aliases in.
-** aliaslevel -- the current alias nesting depth.
-** e -- the current envelope.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** Aliases found are expanded.
-**
-** Deficiencies:
-** It should complain about names that are aliased to
-** nothing.
-*/
-
-void
-alias(a, sendq, aliaslevel, e)
- register ADDRESS *a;
- ADDRESS **sendq;
- int aliaslevel;
- register ENVELOPE *e;
-{
- register char *p;
- char *owner;
- auto int status = EX_OK;
- char obuf[MAXNAME + 7];
-
- if (tTd(27, 1))
- sm_dprintf("alias(%s)\n", a->q_user);
-
- /* don't realias already aliased names */
- if (!QS_IS_OK(a->q_state))
- return;
-
- if (NoAlias)
- return;
-
- e->e_to = a->q_paddr;
-
- /*
- ** Look up this name.
- **
- ** If the map was unavailable, we will queue this message
- ** until the map becomes available; otherwise, we could
- ** bounce messages inappropriately.
- */
-
-#if _FFR_REDIRECTEMPTY
- /*
- ** envelope <> can't be sent to mailing lists, only owner-
- ** send spam of this type to owner- of the list
- ** ---- to stop spam from going to mailing lists!
- */
-
- if (e->e_sender != NULL && *e->e_sender == '\0')
- {
- /* Look for owner of alias */
- (void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
- if (aliaslookup(obuf, &status, a->q_host) != NULL)
- {
- if (LogLevel > 8)
- sm_syslog(LOG_WARNING, e->e_id,
- "possible spam from <> to list: %s, redirected to %s\n",
- a->q_user, obuf);
- a->q_user = sm_rpool_strdup_x(e->e_rpool, obuf);
- }
- }
-#endif /* _FFR_REDIRECTEMPTY */
-
- p = aliaslookup(a->q_user, &status, a->q_host);
- if (status == EX_TEMPFAIL || status == EX_UNAVAILABLE)
- {
- a->q_state = QS_QUEUEUP;
- if (e->e_message == NULL)
- e->e_message = sm_rpool_strdup_x(e->e_rpool,
- "alias database unavailable");
-
- /* XXX msg only per recipient? */
- if (a->q_message == NULL)
- a->q_message = "alias database unavailable";
- return;
- }
- if (p == NULL)
- return;
-
- /*
- ** Match on Alias.
- ** Deliver to the target list.
- */
-
- if (tTd(27, 1))
- sm_dprintf("%s (%s, %s) aliased to %s\n",
- a->q_paddr, a->q_host, a->q_user, p);
- if (bitset(EF_VRFYONLY, e->e_flags))
- {
- a->q_state = QS_VERIFIED;
- return;
- }
- message("aliased to %s", shortenstring(p, MAXSHORTSTR));
- if (LogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "alias %.100s => %s",
- a->q_paddr, shortenstring(p, MAXSHORTSTR));
- a->q_flags &= ~QSELFREF;
- if (tTd(27, 5))
- {
- sm_dprintf("alias: QS_EXPANDED ");
- printaddr(sm_debug_file(), a, false);
- }
- 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) && QS_IS_EXPANDED(a->q_state))
- a->q_state = QS_OK;
-
- /*
- ** Look for owner of alias
- */
-
- if (strncmp(a->q_user, "owner-", 6) == 0 ||
- strlen(a->q_user) > sizeof(obuf) - 7)
- (void) sm_strlcpy(obuf, "owner-owner", sizeof(obuf));
- else
- (void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
- owner = aliaslookup(obuf, &status, a->q_host);
- if (owner == NULL)
- return;
-
- /* reflect owner into envelope sender */
- if (strpbrk(owner, ",:/|\"") != NULL)
- owner = obuf;
- a->q_owner = sm_rpool_strdup_x(e->e_rpool, owner);
-
- /* announce delivery to this alias; NORECEIPT bit set later */
- if (e->e_xfp != NULL)
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "Message delivered to mailing list %s\n",
- a->q_paddr);
- e->e_flags |= EF_SENDRECEIPT;
- a->q_flags |= QDELIVERED|QEXPANDED;
-}
-/*
-** ALIASLOOKUP -- look up a name in the alias file.
-**
-** Parameters:
-** name -- the name to look up.
-** pstat -- a pointer to a place to put the status.
-** av -- argument for %1 expansion.
-**
-** Returns:
-** the value of name.
-** NULL if unknown.
-**
-** Side Effects:
-** none.
-**
-** Warnings:
-** The return value will be trashed across calls.
-*/
-
-static char *
-aliaslookup(name, pstat, av)
- char *name;
- int *pstat;
- char *av;
-{
- static MAP *map = NULL;
-#if _FFR_ALIAS_DETAIL
- int i;
- char *argv[4];
-#endif /* _FFR_ALIAS_DETAIL */
-
- if (map == NULL)
- {
- STAB *s = stab("aliases", ST_MAP, ST_FIND);
-
- if (s == NULL)
- return NULL;
- map = &s->s_map;
- }
- DYNOPENMAP(map);
-
- /* special case POstMastER -- always use lower case */
- if (sm_strcasecmp(name, "postmaster") == 0)
- name = "postmaster";
-
-#if _FFR_ALIAS_DETAIL
- i = 0;
- argv[i++] = name;
- argv[i++] = av;
-
- /* XXX '+' is hardwired here as delimiter! */
- if (av != NULL && *av == '+')
- argv[i++] = av + 1;
- argv[i++] = NULL;
- return (*map->map_class->map_lookup)(map, name, argv, pstat);
-#else /* _FFR_ALIAS_DETAIL */
- return (*map->map_class->map_lookup)(map, name, NULL, pstat);
-#endif /* _FFR_ALIAS_DETAIL */
-}
-/*
-** SETALIAS -- set up an alias map
-**
-** Called when reading configuration file.
-**
-** Parameters:
-** spec -- the alias specification
-**
-** Returns:
-** none.
-*/
-
-void
-setalias(spec)
- char *spec;
-{
- register char *p;
- register MAP *map;
- char *class;
- STAB *s;
-
- if (tTd(27, 8))
- sm_dprintf("setalias(%s)\n", spec);
-
- for (p = spec; p != NULL; )
- {
- char buf[50];
-
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- break;
- spec = p;
-
- if (NAliasFileMaps >= MAXMAPSTACK)
- {
- syserr("Too many alias databases defined, %d max",
- MAXMAPSTACK);
- return;
- }
- if (AliasFileMap == NULL)
- {
- (void) sm_strlcpy(buf, "aliases.files sequence",
- sizeof(buf));
- AliasFileMap = makemapentry(buf);
- if (AliasFileMap == NULL)
- {
- syserr("setalias: cannot create aliases.files map");
- return;
- }
- }
- (void) sm_snprintf(buf, sizeof(buf), "Alias%d", NAliasFileMaps);
- s = stab(buf, ST_MAP, ST_ENTER);
- map = &s->s_map;
- memset(map, '\0', sizeof(*map));
- map->map_mname = s->s_name;
- p = strpbrk(p, ALIAS_SPEC_SEPARATORS);
- if (p != NULL && *p == SEPARATOR)
- {
- /* map name */
- *p++ = '\0';
- class = spec;
- spec = p;
- }
- else
- {
- class = "implicit";
- map->map_mflags = MF_INCLNULL;
- }
-
- /* find end of spec */
- if (p != NULL)
- {
- 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))
- sm_dprintf(" map %s:%s %s\n", class, s->s_name, spec);
-
- /* look up class */
- s = stab(class, ST_MAPCLASS, ST_FIND);
- if (s == NULL)
- {
- syserr("setalias: unknown alias class %s", class);
- }
- else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
- {
- syserr("setalias: map class %s can't handle aliases",
- class);
- }
- else
- {
- map->map_class = &s->s_mapclass;
- map->map_mflags |= MF_ALIAS;
- if (map->map_class->map_parse(map, spec))
- {
- map->map_mflags |= MF_VALID;
- AliasFileMap->map_stack[NAliasFileMaps++] = map;
- }
- }
- }
-}
-/*
-** ALIASWAIT -- wait for distinguished @:@ token to appear.
-**
-** This can decide to reopen or rebuild the alias file
-**
-** Parameters:
-** map -- a pointer to the map descriptor for this alias file.
-** ext -- the filename extension (e.g., ".db") for the
-** database file.
-** isopen -- if set, the database is already open, and we
-** should check for validity; otherwise, we are
-** just checking to see if it should be created.
-**
-** Returns:
-** true -- if the database is open when we return.
-** false -- if the database is closed when we return.
-*/
-
-bool
-aliaswait(map, ext, isopen)
- MAP *map;
- char *ext;
- bool isopen;
-{
- bool attimeout = false;
- time_t mtime;
- struct stat stb;
- char buf[MAXPATHLEN];
-
- if (tTd(27, 3))
- sm_dprintf("aliaswait(%s:%s)\n",
- map->map_class->map_cname, map->map_file);
- if (bitset(MF_ALIASWAIT, map->map_mflags))
- return isopen;
- map->map_mflags |= MF_ALIASWAIT;
-
- if (SafeAlias > 0)
- {
- auto int st;
- unsigned int sleeptime = 2;
- unsigned int loopcount = 0; /* only used for debugging */
- time_t toolong = curtime() + SafeAlias;
-
- while (isopen &&
- map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
- {
- if (curtime() > toolong)
- {
- /* we timed out */
- attimeout = true;
- break;
- }
-
- /*
- ** Close and re-open the alias database in case
- ** the one is mv'ed instead of cp'ed in.
- */
-
- if (tTd(27, 2))
- {
- loopcount++;
- sm_dprintf("aliaswait: sleeping for %u seconds (loopcount = %u)\n",
- sleeptime, loopcount);
- }
-
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
- (void) sleep(sleeptime);
- sleeptime *= 2;
- if (sleeptime > 60)
- sleeptime = 60;
- isopen = map->map_class->map_open(map, O_RDONLY);
- }
- }
-
- /* see if we need to go into auto-rebuild mode */
- if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
- {
- if (tTd(27, 3))
- sm_dprintf("aliaswait: not rebuildable\n");
- map->map_mflags &= ~MF_ALIASWAIT;
- return isopen;
- }
- if (stat(map->map_file, &stb) < 0)
- {
- if (tTd(27, 3))
- sm_dprintf("aliaswait: no source file\n");
- map->map_mflags &= ~MF_ALIASWAIT;
- return isopen;
- }
- mtime = stb.st_mtime;
- if (sm_strlcpyn(buf, sizeof(buf), 2,
- map->map_file, ext == NULL ? "" : ext) >= sizeof(buf))
- {
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID,
- "alias database %s%s name too long",
- map->map_file, ext == NULL ? "" : ext);
- message("alias database %s%s name too long",
- map->map_file, ext == NULL ? "" : ext);
- }
-
- if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
- {
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID,
- "alias database %s out of date", buf);
- message("Warning: alias database %s out of date", buf);
- }
- map->map_mflags &= ~MF_ALIASWAIT;
- return isopen;
-}
-/*
-** REBUILDALIASES -- rebuild the alias database.
-**
-** Parameters:
-** map -- the database to rebuild.
-** automatic -- set if this was automatically generated.
-**
-** Returns:
-** true if successful; false otherwise.
-**
-** Side Effects:
-** Reads the text version of the database, builds the
-** DBM or DB version.
-*/
-
-bool
-rebuildaliases(map, automatic)
- register MAP *map;
- bool automatic;
-{
- SM_FILE_T *af;
- bool nolock = false;
- bool success = false;
- long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
- sigfunc_t oldsigint, oldsigquit;
-#ifdef SIGTSTP
- sigfunc_t oldsigtstp;
-#endif /* SIGTSTP */
-
- if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
- return false;
-
- if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
- sff |= SFF_NOWLINK;
- if (!bitnset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
- sff |= SFF_NOGWFILES;
- if (!bitnset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
- sff |= SFF_NOWWFILES;
-
- /* try to lock the source file */
- if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL)
- {
- struct stat stb;
-
- if ((errno != EACCES && errno != EROFS) || automatic ||
- (af = safefopen(map->map_file, O_RDONLY, 0, sff)) == NULL)
- {
- int saveerr = errno;
-
- if (tTd(27, 1))
- sm_dprintf("Can't open %s: %s\n",
- map->map_file, sm_errstring(saveerr));
- if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
- message("newaliases: cannot open %s: %s",
- map->map_file, sm_errstring(saveerr));
- errno = 0;
- return false;
- }
- nolock = true;
- if (tTd(27, 1) ||
- fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &stb) < 0 ||
- bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode))
- message("warning: cannot lock %s: %s",
- map->map_file, sm_errstring(errno));
- }
-
- /* see if someone else is rebuilding the alias file */
- if (!nolock &&
- !lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), map->map_file,
- NULL, LOCK_EX|LOCK_NB))
- {
- /* yes, they are -- wait until done */
- message("Alias file %s is locked (maybe being rebuilt)",
- map->map_file);
- if (OpMode != MD_INITALIAS)
- {
- /* wait for other rebuild to complete */
- (void) lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL),
- map->map_file, NULL, LOCK_EX);
- }
- (void) sm_io_close(af, SM_TIME_DEFAULT);
- errno = 0;
- return false;
- }
-
- oldsigint = sm_signal(SIGINT, SIG_IGN);
- oldsigquit = sm_signal(SIGQUIT, SIG_IGN);
-#ifdef SIGTSTP
- oldsigtstp = sm_signal(SIGTSTP, SIG_IGN);
-#endif /* SIGTSTP */
-
- if (map->map_class->map_open(map, O_RDWR))
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_NOTICE, NOQID,
- "alias database %s %srebuilt by %s",
- map->map_file, automatic ? "auto" : "",
- username());
- }
- map->map_mflags |= MF_OPEN|MF_WRITABLE;
- map->map_pid = CurrentPid;
- readaliases(map, af, !automatic, true);
- success = true;
- }
- else
- {
- if (tTd(27, 1))
- sm_dprintf("Can't create database for %s: %s\n",
- map->map_file, sm_errstring(errno));
- if (!automatic)
- syserr("Cannot create database for alias file %s",
- map->map_file);
- }
-
- /* close the file, thus releasing locks */
- (void) sm_io_close(af, SM_TIME_DEFAULT);
-
- /* add distinguished entries and close the database */
- if (bitset(MF_OPEN, map->map_mflags))
- {
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
- }
-
- /* restore the old signals */
- (void) sm_signal(SIGINT, oldsigint);
- (void) sm_signal(SIGQUIT, oldsigquit);
-#ifdef SIGTSTP
- (void) sm_signal(SIGTSTP, oldsigtstp);
-#endif /* SIGTSTP */
- return success;
-}
-/*
-** READALIASES -- read and process the alias file.
-**
-** This routine implements the part of initaliases that occurs
-** when we are not going to use the DBM stuff.
-**
-** Parameters:
-** map -- the alias database descriptor.
-** af -- file to read the aliases from.
-** announcestats -- announce statistics regarding number of
-** aliases, longest alias, etc.
-** logstats -- lot the same info.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Reads aliasfile into the symbol table.
-** Optionally, builds the .dir & .pag files.
-*/
-
-void
-readaliases(map, af, announcestats, logstats)
- register MAP *map;
- SM_FILE_T *af;
- bool announcestats;
- bool logstats;
-{
- register char *p;
- char *rhs;
- bool skipping;
- long naliases, bytes, longest;
- ADDRESS al, bl;
- char line[BUFSIZ];
-
- /*
- ** Read and interpret lines
- */
-
- FileName = map->map_file;
- LineNumber = 0;
- naliases = bytes = longest = 0;
- skipping = false;
- while (sm_io_fgets(af, SM_TIME_DEFAULT, line, sizeof(line)) != NULL)
- {
- int lhssize, rhssize;
- int c;
-
- LineNumber++;
- p = strchr(line, '\n');
-
- /* XXX what if line="a\\" ? */
- while (p != NULL && p > line && p[-1] == '\\')
- {
- p--;
- if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
- SPACELEFT(line, p)) == NULL)
- break;
- LineNumber++;
- p = strchr(p, '\n');
- }
- if (p != NULL)
- *p = '\0';
- else if (!sm_io_eof(af))
- {
- errno = 0;
- syserr("554 5.3.0 alias line too long");
-
- /* flush to end of line */
- while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) !=
- SM_IO_EOF && c != '\n')
- continue;
-
- /* skip any continuation lines */
- skipping = true;
- continue;
- }
- switch (line[0])
- {
- case '#':
- case '\0':
- skipping = false;
- continue;
-
- case ' ':
- case '\t':
- if (!skipping)
- syserr("554 5.3.5 Non-continuation line starts with space");
- skipping = true;
- continue;
- }
- skipping = false;
-
- /*
- ** Process the LHS
- ** Find the colon separator, and parse the address.
- ** It should resolve to a local name -- this will
- ** be checked later (we want to optionally do
- ** parsing of the RHS first to maximize error
- ** detection).
- */
-
- for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
- continue;
- if (*p++ != ':')
- {
- syserr("554 5.3.5 missing colon");
- continue;
- }
- if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true)
- == NULL)
- {
- syserr("554 5.3.5 %.40s... illegal alias name", line);
- continue;
- }
-
- /*
- ** Process the RHS.
- ** 'al' is the internal form of the LHS address.
- ** 'p' points to the text of the RHS.
- */
-
- while (isascii(*p) && isspace(*p))
- p++;
- rhs = p;
- for (;;)
- {
- register char *nlp;
-
- nlp = &p[strlen(p)];
- if (nlp > p && nlp[-1] == '\n')
- *--nlp = '\0';
-
- if (CheckAliases)
- {
- /* do parsing & compression of addresses */
- while (*p != '\0')
- {
- auto char *delimptr;
-
- while ((isascii(*p) && isspace(*p)) ||
- *p == ',')
- p++;
- if (*p == '\0')
- break;
- if (parseaddr(p, &bl, RF_COPYNONE, ',',
- &delimptr, CurEnv, true)
- == NULL)
- usrerr("553 5.3.5 %s... bad address", p);
- p = delimptr;
- }
- }
- else
- {
- p = nlp;
- }
-
- /* see if there should be a continuation line */
- c = sm_io_getc(af, SM_TIME_DEFAULT);
- if (!sm_io_eof(af))
- (void) sm_io_ungetc(af, SM_TIME_DEFAULT, c);
- if (c != ' ' && c != '\t')
- break;
-
- /* read continuation line */
- if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
- sizeof(line) - (p-line)) == NULL)
- break;
- LineNumber++;
-
- /* check for line overflow */
- if (strchr(p, '\n') == NULL && !sm_io_eof(af))
- {
- usrerr("554 5.3.5 alias too long");
- while ((c = sm_io_getc(af, SM_TIME_DEFAULT))
- != SM_IO_EOF && c != '\n')
- continue;
- skipping = true;
- break;
- }
- }
-
- if (skipping)
- continue;
-
- if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
- {
- syserr("554 5.3.5 %s... cannot alias non-local names",
- al.q_paddr);
- continue;
- }
-
- /*
- ** Insert alias into symbol table or database file.
- **
- ** Special case pOStmaStER -- always make it lower case.
- */
-
- if (sm_strcasecmp(al.q_user, "postmaster") == 0)
- makelower(al.q_user);
-
- lhssize = strlen(al.q_user);
- rhssize = strlen(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 0
- /*
- ** address strings are now stored in the envelope rpool,
- ** and therefore cannot be freed.
- */
- if (al.q_paddr != NULL)
- sm_free(al.q_paddr); /* disabled */
- if (al.q_host != NULL)
- sm_free(al.q_host); /* disabled */
- if (al.q_user != NULL)
- sm_free(al.q_user); /* disabled */
-#endif /* 0 */
- }
-
- CurEnv->e_to = NULL;
- FileName = NULL;
- if (Verbose || announcestats)
- message("%s: %ld aliases, longest %ld bytes, %ld bytes total",
- map->map_file, naliases, longest, bytes);
- if (LogLevel > 7 && logstats)
- sm_syslog(LOG_INFO, NOQID,
- "%s: %ld aliases, longest %ld bytes, %ld bytes total",
- map->map_file, naliases, longest, bytes);
-}
-/*
-** FORWARD -- Try to forward mail
-**
-** This is similar but not identical to aliasing.
-**
-** Parameters:
-** user -- the name of the user who's mail we would like
-** to forward to. It must have been verified --
-** i.e., the q_home field must have been filled
-** in.
-** sendq -- a pointer to the head of the send queue to
-** put this user's aliases in.
-** aliaslevel -- the current alias nesting depth.
-** e -- the current envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** New names are added to send queues.
-*/
-
-void
-forward(user, sendq, aliaslevel, e)
- ADDRESS *user;
- ADDRESS **sendq;
- int aliaslevel;
- register ENVELOPE *e;
-{
- char *pp;
- char *ep;
- bool got_transient;
-
- if (tTd(27, 1))
- sm_dprintf("forward(%s)\n", user->q_paddr);
-
- if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
- !QS_IS_OK(user->q_state))
- return;
- if (ForwardPath != NULL && *ForwardPath == '\0')
- return;
- if (user->q_home == NULL)
- {
- syserr("554 5.3.0 forward: no home");
- user->q_home = "/no/such/directory";
- }
-
- /* good address -- look for .forward file in home */
- macdefine(&e->e_macro, A_PERM, 'z', user->q_home);
- macdefine(&e->e_macro, A_PERM, 'u', user->q_user);
- macdefine(&e->e_macro, A_PERM, 'h', user->q_host);
- if (ForwardPath == NULL)
- ForwardPath = newstr("\201z/.forward");
-
- got_transient = false;
- for (pp = ForwardPath; pp != NULL; pp = ep)
- {
- int err;
- char buf[MAXPATHLEN];
- struct stat st;
-
- ep = strchr(pp, SEPARATOR);
- if (ep != NULL)
- *ep = '\0';
- expand(pp, buf, sizeof(buf), e);
- if (ep != NULL)
- *ep++ = SEPARATOR;
- if (buf[0] == '\0')
- continue;
- if (tTd(27, 3))
- sm_dprintf("forward: trying %s\n", buf);
-
- err = include(buf, true, user, sendq, aliaslevel, e);
- if (err == 0)
- break;
- else if (transienterror(err))
- {
- /* we may have to suspend this message */
- got_transient = true;
- if (tTd(27, 2))
- sm_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, sm_errstring(err));
- CurHostName = curhost;
- }
-
- }
- else
- {
- switch (err)
- {
- 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_WWFILE:
- case E_SM_GWFILE:
- syserr("forward: %s: %s", buf, sm_errstring(err));
- break;
-#endif /* _FFR_FORWARD_SYSERR */
-
- default:
- if (LogLevel > (RunAsUid == 0 ? 2 : 10))
- sm_syslog(LOG_WARNING, e->e_id,
- "forward %s: %s", buf,
- sm_errstring(err));
- if (Verbose)
- message("forward: %s: %s",
- buf, sm_errstring(err));
- break;
- }
- }
- }
- if (pp == NULL && got_transient)
- {
- /*
- ** There was no successful .forward open and at least one
- ** transient open. We have to defer this address for
- ** further delivery.
- */
-
- message("transient .forward open error: message queued");
- user->q_state = QS_QUEUEUP;
- return;
- }
-}
diff --git a/contrib/sendmail/src/aliases b/contrib/sendmail/src/aliases
deleted file mode 100644
index 2d06ae3..0000000
--- a/contrib/sendmail/src/aliases
+++ /dev/null
@@ -1,65 +0,0 @@
-#
-# $Id: aliases,v 8.5 2002/06/05 22:54:26 gshapiro Exp $
-# @(#)aliases 8.2 (Berkeley) 3/5/94
-#
-# Aliases in this file will NOT be expanded in the header from
-# Mail, but WILL be visible over networks.
-#
-# >>>>>>>>>> The program "newaliases" must be run after
-# >> NOTE >> this file is updated for any changes to
-# >>>>>>>>>> show through to sendmail.
-#
-#
-# See also RFC 2142, `MAILBOX NAMES FOR COMMON SERVICES, ROLES
-# AND FUNCTIONS', May 1997
-
-# Pretty much everything else in this file points to "root", so
-# you should forward root's email to the system administrator.
-# Delivering mail to root's mailbox or reading mail as root is
-# inadvisable.
-
-# Uncomment and *CHANGE* this!
-# root: insert-human-being-here
-
-# Basic system aliases -- these MUST be present
-MAILER-DAEMON: postmaster
-postmaster: root
-
-# General redirections for pseudo accounts
-bin: root
-daemon: root
-games: root
-mailnull: postmaster
-smmsp: postmaster
-ingres: root
-nobody: root
-system: root
-toor: root
-
-# Well-known aliases
-manager: root
-dumper: root
-operator: root
-
-# RFC 2142: BUSINESS-RELATED MAILBOX NAMES
-# info: root
-# marketing: root
-# sales: root
-# support: root
-
-# RFC 2142: NETWORK OPERATIONS MAILBOX NAMES
-abuse: root
-noc: root
-security: root
-
-# RFC 2142: SUPPORT MAILBOX NAMES FOR SPECIFIC INTERNET SERVICES
-hostmaster: root
-usenet: root
-news: usenet
-webmaster: root
-www: webmaster
-uucp: root
-ftp: root
-
-# Trap decode to catch security attacks
-decode: root
diff --git a/contrib/sendmail/src/aliases.5 b/contrib/sendmail/src/aliases.5
deleted file mode 100644
index 32fb50c..0000000
--- a/contrib/sendmail/src/aliases.5
+++ /dev/null
@@ -1,131 +0,0 @@
-.\" Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
-.\" All rights reserved.
-.\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved.
-.\" Copyright (c) 1985, 1991, 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.
-.\"
-.\"
-.\" $Id: aliases.5,v 8.19 2004/07/12 05:39:21 ca Exp $
-.\"
-.TH ALIASES 5 "$Date: 2004/07/12 05:39:21 $"
-.SH NAME
-aliases
-\- aliases file for sendmail
-.SH SYNOPSIS
-.B aliases
-.SH DESCRIPTION
-This file describes user
-ID
-aliases used by
-sendmail.
-The file resides in
-/etc/mail
-and
-is formatted as a series of lines of the form
-.IP
-name: addr_1, addr_2, addr_3, . . .
-.PP
-The
-.I name
-is the name to alias, and the
-.I addr_n
-are the aliases for that name.
-.I addr_n
-can be another alias, a local username, a local filename,
-a command,
-an include file,
-or an external address.
-.TP
-.B Local Username
-username
-.IP
-The username must be available via getpwnam(3).
-.TP
-.B Local Filename
-/path/name
-.IP
-Messages are appended to the file specified by the full pathname
-(starting with a slash (/))
-.TP
-.B Command
-|command
-.IP
-A command starts with a pipe symbol (|),
-it receives messages via standard input.
-.TP
-.B Include File
-:include: /path/name
-.IP
-The aliases in pathname are added to the aliases for
-.I name.
-.TP
-.B E-Mail Address
-user@domain
-.IP
-An e-mail address in RFC 822 format.
-.PP
-Lines beginning with white space are continuation lines.
-Another way to continue lines is by placing a backslash
-directly before a newline.
-Lines beginning with
-#
-are comments.
-.PP
-Aliasing occurs only on local names.
-Loops can not occur, since no message will be sent to any person more than once.
-.PP
-If an alias is found for
-.IR name ,
-sendmail then checks for an alias for
-.IR owner-name .
-If it is found and the result of the lookup expands to a single
-address, the envelope sender address of the message is rewritten to
-that address.
-If it is found and the result expands to more than one address, the
-envelope sender address is changed to
-.IR owner-name .
-.PP
-After aliasing has been done, local and valid recipients who have a
-``.forward''
-file in their home directory have messages forwarded to the
-list of users defined in that file.
-.PP
-This is only the raw data file; the actual aliasing information is
-placed into a binary format in the file
-/etc/mail/aliases.db
-using the program
-newaliases(1).
-A
-newaliases
-command should be executed each time the aliases file is changed for the
-change to take effect.
-.SH SEE ALSO
-newaliases(1),
-dbm(3),
-dbopen(3),
-db_open(3),
-sendmail(8)
-.PP
-.I
-SENDMAIL Installation and Operation Guide.
-.PP
-.I
-SENDMAIL An Internetwork Mail Router.
-.SH BUGS
-If you have compiled
-sendmail
-with DBM support instead of NEWDB,
-you may have encountered problems in
-dbm(3)
-restricting a single alias to about 1000 bytes of information.
-You can get longer aliases by ``chaining''; that is, make the last name in
-the alias be a dummy name which is a continuation alias.
-.SH HISTORY
-The
-.B aliases
-file format appeared in
-4.0BSD.
diff --git a/contrib/sendmail/src/arpadate.c b/contrib/sendmail/src/arpadate.c
deleted file mode 100644
index 5d3d7a6..0000000
--- a/contrib/sendmail/src/arpadate.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: arpadate.c,v 8.31 2006/08/15 23:24:55 ca Exp $")
-
-/*
-** ARPADATE -- Create date in ARPANET format
-**
-** Parameters:
-** ud -- unix style date string. if NULL, one is created.
-**
-** Returns:
-** pointer to an ARPANET date field
-**
-** Side Effects:
-** none
-**
-** WARNING:
-** date is stored in a local buffer -- subsequent
-** calls will overwrite.
-**
-** Bugs:
-** Timezone is computed from local time, rather than
-** from wherever (and whenever) the message was sent.
-** To do better is very hard.
-**
-** Some sites are now inserting the timezone into the
-** local date. This routine should figure out what
-** the format is and work appropriately.
-*/
-
-#ifndef TZNAME_MAX
-# define TZNAME_MAX 50 /* max size of timezone */
-#endif /* ! TZNAME_MAX */
-
-/* values for TZ_TYPE */
-#define TZ_NONE 0 /* no character timezone support */
-#define TZ_TM_NAME 1 /* use tm->tm_name */
-#define TZ_TM_ZONE 2 /* use tm->tm_zone */
-#define TZ_TZNAME 3 /* use tzname[] */
-#define TZ_TIMEZONE 4 /* use timezone() */
-
-char *
-arpadate(ud)
- register char *ud;
-{
- register char *p;
- register char *q;
- register int off;
- register int i;
- register struct tm *lt;
- time_t t;
- struct tm gmt;
- char *tz;
- static char b[43 + TZNAME_MAX];
-
- /*
- ** Get current time.
- ** This will be used if a null argument is passed and
- ** to resolve the timezone.
- */
-
- /* SM_REQUIRE(ud == NULL || strlen(ud) >= 23); */
- t = curtime();
- if (ud == NULL)
- ud = ctime(&t);
-
- /*
- ** Crack the UNIX date line in a singularly unoriginal way.
- */
-
- q = b;
-
- p = &ud[0]; /* Mon */
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = ',';
- *q++ = ' ';
-
- p = &ud[8]; /* 16 */
- if (*p == ' ')
- p++;
- else
- *q++ = *p++;
- *q++ = *p++;
- *q++ = ' ';
-
- p = &ud[4]; /* Sep */
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = ' ';
-
- p = &ud[20]; /* 1979 */
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = ' ';
-
- p = &ud[11]; /* 01:03:52 */
- for (i = 8; i > 0; i--)
- *q++ = *p++;
-
- /*
- ** should really get the timezone from the time in "ud" (which
- ** is only different if a non-null arg was passed which is different
- ** from the current time), but for all practical purposes, returning
- ** the current local zone will do (its all that is ever needed).
- */
-
- gmt = *gmtime(&t);
- lt = localtime(&t);
-
- off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
-
- /* assume that offset isn't more than a day ... */
- if (lt->tm_year < gmt.tm_year)
- off -= 24 * 60;
- else if (lt->tm_year > gmt.tm_year)
- off += 24 * 60;
- else if (lt->tm_yday < gmt.tm_yday)
- off -= 24 * 60;
- else if (lt->tm_yday > gmt.tm_yday)
- off += 24 * 60;
-
- *q++ = ' ';
- if (off == 0)
- {
- *q++ = 'G';
- *q++ = 'M';
- *q++ = 'T';
- }
- else
- {
- tz = NULL;
-#if TZ_TYPE == TZ_TM_NAME
- tz = lt->tm_name;
-#endif /* TZ_TYPE == TZ_TM_NAME */
-#if TZ_TYPE == TZ_TM_ZONE
- tz = lt->tm_zone;
-#endif /* TZ_TYPE == TZ_TM_ZONE */
-#if TZ_TYPE == TZ_TZNAME
- {
- extern char *tzname[];
-
- if (lt->tm_isdst > 0)
- tz = tzname[1];
- else if (lt->tm_isdst == 0)
- tz = tzname[0];
- else
- tz = NULL;
- }
-#endif /* TZ_TYPE == TZ_TZNAME */
-#if TZ_TYPE == TZ_TIMEZONE
- {
- extern char *timezone();
-
- tz = timezone(off, lt->tm_isdst);
- }
-#endif /* TZ_TYPE == TZ_TIMEZONE */
- if (off < 0)
- {
- off = -off;
- *q++ = '-';
- }
- else
- *q++ = '+';
-
- if (off >= 24*60) /* should be impossible */
- off = 23*60+59; /* if not, insert silly value */
-
- *q++ = (off / 600) + '0';
- *q++ = (off / 60) % 10 + '0';
- off %= 60;
- *q++ = (off / 10) + '0';
- *q++ = (off % 10) + '0';
- if (tz != NULL && *tz != '\0')
- {
- *q++ = ' ';
- *q++ = '(';
- while (*tz != '\0' && q < &b[sizeof(b) - 3])
- *q++ = *tz++;
- *q++ = ')';
- }
- }
- *q = '\0';
-
- return b;
-}
diff --git a/contrib/sendmail/src/bf.c b/contrib/sendmail/src/bf.c
deleted file mode 100644
index b31ce7e..0000000
--- a/contrib/sendmail/src/bf.c
+++ /dev/null
@@ -1,864 +0,0 @@
-/*
- * Copyright (c) 1999-2002, 2004, 2006 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- * Contributed by Exactis.com, Inc.
- *
- */
-
-/*
-** This is in transition. Changed from the original bf_torek.c code
-** to use sm_io function calls directly rather than through stdio
-** translation layer. Will be made a built-in file type of libsm
-** next (once safeopen() linkable from libsm).
-*/
-
-#include <sm/gen.h>
-SM_RCSID("@(#)$Id: bf.c,v 8.62 2006/03/31 18:45:56 ca Exp $")
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include "sendmail.h"
-#include "bf.h"
-
-#include <syslog.h>
-
-/* bf io functions */
-static ssize_t sm_bfread __P((SM_FILE_T *, char *, size_t));
-static ssize_t sm_bfwrite __P((SM_FILE_T *, const char *, size_t));
-static off_t sm_bfseek __P((SM_FILE_T *, off_t, int));
-static int sm_bfclose __P((SM_FILE_T *));
-static int sm_bfcommit __P((SM_FILE_T *));
-static int sm_bftruncate __P((SM_FILE_T *));
-
-static int sm_bfopen __P((SM_FILE_T *, const void *, int, const void *));
-static int sm_bfsetinfo __P((SM_FILE_T *, int , void *));
-static int sm_bfgetinfo __P((SM_FILE_T *, int , void *));
-
-/*
-** Data structure for storing information about each buffered file
-** (Originally in sendmail/bf_torek.h for the curious.)
-*/
-
-struct bf
-{
- bool bf_committed; /* Has this buffered file been committed? */
- bool bf_ondisk; /* On disk: committed or buffer overflow */
- long bf_flags;
- int bf_disk_fd; /* If on disk, associated file descriptor */
- char *bf_buf; /* Memory buffer */
- int bf_bufsize; /* Length of above buffer */
- int bf_buffilled; /* Bytes of buffer actually filled */
- char *bf_filename; /* Name of buffered file, if ever committed */
- MODE_T bf_filemode; /* Mode of buffered file, if ever committed */
- off_t bf_offset; /* Currect file offset */
- int bf_size; /* Total current size of file */
-};
-
-#ifdef BF_STANDALONE
-# define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode)
-#else /* BF_STANDALONE */
-# define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
-#endif /* BF_STANDALONE */
-
-struct bf_info
-{
- char *bi_filename;
- MODE_T bi_fmode;
- size_t bi_bsize;
- long bi_flags;
-};
-
-/*
-** SM_BFOPEN -- the "base" open function called by sm_io_open() for the
-** internal, file-type-specific info setup.
-**
-** Parameters:
-** fp -- file pointer being filled-in for file being open'd
-** info -- information about file being opened
-** flags -- ignored
-** rpool -- ignored (currently)
-**
-** Returns:
-** Failure: -1 and sets errno
-** Success: 0 (zero)
-*/
-
-static int
-sm_bfopen(fp, info, flags, rpool)
- SM_FILE_T *fp;
- const void *info;
- int flags;
- const void *rpool;
-{
- char *filename;
- MODE_T fmode;
- size_t bsize;
- long sflags;
- struct bf *bfp;
- int l;
- struct stat st;
-
- filename = ((struct bf_info *) info)->bi_filename;
- fmode = ((struct bf_info *) info)->bi_fmode;
- bsize = ((struct bf_info *) info)->bi_bsize;
- sflags = ((struct bf_info *) info)->bi_flags;
-
- /* Sanity checks */
- if (*filename == '\0')
- {
- /* Empty filename string */
- errno = ENOENT;
- return -1;
- }
- if (stat(filename, &st) == 0)
- {
- /* File already exists on disk */
- errno = EEXIST;
- return -1;
- }
-
- /* Allocate memory */
- bfp = (struct bf *) sm_malloc(sizeof(struct bf));
- if (bfp == NULL)
- {
- errno = ENOMEM;
- return -1;
- }
-
- /* Assign data buffer */
- /* A zero bsize is valid, just don't allocate memory */
- if (bsize > 0)
- {
- bfp->bf_buf = (char *) sm_malloc(bsize);
- if (bfp->bf_buf == NULL)
- {
- bfp->bf_bufsize = 0;
- sm_free(bfp);
- errno = ENOMEM;
- return -1;
- }
- }
- else
- bfp->bf_buf = NULL;
-
- /* Nearly home free, just set all the parameters now */
- bfp->bf_committed = false;
- bfp->bf_ondisk = false;
- bfp->bf_flags = sflags;
- bfp->bf_bufsize = bsize;
- bfp->bf_buffilled = 0;
- l = strlen(filename) + 1;
- bfp->bf_filename = (char *) sm_malloc(l);
- if (bfp->bf_filename == NULL)
- {
- if (bfp->bf_buf != NULL)
- sm_free(bfp->bf_buf);
- sm_free(bfp);
- errno = ENOMEM;
- return -1;
- }
- (void) sm_strlcpy(bfp->bf_filename, filename, l);
- bfp->bf_filemode = fmode;
- bfp->bf_offset = 0;
- bfp->bf_size = 0;
- bfp->bf_disk_fd = -1;
- fp->f_cookie = bfp;
-
- if (tTd(58, 8))
- sm_dprintf("sm_bfopen(%s)\n", filename);
-
- return 0;
-}
-
-/*
-** BFOPEN -- create a new buffered file
-**
-** Parameters:
-** filename -- the file's name
-** fmode -- what mode the file should be created as
-** bsize -- amount of buffer space to allocate (may be 0)
-** flags -- if running under sendmail, passed directly to safeopen
-**
-** Returns:
-** a SM_FILE_T * which may then be used with stdio functions,
-** or NULL on failure. SM_FILE_T * is opened for writing
-** "SM_IO_WHAT_VECTORS").
-**
-** Side Effects:
-** none.
-**
-** Sets errno:
-** any value of errno specified by sm_io_setinfo_type()
-** any value of errno specified by sm_io_open()
-** any value of errno specified by sm_io_setinfo()
-*/
-
-#ifdef __STDC__
-/*
-** XXX This is a temporary hack since MODE_T on HP-UX 10.x is short.
-** If we use K&R here, the compiler will complain about
-** Inconsistent parameter list declaration
-** due to the change from short to int.
-*/
-
-SM_FILE_T *
-bfopen(char *filename, MODE_T fmode, size_t bsize, long flags)
-#else /* __STDC__ */
-SM_FILE_T *
-bfopen(filename, fmode, bsize, flags)
- char *filename;
- MODE_T fmode;
- size_t bsize;
- long flags;
-#endif /* __STDC__ */
-{
- MODE_T omask;
- SM_FILE_T SM_IO_SET_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
- sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
- SM_TIME_FOREVER);
- struct bf_info info;
-
- /*
- ** Apply current umask to fmode as it may change by the time
- ** the file is actually created. fmode becomes the true
- ** permissions of the file, which OPEN() must obey.
- */
-
- omask = umask(0);
- fmode &= ~omask;
- (void) umask(omask);
-
- SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
- sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
- SM_TIME_FOREVER);
- info.bi_filename = filename;
- info.bi_fmode = fmode;
- info.bi_bsize = bsize;
- info.bi_flags = flags;
-
- return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL);
-}
-
-/*
-** SM_BFGETINFO -- returns info about an open file pointer
-**
-** Parameters:
-** fp -- file pointer to get info about
-** what -- type of info to obtain
-** valp -- thing to return the info in
-*/
-
-static int
-sm_bfgetinfo(fp, what, valp)
- SM_FILE_T *fp;
- int what;
- void *valp;
-{
- struct bf *bfp;
-
- bfp = (struct bf *) fp->f_cookie;
- switch (what)
- {
- case SM_IO_WHAT_FD:
- return bfp->bf_disk_fd;
- case SM_IO_WHAT_SIZE:
- return bfp->bf_size;
- default:
- return -1;
- }
-}
-
-/*
-** SM_BFCLOSE -- close a buffered file
-**
-** Parameters:
-** fp -- cookie of file to close
-**
-** Returns:
-** 0 to indicate success
-**
-** Side Effects:
-** deletes backing file, sm_frees memory.
-**
-** Sets errno:
-** never.
-*/
-
-static int
-sm_bfclose(fp)
- SM_FILE_T *fp;
-{
- struct bf *bfp;
-
- /* Cast cookie back to correct type */
- bfp = (struct bf *) fp->f_cookie;
-
- /* Need to clean up the file */
- if (bfp->bf_ondisk && !bfp->bf_committed)
- unlink(bfp->bf_filename);
- sm_free(bfp->bf_filename);
-
- if (bfp->bf_disk_fd != -1)
- close(bfp->bf_disk_fd);
-
- /* Need to sm_free the buffer */
- if (bfp->bf_bufsize > 0)
- sm_free(bfp->bf_buf);
-
- /* Finally, sm_free the structure */
- sm_free(bfp);
- return 0;
-}
-
-/*
-** SM_BFREAD -- read a buffered file
-**
-** Parameters:
-** cookie -- cookie of file to read
-** buf -- buffer to fill
-** nbytes -- how many bytes to read
-**
-** Returns:
-** number of bytes read or -1 indicate failure
-**
-** Side Effects:
-** none.
-**
-*/
-
-static ssize_t
-sm_bfread(fp, buf, nbytes)
- SM_FILE_T *fp;
- char *buf;
- size_t nbytes;
-{
- struct bf *bfp;
- ssize_t count = 0; /* Number of bytes put in buf so far */
- int retval;
-
- /* Cast cookie back to correct type */
- bfp = (struct bf *) fp->f_cookie;
-
- if (bfp->bf_offset < bfp->bf_buffilled)
- {
- /* Need to grab some from buffer */
- count = nbytes;
- if ((bfp->bf_offset + count) > bfp->bf_buffilled)
- count = bfp->bf_buffilled - bfp->bf_offset;
-
- memcpy(buf, bfp->bf_buf + bfp->bf_offset, count);
- }
-
- if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled)
- {
- /* Need to grab some from file */
- if (!bfp->bf_ondisk)
- {
- /* Oops, the file doesn't exist. EOF. */
- if (tTd(58, 8))
- sm_dprintf("sm_bfread(%s): to disk\n",
- bfp->bf_filename);
- goto finished;
- }
-
- /* Catch a read() on an earlier failed write to disk */
- if (bfp->bf_disk_fd < 0)
- {
- errno = EIO;
- return -1;
- }
-
- if (lseek(bfp->bf_disk_fd,
- bfp->bf_offset + count, SEEK_SET) < 0)
- {
- if ((errno == EINVAL) || (errno == ESPIPE))
- {
- /*
- ** stdio won't be expecting these
- ** errnos from read()! Change them
- ** into something it can understand.
- */
-
- errno = EIO;
- }
- return -1;
- }
-
- while (count < nbytes)
- {
- retval = read(bfp->bf_disk_fd,
- buf + count,
- nbytes - count);
- if (retval < 0)
- {
- /* errno is set implicitly by read() */
- return -1;
- }
- else if (retval == 0)
- goto finished;
- else
- count += retval;
- }
- }
-
-finished:
- bfp->bf_offset += count;
- return count;
-}
-
-/*
-** SM_BFSEEK -- seek to a position in a buffered file
-**
-** Parameters:
-** fp -- fp of file to seek
-** offset -- position to seek to
-** whence -- how to seek
-**
-** Returns:
-** new file offset or -1 indicate failure
-**
-** Side Effects:
-** none.
-**
-*/
-
-static off_t
-sm_bfseek(fp, offset, whence)
- SM_FILE_T *fp;
- off_t offset;
- int whence;
-
-{
- struct bf *bfp;
-
- /* Cast cookie back to correct type */
- bfp = (struct bf *) fp->f_cookie;
-
- switch (whence)
- {
- case SEEK_SET:
- bfp->bf_offset = offset;
- break;
-
- case SEEK_CUR:
- bfp->bf_offset += offset;
- break;
-
- case SEEK_END:
- bfp->bf_offset = bfp->bf_size + offset;
- break;
-
- default:
- errno = EINVAL;
- return -1;
- }
- return bfp->bf_offset;
-}
-
-/*
-** SM_BFWRITE -- write to a buffered file
-**
-** Parameters:
-** fp -- fp of file to write
-** buf -- data buffer
-** nbytes -- how many bytes to write
-**
-** Returns:
-** number of bytes written or -1 indicate failure
-**
-** Side Effects:
-** may create backing file if over memory limit for file.
-**
-*/
-
-static ssize_t
-sm_bfwrite(fp, buf, nbytes)
- SM_FILE_T *fp;
- const char *buf;
- size_t nbytes;
-{
- struct bf *bfp;
- ssize_t count = 0; /* Number of bytes written so far */
- int retval;
-
- /* Cast cookie back to correct type */
- bfp = (struct bf *) fp->f_cookie;
-
- /* If committed, go straight to disk */
- if (bfp->bf_committed)
- {
- if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0)
- {
- if ((errno == EINVAL) || (errno == ESPIPE))
- {
- /*
- ** stdio won't be expecting these
- ** errnos from write()! Change them
- ** into something it can understand.
- */
-
- errno = EIO;
- }
- return -1;
- }
-
- count = write(bfp->bf_disk_fd, buf, nbytes);
- if (count < 0)
- {
- /* errno is set implicitly by write() */
- return -1;
- }
- goto finished;
- }
-
- if (bfp->bf_offset < bfp->bf_bufsize)
- {
- /* Need to put some in buffer */
- count = nbytes;
- if ((bfp->bf_offset + count) > bfp->bf_bufsize)
- count = bfp->bf_bufsize - bfp->bf_offset;
-
- memcpy(bfp->bf_buf + bfp->bf_offset, buf, count);
- if ((bfp->bf_offset + count) > bfp->bf_buffilled)
- bfp->bf_buffilled = bfp->bf_offset + count;
- }
-
- if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize)
- {
- /* Need to put some in file */
- if (!bfp->bf_ondisk)
- {
- MODE_T omask;
- int save_errno;
-
- /* Clear umask as bf_filemode are the true perms */
- omask = umask(0);
- retval = OPEN(bfp->bf_filename,
- O_RDWR | O_CREAT | O_TRUNC | QF_O_EXTRA,
- bfp->bf_filemode, bfp->bf_flags);
- save_errno = errno;
- (void) umask(omask);
- errno = save_errno;
-
- /* Couldn't create file: failure */
- if (retval < 0)
- {
- /*
- ** stdio may not be expecting these
- ** errnos from write()! Change to
- ** something which it can understand.
- ** Note that ENOSPC and EDQUOT are saved
- ** because they are actually valid for
- ** write().
- */
-
- if (!(errno == ENOSPC
-#ifdef EDQUOT
- || errno == EDQUOT
-#endif /* EDQUOT */
- ))
- errno = EIO;
-
- return -1;
- }
- bfp->bf_disk_fd = retval;
- bfp->bf_ondisk = true;
- }
-
- /* Catch a write() on an earlier failed write to disk */
- if (bfp->bf_ondisk && bfp->bf_disk_fd < 0)
- {
- errno = EIO;
- return -1;
- }
-
- if (lseek(bfp->bf_disk_fd,
- bfp->bf_offset + count, SEEK_SET) < 0)
- {
- if ((errno == EINVAL) || (errno == ESPIPE))
- {
- /*
- ** stdio won't be expecting these
- ** errnos from write()! Change them into
- ** something which it can understand.
- */
-
- errno = EIO;
- }
- return -1;
- }
-
- while (count < nbytes)
- {
- retval = write(bfp->bf_disk_fd, buf + count,
- nbytes - count);
- if (retval < 0)
- {
- /* errno is set implicitly by write() */
- return -1;
- }
- else
- count += retval;
- }
- }
-
-finished:
- bfp->bf_offset += count;
- if (bfp->bf_offset > bfp->bf_size)
- bfp->bf_size = bfp->bf_offset;
- return count;
-}
-
-/*
-** BFREWIND -- rewinds the SM_FILE_T *
-**
-** Parameters:
-** fp -- SM_FILE_T * to rewind
-**
-** Returns:
-** 0 on success, -1 on error
-**
-** Side Effects:
-** rewinds the SM_FILE_T * and puts it into read mode. Normally
-** one would bfopen() a file, write to it, then bfrewind() and
-** fread(). If fp is not a buffered file, this is equivalent to
-** rewind().
-**
-** Sets errno:
-** any value of errno specified by sm_io_rewind()
-*/
-
-int
-bfrewind(fp)
- SM_FILE_T *fp;
-{
- (void) sm_io_flush(fp, SM_TIME_DEFAULT);
- sm_io_clearerr(fp); /* quicker just to do it */
- return sm_io_seek(fp, SM_TIME_DEFAULT, 0, SM_IO_SEEK_SET);
-}
-
-/*
-** SM_BFCOMMIT -- "commits" the buffered file
-**
-** Parameters:
-** fp -- SM_FILE_T * to commit to disk
-**
-** Returns:
-** 0 on success, -1 on error
-**
-** Side Effects:
-** Forces the given SM_FILE_T * to be written to disk if it is not
-** already, and ensures that it will be kept after closing. If
-** fp is not a buffered file, this is a no-op.
-**
-** Sets errno:
-** any value of errno specified by open()
-** any value of errno specified by write()
-** any value of errno specified by lseek()
-*/
-
-static int
-sm_bfcommit(fp)
- SM_FILE_T *fp;
-{
- struct bf *bfp;
- int retval;
- int byteswritten;
-
- /* Get associated bf structure */
- bfp = (struct bf *) fp->f_cookie;
-
- /* If already committed, noop */
- if (bfp->bf_committed)
- return 0;
-
- /* Do we need to open a file? */
- if (!bfp->bf_ondisk)
- {
- int save_errno;
- MODE_T omask;
- struct stat st;
-
- if (tTd(58, 8))
- {
- sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
- if (tTd(58, 32))
- sm_dprintf("bfcommit(): filemode %o flags %ld\n",
- bfp->bf_filemode, bfp->bf_flags);
- }
-
- if (stat(bfp->bf_filename, &st) == 0)
- {
- errno = EEXIST;
- return -1;
- }
-
- /* Clear umask as bf_filemode are the true perms */
- omask = umask(0);
- retval = OPEN(bfp->bf_filename,
- O_RDWR | O_CREAT | O_EXCL | QF_O_EXTRA,
- bfp->bf_filemode, bfp->bf_flags);
- save_errno = errno;
- (void) umask(omask);
-
- /* Couldn't create file: failure */
- if (retval < 0)
- {
- /* errno is set implicitly by open() */
- errno = save_errno;
- return -1;
- }
-
- bfp->bf_disk_fd = retval;
- bfp->bf_ondisk = true;
- }
-
- /* Write out the contents of our buffer, if we have any */
- if (bfp->bf_buffilled > 0)
- {
- byteswritten = 0;
-
- if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0)
- {
- /* errno is set implicitly by lseek() */
- return -1;
- }
-
- while (byteswritten < bfp->bf_buffilled)
- {
- retval = write(bfp->bf_disk_fd,
- bfp->bf_buf + byteswritten,
- bfp->bf_buffilled - byteswritten);
- if (retval < 0)
- {
- /* errno is set implicitly by write() */
- return -1;
- }
- else
- byteswritten += retval;
- }
- }
- bfp->bf_committed = true;
-
- /* Invalidate buf; all goes to file now */
- bfp->bf_buffilled = 0;
- if (bfp->bf_bufsize > 0)
- {
- /* Don't need buffer anymore; free it */
- bfp->bf_bufsize = 0;
- sm_free(bfp->bf_buf);
- }
- return 0;
-}
-
-/*
-** SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T *
-**
-** Parameters:
-** fp -- SM_FILE_T * to truncate
-**
-** Returns:
-** 0 on success, -1 on error
-**
-** Side Effects:
-** rewinds the SM_FILE_T *, truncates it to zero length, and puts
-** it into write mode.
-**
-** Sets errno:
-** any value of errno specified by fseek()
-** any value of errno specified by ftruncate()
-*/
-
-static int
-sm_bftruncate(fp)
- SM_FILE_T *fp;
-{
- struct bf *bfp;
-
- if (bfrewind(fp) < 0)
- return -1;
-
- /* Get bf structure */
- bfp = (struct bf *) fp->f_cookie;
- bfp->bf_buffilled = 0;
- bfp->bf_size = 0;
-
- /* Need to zero the buffer */
- if (bfp->bf_bufsize > 0)
- memset(bfp->bf_buf, '\0', bfp->bf_bufsize);
- if (bfp->bf_ondisk)
- {
-#if NOFTRUNCATE
- /* XXX: Not much we can do except rewind it */
- errno = EINVAL;
- return -1;
-#else /* NOFTRUNCATE */
- return ftruncate(bfp->bf_disk_fd, 0);
-#endif /* NOFTRUNCATE */
- }
- return 0;
-}
-
-/*
-** SM_BFSETINFO -- set/change info for an open file pointer
-**
-** Parameters:
-** fp -- file pointer to get info about
-** what -- type of info to set/change
-** valp -- thing to set/change the info to
-**
-*/
-
-static int
-sm_bfsetinfo(fp, what, valp)
- SM_FILE_T *fp;
- int what;
- void *valp;
-{
- struct bf *bfp;
- int bsize;
-
- /* Get bf structure */
- bfp = (struct bf *) fp->f_cookie;
- switch (what)
- {
- case SM_BF_SETBUFSIZE:
- bsize = *((int *) valp);
- bfp->bf_bufsize = bsize;
-
- /* A zero bsize is valid, just don't allocate memory */
- if (bsize > 0)
- {
- bfp->bf_buf = (char *) sm_malloc(bsize);
- if (bfp->bf_buf == NULL)
- {
- bfp->bf_bufsize = 0;
- errno = ENOMEM;
- return -1;
- }
- }
- else
- bfp->bf_buf = NULL;
- return 0;
- case SM_BF_COMMIT:
- return sm_bfcommit(fp);
- case SM_BF_TRUNCATE:
- return sm_bftruncate(fp);
- case SM_BF_TEST:
- return 1; /* always */
- default:
- errno = EINVAL;
- return -1;
- }
-}
diff --git a/contrib/sendmail/src/bf.h b/contrib/sendmail/src/bf.h
deleted file mode 100644
index 5a02292..0000000
--- a/contrib/sendmail/src/bf.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 1999-2002 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.16 2002/04/15 02:37:09 ca Exp $
- *
- * Contributed by Exactis.com, Inc.
- *
- */
-
-#ifndef BF_H
-# define BF_H 1
-
-extern SM_FILE_T *bfopen __P((char *, MODE_T, size_t, long));
-extern int bfcommit __P((SM_FILE_T *));
-extern int bfrewind __P((SM_FILE_T *));
-extern int bftruncate __P((SM_FILE_T *));
-extern int bfclose __P((SM_FILE_T *));
-extern bool bftest __P((SM_FILE_T *));
-
-/* "what" flags for sm_io_setinfo() for the SM_FILE_TYPE file type */
-# define SM_BF_SETBUFSIZE 1000 /* set buffer size */
-# define SM_BF_COMMIT 1001 /* commit file to disk */
-# define SM_BF_TRUNCATE 1002 /* truncate the file */
-# define SM_BF_TEST 1003 /* historical support; temp */
-
-# define BF_FILE_TYPE "SendmailBufferedFile"
-#endif /* ! BF_H */
diff --git a/contrib/sendmail/src/collect.c b/contrib/sendmail/src/collect.c
deleted file mode 100644
index 56fed0a..0000000
--- a/contrib/sendmail/src/collect.c
+++ /dev/null
@@ -1,1101 +0,0 @@
-/*
- * Copyright (c) 1998-2006 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: collect.c,v 8.280 2006/11/29 00:20:40 ca Exp $")
-
-static void eatfrom __P((char *volatile, ENVELOPE *));
-static void collect_doheader __P((ENVELOPE *));
-static SM_FILE_T *collect_dfopen __P((ENVELOPE *));
-static SM_FILE_T *collect_eoh __P((ENVELOPE *, int, int));
-
-/*
-** COLLECT_EOH -- end-of-header processing in collect()
-**
-** Called by collect() when it encounters the blank line
-** separating the header from the message body, or when it
-** encounters EOF in a message that contains only a header.
-**
-** Parameters:
-** e -- envelope
-** numhdrs -- number of headers
-** hdrslen -- length of headers
-**
-** Results:
-** NULL, or handle to open data file
-**
-** Side Effects:
-** end-of-header check ruleset is invoked.
-** envelope state is updated.
-** headers may be added and deleted.
-** selects the queue.
-** opens the data file.
-*/
-
-static SM_FILE_T *
-collect_eoh(e, numhdrs, hdrslen)
- ENVELOPE *e;
- int numhdrs;
- int hdrslen;
-{
- char hnum[16];
- char hsize[16];
-
- /* call the end-of-header check ruleset */
- (void) sm_snprintf(hnum, sizeof(hnum), "%d", numhdrs);
- (void) sm_snprintf(hsize, sizeof(hsize), "%d", hdrslen);
- if (tTd(30, 10))
- sm_dprintf("collect: rscheck(\"check_eoh\", \"%s $| %s\")\n",
- hnum, hsize);
- (void) rscheck("check_eoh", hnum, hsize, e, RSF_UNSTRUCTURED|RSF_COUNT,
- 3, NULL, e->e_id, NULL);
-
- /*
- ** Process the header,
- ** select the queue, open the data file.
- */
-
- collect_doheader(e);
- return collect_dfopen(e);
-}
-
-/*
-** COLLECT_DOHEADER -- process header in collect()
-**
-** Called by collect() after it has finished parsing the header,
-** but before it selects the queue and creates the data file.
-** The results of processing the header will affect queue selection.
-**
-** Parameters:
-** e -- envelope
-**
-** Results:
-** none.
-**
-** Side Effects:
-** envelope state is updated.
-** headers may be added and deleted.
-*/
-
-static void
-collect_doheader(e)
- ENVELOPE *e;
-{
- /*
- ** Find out some information from the headers.
- ** Examples are who is the from person & the date.
- */
-
- eatheader(e, true, false);
-
- if (GrabTo && e->e_sendqueue == NULL)
- usrerr("No recipient addresses found in header");
-
- /*
- ** If we have a Return-Receipt-To:, turn it into a DSN.
- */
-
- if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL)
- {
- ADDRESS *q;
-
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- if (!bitset(QHASNOTIFY, q->q_flags))
- q->q_flags |= QHASNOTIFY|QPINGONSUCCESS;
- }
-
- /*
- ** Add an appropriate recipient line if we have none.
- */
-
- if (hvalue("to", e->e_header) != NULL ||
- hvalue("cc", e->e_header) != NULL ||
- hvalue("apparently-to", e->e_header) != NULL)
- {
- /* have a valid recipient header -- delete Bcc: headers */
- e->e_flags |= EF_DELETE_BCC;
- }
- else if (hvalue("bcc", e->e_header) == NULL)
- {
- /* no valid recipient headers */
- register ADDRESS *q;
- char *hdr = NULL;
-
- /* create a recipient field */
- switch (NoRecipientAction)
- {
- case NRA_ADD_APPARENTLY_TO:
- hdr = "Apparently-To";
- break;
-
- case NRA_ADD_TO:
- hdr = "To";
- break;
-
- case NRA_ADD_BCC:
- addheader("Bcc", " ", 0, e, true);
- break;
-
- case NRA_ADD_TO_UNDISCLOSED:
- addheader("To", "undisclosed-recipients:;", 0, e, true);
- break;
- }
-
- if (hdr != NULL)
- {
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (q->q_alias != NULL)
- continue;
- if (tTd(30, 3))
- sm_dprintf("Adding %s: %s\n",
- hdr, q->q_paddr);
- addheader(hdr, q->q_paddr, 0, e, true);
- }
- }
- }
-}
-
-/*
-** COLLECT_DFOPEN -- open the message data file
-**
-** Called by collect() after it has finished processing the header.
-** Queue selection occurs at this point, possibly based on the
-** envelope's recipient list and on header information.
-**
-** Parameters:
-** e -- envelope
-**
-** Results:
-** NULL, or a pointer to an open data file,
-** into which the message body will be written by collect().
-**
-** Side Effects:
-** Calls syserr, sets EF_FATALERRS and returns NULL
-** if there is insufficient disk space.
-** Aborts process if data file could not be opened.
-** Otherwise, the queue is selected,
-** e->e_{dfino,dfdev,msgsize,flags} are updated,
-** and a pointer to an open data file is returned.
-*/
-
-static SM_FILE_T *
-collect_dfopen(e)
- ENVELOPE *e;
-{
- MODE_T oldumask = 0;
- int dfd;
- struct stat stbuf;
- SM_FILE_T *df;
- char *dfname;
-
- if (!setnewqueue(e))
- return NULL;
-
- dfname = queuename(e, DATAFL_LETTER);
- if (bitset(S_IWGRP, QueueFileMode))
- oldumask = umask(002);
- df = bfopen(dfname, QueueFileMode, DataFileBufferSize,
- SFF_OPENASROOT);
- if (bitset(S_IWGRP, QueueFileMode))
- (void) umask(oldumask);
- if (df == NULL)
- {
- syserr("@Cannot create %s", dfname);
- e->e_flags |= EF_NO_BODY_RETN;
- flush_errors(true);
- finis(false, true, ExitStat);
- /* NOTREACHED */
- }
- dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
- if (dfd < 0 || fstat(dfd, &stbuf) < 0)
- e->e_dfino = -1;
- else
- {
- e->e_dfdev = stbuf.st_dev;
- e->e_dfino = stbuf.st_ino;
- }
- e->e_flags |= EF_HAS_DF;
- return df;
-}
-
-/*
-** COLLECT -- read & parse message header & make temp file.
-**
-** Creates a temporary file name and copies the standard
-** input to that file. Leading UNIX-style "From" lines are
-** stripped off (after important information is extracted).
-**
-** Parameters:
-** fp -- file to read.
-** smtpmode -- if set, we are running SMTP: give an RFC821
-** style message to say we are ready to collect
-** input, and never ignore a single dot to mean
-** end of message.
-** hdrp -- the location to stash the header.
-** e -- the current envelope.
-** rsetsize -- reset e_msgsize?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** If successful,
-** - Data file is created and filled, and e->e_dfp is set.
-** - The from person may be set.
-** If the "enough disk space" check fails,
-** - syserr is called.
-** - e->e_dfp is NULL.
-** - e->e_flags & EF_FATALERRS is set.
-** - collect() returns.
-** If data file cannot be created, the process is terminated.
-*/
-
-/* values for input state machine */
-#define IS_NORM 0 /* middle of line */
-#define IS_BOL 1 /* beginning of line */
-#define IS_DOT 2 /* read a dot at beginning of line */
-#define IS_DOTCR 3 /* read ".\r" at beginning of line */
-#define IS_CR 4 /* read a carriage return */
-
-/* values for message state machine */
-#define MS_UFROM 0 /* reading Unix from line */
-#define MS_HEADER 1 /* reading message header */
-#define MS_BODY 2 /* reading message body */
-#define MS_DISCARD 3 /* discarding rest of message */
-
-void
-collect(fp, smtpmode, hdrp, e, rsetsize)
- SM_FILE_T *fp;
- bool smtpmode;
- HDR **hdrp;
- register ENVELOPE *e;
- bool rsetsize;
-{
- register SM_FILE_T *df;
- bool ignrdot;
- int dbto;
- register char *bp;
- int c;
- bool inputerr;
- bool headeronly;
- char *buf;
- int buflen;
- int istate;
- int mstate;
- int hdrslen;
- int numhdrs;
- int afd;
- unsigned char *pbp;
- unsigned char peekbuf[8];
- char bufbuf[MAXLINE];
-
- df = NULL;
- ignrdot = smtpmode ? false : IgnrDot;
-
- /* timeout for I/O functions is in milliseconds */
- dbto = smtpmode ? ((int) TimeOuts.to_datablock * 1000)
- : SM_TIME_FOREVER;
- sm_io_setinfo(fp, SM_IO_WHAT_TIMEOUT, &dbto);
- c = SM_IO_EOF;
- inputerr = false;
- headeronly = hdrp != NULL;
- hdrslen = 0;
- numhdrs = 0;
- HasEightBits = false;
- buf = bp = bufbuf;
- buflen = sizeof(bufbuf);
- pbp = peekbuf;
- istate = IS_BOL;
- mstate = SaveFrom ? MS_HEADER : MS_UFROM;
-
- /*
- ** Tell ARPANET to go ahead.
- */
-
- if (smtpmode)
- message("354 Enter mail, end with \".\" on a line by itself");
-
- /* simulate an I/O timeout when used as sink */
- if (tTd(83, 101))
- sleep(319);
-
- if (tTd(30, 2))
- sm_dprintf("collect\n");
-
- /*
- ** Read the message.
- **
- ** This is done using two interleaved state machines.
- ** The input state machine is looking for things like
- ** hidden dots; the message state machine is handling
- ** the larger picture (e.g., header versus body).
- */
-
- if (rsetsize)
- e->e_msgsize = 0;
- for (;;)
- {
- if (tTd(30, 35))
- sm_dprintf("top, istate=%d, mstate=%d\n", istate,
- mstate);
- for (;;)
- {
- if (pbp > peekbuf)
- c = *--pbp;
- else
- {
- while (!sm_io_eof(fp) && !sm_io_error(fp))
- {
- errno = 0;
- c = sm_io_getc(fp, SM_TIME_DEFAULT);
- if (c == SM_IO_EOF && errno == EINTR)
- {
- /* Interrupted, retry */
- sm_io_clearerr(fp);
- continue;
- }
-
- /* timeout? */
- if (c == SM_IO_EOF && errno == EAGAIN
- && smtpmode)
- {
- /*
- ** Override e_message in
- ** usrerr() as this is the
- ** reason for failure that
- ** should be logged for
- ** undelivered recipients.
- */
-
- e->e_message = NULL;
- errno = 0;
- inputerr = true;
- goto readabort;
- }
- break;
- }
- if (TrafficLogFile != NULL && !headeronly)
- {
- if (istate == IS_BOL)
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT,
- "%05d <<< ",
- (int) CurrentPid);
- if (c == SM_IO_EOF)
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT,
- "[EOF]\n");
- else
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- c);
- }
- if (c == SM_IO_EOF)
- goto readerr;
- if (SevenBitInput)
- c &= 0x7f;
- else
- HasEightBits |= bitset(0x80, c);
- }
- if (tTd(30, 94))
- sm_dprintf("istate=%d, c=%c (0x%x)\n",
- istate, (char) c, c);
- switch (istate)
- {
- case IS_BOL:
- if (c == '.')
- {
- istate = IS_DOT;
- continue;
- }
- break;
-
- case IS_DOT:
- if (c == '\n' && !ignrdot &&
- !bitset(EF_NL_NOT_EOL, e->e_flags))
- goto readerr;
- else if (c == '\r' &&
- !bitset(EF_CRLF_NOT_EOL, e->e_flags))
- {
- istate = IS_DOTCR;
- continue;
- }
- else if (ignrdot ||
- (c != '.' &&
- OpMode != MD_SMTP &&
- OpMode != MD_DAEMON &&
- OpMode != MD_ARPAFTP))
-
- {
- SM_ASSERT(pbp < peekbuf +
- sizeof(peekbuf));
- *pbp++ = c;
- c = '.';
- }
- break;
-
- case IS_DOTCR:
- if (c == '\n' && !ignrdot)
- goto readerr;
- else
- {
- /* push back the ".\rx" */
- SM_ASSERT(pbp < peekbuf +
- sizeof(peekbuf));
- *pbp++ = c;
- if (OpMode != MD_SMTP &&
- OpMode != MD_DAEMON &&
- OpMode != MD_ARPAFTP)
- {
- SM_ASSERT(pbp < peekbuf +
- sizeof(peekbuf));
- *pbp++ = '\r';
- c = '.';
- }
- else
- c = '\r';
- }
- break;
-
- case IS_CR:
- if (c == '\n')
- istate = IS_BOL;
- else
- {
- (void) sm_io_ungetc(fp, SM_TIME_DEFAULT,
- c);
- c = '\r';
- istate = IS_NORM;
- }
- goto bufferchar;
- }
-
- if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags))
- {
- istate = IS_CR;
- continue;
- }
- else if (c == '\n' && !bitset(EF_NL_NOT_EOL,
- e->e_flags))
- istate = IS_BOL;
- else
- istate = IS_NORM;
-
-bufferchar:
- if (!headeronly)
- {
- /* no overflow? */
- if (e->e_msgsize >= 0)
- {
- e->e_msgsize++;
- if (MaxMessageSize > 0 &&
- !bitset(EF_TOOBIG, e->e_flags) &&
- e->e_msgsize > MaxMessageSize)
- e->e_flags |= EF_TOOBIG;
- }
- }
- switch (mstate)
- {
- case MS_BODY:
- /* just put the character out */
- if (!bitset(EF_TOOBIG, e->e_flags))
- (void) sm_io_putc(df, SM_TIME_DEFAULT,
- c);
-
- /* FALLTHROUGH */
-
- case MS_DISCARD:
- continue;
- }
-
- SM_ASSERT(mstate == MS_UFROM || mstate == MS_HEADER);
-
- /* header -- buffer up */
- if (bp >= &buf[buflen - 2])
- {
- char *obuf;
-
- /* out of space for header */
- obuf = buf;
- if (buflen < MEMCHUNKSIZE)
- buflen *= 2;
- else
- buflen += MEMCHUNKSIZE;
- if (buflen <= 0)
- {
- sm_syslog(LOG_NOTICE, e->e_id,
- "header overflow from %s during message collect",
- CURHOSTNAME);
- errno = 0;
- e->e_flags |= EF_CLRQUEUE;
- e->e_status = "5.6.0";
- usrerrenh(e->e_status,
- "552 Headers too large");
- goto discard;
- }
- buf = xalloc(buflen);
- memmove(buf, obuf, bp - obuf);
- bp = &buf[bp - obuf];
- if (obuf != bufbuf)
- sm_free(obuf); /* XXX */
- }
-
- if (c != '\0')
- {
- *bp++ = c;
- ++hdrslen;
- if (!headeronly &&
- MaxHeadersLength > 0 &&
- hdrslen > MaxHeadersLength)
- {
- sm_syslog(LOG_NOTICE, e->e_id,
- "headers too large (%d max) from %s during message collect",
- MaxHeadersLength,
- CURHOSTNAME);
- errno = 0;
- e->e_flags |= EF_CLRQUEUE;
- e->e_status = "5.6.0";
- usrerrenh(e->e_status,
- "552 Headers too large (%d max)",
- MaxHeadersLength);
- discard:
- mstate = MS_DISCARD;
- }
- }
- if (istate == IS_BOL)
- break;
- }
- *bp = '\0';
-
-nextstate:
- if (tTd(30, 35))
- sm_dprintf("nextstate, istate=%d, mstate=%d, line=\"%s\"\n",
- istate, mstate, buf);
- switch (mstate)
- {
- case MS_UFROM:
- mstate = MS_HEADER;
-#ifndef NOTUNIX
- if (strncmp(buf, "From ", 5) == 0)
- {
- bp = buf;
- eatfrom(buf, e);
- continue;
- }
-#endif /* ! NOTUNIX */
- /* FALLTHROUGH */
-
- case MS_HEADER:
- if (!isheader(buf))
- {
- mstate = MS_BODY;
- goto nextstate;
- }
-
- /* check for possible continuation line */
- do
- {
- sm_io_clearerr(fp);
- errno = 0;
- c = sm_io_getc(fp, SM_TIME_DEFAULT);
-
- /* timeout? */
- if (c == SM_IO_EOF && errno == EAGAIN
- && smtpmode)
- {
- /*
- ** Override e_message in
- ** usrerr() as this is the
- ** reason for failure that
- ** should be logged for
- ** undelivered recipients.
- */
-
- e->e_message = NULL;
- errno = 0;
- inputerr = true;
- goto readabort;
- }
- } while (c == SM_IO_EOF && errno == EINTR);
- if (c != SM_IO_EOF)
- (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
- if (c == ' ' || c == '\t')
- {
- /* yep -- defer this */
- continue;
- }
-
- SM_ASSERT(bp > buf);
-
- /* guaranteed by isheader(buf) */
- SM_ASSERT(*(bp - 1) != '\n' || bp > buf + 1);
-
- /* trim off trailing CRLF or NL */
- if (*--bp != '\n' || *--bp != '\r')
- bp++;
- *bp = '\0';
-
- 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))
- sm_dprintf("EOH\n");
-
- if (headeronly)
- goto readerr;
-
- df = collect_eoh(e, numhdrs, hdrslen);
- if (df == NULL)
- e->e_flags |= EF_TOOBIG;
-
- bp = buf;
-
- /* toss blank line */
- if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) &&
- bp[0] == '\r' && bp[1] == '\n') ||
- (!bitset(EF_NL_NOT_EOL, e->e_flags) &&
- bp[0] == '\n'))
- {
- break;
- }
-
- /* if not a blank separator, write it out */
- if (!bitset(EF_TOOBIG, e->e_flags))
- {
- while (*bp != '\0')
- (void) sm_io_putc(df, SM_TIME_DEFAULT,
- *bp++);
- }
- break;
- }
- bp = buf;
- }
-
-readerr:
- if ((sm_io_eof(fp) && smtpmode) || sm_io_error(fp))
- {
- const char *errmsg;
-
- if (sm_io_eof(fp))
- errmsg = "unexpected close";
- else
- errmsg = sm_errstring(errno);
- if (tTd(30, 1))
- sm_dprintf("collect: premature EOM: %s\n", errmsg);
- if (LogLevel > 1)
- sm_syslog(LOG_WARNING, e->e_id,
- "collect: premature EOM: %s", errmsg);
- inputerr = true;
- }
-
- if (headeronly)
- return;
-
- if (mstate != MS_BODY)
- {
- /* no body or discard, so we never opened the data file */
- SM_ASSERT(df == NULL);
- df = collect_eoh(e, numhdrs, hdrslen);
- }
-
- if (df == NULL)
- {
- /* skip next few clauses */
- /* EMPTY */
- }
- else if (sm_io_flush(df, SM_TIME_DEFAULT) != 0 || sm_io_error(df))
- {
- dferror(df, "sm_io_flush||sm_io_error", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- else if (SuperSafe == SAFE_NO ||
- SuperSafe == SAFE_INTERACTIVE ||
- (SuperSafe == SAFE_REALLY_POSTMILTER && smtpmode))
- {
- /* skip next few clauses */
- /* EMPTY */
- /* Note: updfs() is not called in this case! */
- }
- else if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0 && errno != EINVAL)
- {
- int save_errno = errno;
-
- if (save_errno == EEXIST)
- {
- char *dfile;
- struct stat st;
- int dfd;
-
- dfile = queuename(e, DATAFL_LETTER);
- if (stat(dfile, &st) < 0)
- st.st_size = -1;
- errno = EEXIST;
- syserr("@collect: bfcommit(%s): already on disk, size=%ld",
- dfile, (long) st.st_size);
- dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
- if (dfd >= 0)
- dumpfd(dfd, true, true);
- }
- errno = save_errno;
- dferror(df, "bfcommit", e);
- flush_errors(true);
- finis(save_errno != EEXIST, true, ExitStat);
- }
- else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
- {
- dferror(df, "sm_io_getinfo", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- else if (fsync(afd) < 0)
- {
- dferror(df, "fsync", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
- {
- dferror(df, "sm_io_close", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- else
- {
- /* everything is happily flushed to disk */
- df = NULL;
-
- /* remove from available space in filesystem */
- updfs(e, 0, 1, "collect");
- }
-
- /* An EOF when running SMTP is an error */
- readabort:
- if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
- {
- char *host;
- char *problem;
- ADDRESS *q;
-
- host = RealHostName;
- if (host == NULL)
- host = "localhost";
-
- if (sm_io_eof(fp))
- problem = "unexpected close";
- else if (sm_io_error(fp))
- problem = "I/O error";
- else
- problem = "read timeout";
- if (LogLevel > 0 && sm_io_eof(fp))
- sm_syslog(LOG_NOTICE, e->e_id,
- "collect: %s on connection from %.100s, sender=%s",
- problem, host,
- shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
- if (sm_io_eof(fp))
- usrerr("421 4.4.1 collect: %s on connection from %s, from=%s",
- problem, host,
- shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
- else
- syserr("421 4.4.1 collect: %s on connection from %s, from=%s",
- problem, host,
- shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
- flush_errors(true);
-
- /* don't return an error indication */
- e->e_to = NULL;
- e->e_flags &= ~EF_FATALERRS;
- e->e_flags |= EF_CLRQUEUE;
-
- /* Don't send any message notification to sender */
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_DEAD(q->q_state))
- continue;
- q->q_state = QS_FATALERR;
- }
-
- (void) sm_io_close(df, SM_TIME_DEFAULT);
- df = NULL;
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
-
- /* Log collection information. */
- if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4)
- {
- logsender(e, e->e_msgid);
- e->e_flags &= ~EF_LOGSENDER;
- }
-
- /* check for message too large */
- if (bitset(EF_TOOBIG, e->e_flags))
- {
- e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;
- if (!bitset(EF_FATALERRS, e->e_flags))
- {
- e->e_status = "5.2.3";
- usrerrenh(e->e_status,
- "552 Message exceeds maximum fixed size (%ld)",
- MaxMessageSize);
- if (LogLevel > 6)
- sm_syslog(LOG_NOTICE, e->e_id,
- "message size (%ld) exceeds maximum (%ld)",
- e->e_msgsize, MaxMessageSize);
- }
- }
-
- /* check for illegal 8-bit data */
- if (HasEightBits)
- {
- e->e_flags |= EF_HAS8BIT;
- if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode) &&
- !bitset(EF_IS_MIME, e->e_flags))
- {
- e->e_status = "5.6.1";
- usrerrenh(e->e_status, "554 Eight bit data not allowed");
- }
- }
- else
- {
- /* if it claimed to be 8 bits, well, it lied.... */
- if (e->e_bodytype != NULL &&
- sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0)
- e->e_bodytype = "7BIT";
- }
-
- if (SuperSafe == SAFE_REALLY && !bitset(EF_FATALERRS, e->e_flags))
- {
- char *dfname = queuename(e, DATAFL_LETTER);
- if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
- SM_IO_RDONLY_B, NULL)) == NULL)
- {
- /* we haven't acked receipt yet, so just chuck this */
- syserr("@Cannot reopen %s", dfname);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- }
- else
- e->e_dfp = df;
-
- /* collect statistics */
- if (OpMode != MD_VERIFY)
- {
- /*
- ** Recalculate e_msgpriority, it is done at in eatheader()
- ** which is called (in 8.12) after the header is collected,
- ** hence e_msgsize is (most likely) incorrect.
- */
-
- e->e_msgpriority = e->e_msgsize
- - e->e_class * WkClassFact
- + e->e_nrcpts * WkRecipFact;
- markstats(e, (ADDRESS *) NULL, STATS_NORMAL);
- }
-}
-
-/*
-** DFERROR -- signal error on writing the data file.
-**
-** Called by collect(). Collect() always terminates the process
-** immediately after calling dferror(), which means that the SMTP
-** session will be terminated, which means that any error message
-** issued by dferror must be a 421 error, as per RFC 821.
-**
-** Parameters:
-** df -- the file pointer for the data file.
-** msg -- detailed message.
-** e -- the current envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Gives an error message.
-** Arranges for following output to go elsewhere.
-*/
-
-void
-dferror(df, msg, e)
- SM_FILE_T *volatile df;
- char *msg;
- register ENVELOPE *e;
-{
- char *dfname;
-
- dfname = queuename(e, DATAFL_LETTER);
- setstat(EX_IOERR);
- if (errno == ENOSPC)
- {
-#if STAT64 > 0
- struct stat64 st;
-#else /* STAT64 > 0 */
- struct stat st;
-#endif /* STAT64 > 0 */
- long avail;
- long bsize;
-
- e->e_flags |= EF_NO_BODY_RETN;
-
- if (
-#if STAT64 > 0
- fstat64(sm_io_getinfo(df, SM_IO_WHAT_FD, NULL), &st)
-#else /* STAT64 > 0 */
- fstat(sm_io_getinfo(df, SM_IO_WHAT_FD, NULL), &st)
-#endif /* STAT64 > 0 */
- < 0)
- st.st_size = 0;
- (void) sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, dfname,
- SM_IO_WRONLY_B, NULL, df);
- if (st.st_size <= 0)
- (void) sm_io_fprintf(df, SM_TIME_DEFAULT,
- "\n*** Mail could not be accepted");
- else
- (void) sm_io_fprintf(df, SM_TIME_DEFAULT,
- "\n*** Mail of at least %llu bytes could not be accepted\n",
- (ULONGLONG_T) st.st_size);
- (void) sm_io_fprintf(df, SM_TIME_DEFAULT,
- "*** at %s due to lack of disk space for temp file.\n",
- MyHostName);
- avail = freediskspace(qid_printqueue(e->e_qgrp, e->e_qdir),
- &bsize);
- if (avail > 0)
- {
- if (bsize > 1024)
- avail *= bsize / 1024;
- else if (bsize < 1024)
- avail /= 1024 / bsize;
- (void) sm_io_fprintf(df, SM_TIME_DEFAULT,
- "*** Currently, %ld kilobytes are available for mail temp files.\n",
- avail);
- }
-#if 0
- /* Wrong response code; should be 421. */
- e->e_status = "4.3.1";
- usrerrenh(e->e_status, "452 Out of disk space for temp file");
-#else /* 0 */
- syserr("421 4.3.1 Out of disk space for temp file");
-#endif /* 0 */
- }
- else
- syserr("421 4.3.0 collect: Cannot write %s (%s, uid=%d, gid=%d)",
- dfname, msg, (int) geteuid(), (int) getegid());
- if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
- SM_IO_WRONLY, NULL, df) == NULL)
- sm_syslog(LOG_ERR, e->e_id,
- "dferror: sm_io_reopen(\"/dev/null\") failed: %s",
- sm_errstring(errno));
-}
-/*
-** EATFROM -- chew up a UNIX style from line and process
-**
-** This does indeed make some assumptions about the format
-** of UNIX messages.
-**
-** Parameters:
-** fm -- the from line.
-** e -- envelope
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** extracts what information it can from the header,
-** such as the date.
-*/
-
-#ifndef NOTUNIX
-
-static char *DowList[] =
-{
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
-};
-
-static char *MonthList[] =
-{
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
- NULL
-};
-
-static void
-eatfrom(fm, e)
- char *volatile fm;
- register ENVELOPE *e;
-{
- register char *p;
- register char **dt;
-
- if (tTd(30, 2))
- sm_dprintf("eatfrom(%s)\n", fm);
-
- /* find the date part */
- p = fm;
- while (*p != '\0')
- {
- /* skip a word */
- while (*p != '\0' && *p != ' ')
- p++;
- while (*p == ' ')
- p++;
- if (strlen(p) < 17)
- {
- /* no room for the date */
- return;
- }
- if (!(isascii(*p) && isupper(*p)) ||
- p[3] != ' ' || p[13] != ':' || p[16] != ':')
- continue;
-
- /* we have a possible date */
- for (dt = DowList; *dt != NULL; dt++)
- if (strncmp(*dt, p, 3) == 0)
- break;
- if (*dt == NULL)
- continue;
-
- for (dt = MonthList; *dt != NULL; dt++)
- {
- if (strncmp(*dt, &p[4], 3) == 0)
- break;
- }
- if (*dt != NULL)
- break;
- }
-
- if (*p != '\0')
- {
- char *q, buf[25];
-
- /* we have found a date */
- (void) sm_strlcpy(buf, p, sizeof(buf));
- q = arpadate(buf);
- macdefine(&e->e_macro, A_TEMP, 'a', q);
- }
-}
-#endif /* ! NOTUNIX */
diff --git a/contrib/sendmail/src/conf.c b/contrib/sendmail/src/conf.c
deleted file mode 100644
index 0cbb88e..0000000
--- a/contrib/sendmail/src/conf.c
+++ /dev/null
@@ -1,6376 +0,0 @@
-/*
- * Copyright (c) 1998-2007 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: conf.c,v 8.1136 2007/10/10 00:06:45 ca Exp $")
-
-#include <sm/sendmail.h>
-#include <sendmail/pathnames.h>
-#if NEWDB
-# include "sm/bdb.h"
-#endif /* NEWDB */
-
-#include <daemon.h>
-#include "map.h"
-
-#ifdef DEC
-# if NETINET6
-/* for the IPv6 device lookup */
-# define _SOCKADDR_LEN
-# include <macros.h>
-# endif /* NETINET6 */
-#endif /* DEC */
-
-# include <sys/ioctl.h>
-# include <sys/param.h>
-
-#include <limits.h>
-#if NETINET || NETINET6
-# include <arpa/inet.h>
-#endif /* NETINET || NETINET6 */
-#if HASULIMIT && defined(HPUX11)
-# include <ulimit.h>
-#endif /* HASULIMIT && defined(HPUX11) */
-
-static void setupmaps __P((void));
-static void setupmailers __P((void));
-static void setupqueues __P((void));
-static int get_num_procs_online __P((void));
-static int add_hostnames __P((SOCKADDR *));
-
-#if NETINET6 && NEEDSGETIPNODE
-static struct hostent *getipnodebyname __P((char *, int, int, int *));
-static struct hostent *getipnodebyaddr __P((char *, int, int, int *));
-#endif /* NETINET6 && NEEDSGETIPNODE */
-
-
-/*
-** CONF.C -- Sendmail Configuration Tables.
-**
-** Defines the configuration of this installation.
-**
-** Configuration Variables:
-** HdrInfo -- a table describing well-known header fields.
-** Each entry has the field name and some flags,
-** which are described in sendmail.h.
-**
-** Notes:
-** I have tried to put almost all the reasonable
-** configuration information into the configuration
-** file read at runtime. My intent is that anything
-** here is a function of the version of UNIX you
-** are running, or is really static -- for example
-** the headers are a superset of widely used
-** protocols. If you find yourself playing with
-** this file too much, you may be making a mistake!
-*/
-
-
-/*
-** Header info table
-** Final (null) entry contains the flags used for any other field.
-**
-** Not all of these are actually handled specially by sendmail
-** at this time. They are included as placeholders, to let
-** you know that "someday" I intend to have sendmail do
-** something with them.
-*/
-
-struct hdrinfo HdrInfo[] =
-{
- /* originator fields, most to least significant */
- { "resent-sender", H_FROM|H_RESENT, NULL },
- { "resent-from", H_FROM|H_RESENT, NULL },
- { "resent-reply-to", H_FROM|H_RESENT, NULL },
- { "sender", H_FROM, NULL },
- { "from", H_FROM, NULL },
- { "reply-to", H_FROM, NULL },
- { "errors-to", H_FROM|H_ERRORSTO, NULL },
- { "full-name", H_ACHECK, NULL },
- { "return-receipt-to", H_RECEIPTTO, NULL },
- { "delivery-receipt-to", H_RECEIPTTO, NULL },
- { "disposition-notification-to", H_FROM, NULL },
-
- /* destination fields */
- { "to", H_RCPT, NULL },
- { "resent-to", H_RCPT|H_RESENT, NULL },
- { "cc", H_RCPT, NULL },
- { "resent-cc", H_RCPT|H_RESENT, NULL },
- { "bcc", H_RCPT|H_BCC, NULL },
- { "resent-bcc", H_RCPT|H_BCC|H_RESENT, NULL },
- { "apparently-to", H_RCPT, NULL },
-
- /* message identification and control */
- { "message-id", 0, NULL },
- { "resent-message-id", H_RESENT, NULL },
- { "message", H_EOH, NULL },
- { "text", H_EOH, NULL },
-
- /* date fields */
- { "date", 0, NULL },
- { "resent-date", H_RESENT, NULL },
-
- /* trace fields */
- { "received", H_TRACE|H_FORCE, NULL },
- { "x400-received", H_TRACE|H_FORCE, NULL },
- { "via", H_TRACE|H_FORCE, NULL },
- { "mail-from", H_TRACE|H_FORCE, NULL },
-
- /* miscellaneous fields */
- { "comments", H_FORCE|H_ENCODABLE, NULL },
- { "return-path", H_FORCE|H_ACHECK|H_BINDLATE, NULL },
- { "content-transfer-encoding", H_CTE, NULL },
- { "content-type", H_CTYPE, NULL },
- { "content-length", H_ACHECK, NULL },
- { "subject", H_ENCODABLE, NULL },
- { "x-authentication-warning", H_FORCE, NULL },
-
- { NULL, 0, NULL }
-};
-
-
-
-/*
-** Privacy values
-*/
-
-struct prival PrivacyValues[] =
-{
- { "public", PRIV_PUBLIC },
- { "needmailhelo", PRIV_NEEDMAILHELO },
- { "needexpnhelo", PRIV_NEEDEXPNHELO },
- { "needvrfyhelo", PRIV_NEEDVRFYHELO },
- { "noexpn", PRIV_NOEXPN },
- { "novrfy", PRIV_NOVRFY },
- { "restrictexpand", PRIV_RESTRICTEXPAND },
- { "restrictmailq", PRIV_RESTRICTMAILQ },
- { "restrictqrun", PRIV_RESTRICTQRUN },
- { "noetrn", PRIV_NOETRN },
- { "noverb", PRIV_NOVERB },
- { "authwarnings", PRIV_AUTHWARNINGS },
- { "noreceipts", PRIV_NORECEIPTS },
- { "nobodyreturn", PRIV_NOBODYRETN },
- { "goaway", PRIV_GOAWAY },
- { "noactualrecipient", PRIV_NOACTUALRECIPIENT },
- { NULL, 0 }
-};
-
-/*
-** DontBlameSendmail values
-*/
-
-struct dbsval DontBlameSendmailValues[] =
-{
- { "safe", DBS_SAFE },
- { "assumesafechown", DBS_ASSUMESAFECHOWN },
- { "groupwritabledirpathsafe", DBS_GROUPWRITABLEDIRPATHSAFE },
- { "groupwritableforwardfilesafe",
- DBS_GROUPWRITABLEFORWARDFILESAFE },
- { "groupwritableincludefilesafe",
- DBS_GROUPWRITABLEINCLUDEFILESAFE },
- { "groupwritablealiasfile", DBS_GROUPWRITABLEALIASFILE },
- { "worldwritablealiasfile", DBS_WORLDWRITABLEALIASFILE },
- { "forwardfileinunsafedirpath", DBS_FORWARDFILEINUNSAFEDIRPATH },
- { "includefileinunsafedirpath", DBS_INCLUDEFILEINUNSAFEDIRPATH },
- { "mapinunsafedirpath", DBS_MAPINUNSAFEDIRPATH },
- { "linkedaliasfileinwritabledir",
- DBS_LINKEDALIASFILEINWRITABLEDIR },
- { "linkedclassfileinwritabledir",
- DBS_LINKEDCLASSFILEINWRITABLEDIR },
- { "linkedforwardfileinwritabledir",
- DBS_LINKEDFORWARDFILEINWRITABLEDIR },
- { "linkedincludefileinwritabledir",
- DBS_LINKEDINCLUDEFILEINWRITABLEDIR },
- { "linkedmapinwritabledir", DBS_LINKEDMAPINWRITABLEDIR },
- { "linkedserviceswitchfileinwritabledir",
- DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR },
- { "filedeliverytohardlink", DBS_FILEDELIVERYTOHARDLINK },
- { "filedeliverytosymlink", DBS_FILEDELIVERYTOSYMLINK },
- { "writemaptohardlink", DBS_WRITEMAPTOHARDLINK },
- { "writemaptosymlink", DBS_WRITEMAPTOSYMLINK },
- { "writestatstohardlink", DBS_WRITESTATSTOHARDLINK },
- { "writestatstosymlink", DBS_WRITESTATSTOSYMLINK },
- { "forwardfileingroupwritabledirpath",
- DBS_FORWARDFILEINGROUPWRITABLEDIRPATH },
- { "includefileingroupwritabledirpath",
- DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH },
- { "classfileinunsafedirpath", DBS_CLASSFILEINUNSAFEDIRPATH },
- { "errorheaderinunsafedirpath", DBS_ERRORHEADERINUNSAFEDIRPATH },
- { "helpfileinunsafedirpath", DBS_HELPFILEINUNSAFEDIRPATH },
- { "forwardfileinunsafedirpathsafe",
- DBS_FORWARDFILEINUNSAFEDIRPATHSAFE },
- { "includefileinunsafedirpathsafe",
- DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE },
- { "runprograminunsafedirpath", DBS_RUNPROGRAMINUNSAFEDIRPATH },
- { "runwritableprogram", DBS_RUNWRITABLEPROGRAM },
- { "nonrootsafeaddr", DBS_NONROOTSAFEADDR },
- { "truststickybit", DBS_TRUSTSTICKYBIT },
- { "dontwarnforwardfileinunsafedirpath",
- DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH },
- { "insufficiententropy", DBS_INSUFFICIENTENTROPY },
- { "groupreadablesasldbfile", DBS_GROUPREADABLESASLDBFILE },
- { "groupwritablesasldbfile", DBS_GROUPWRITABLESASLDBFILE },
- { "groupwritableforwardfile", DBS_GROUPWRITABLEFORWARDFILE },
- { "groupwritableincludefile", DBS_GROUPWRITABLEINCLUDEFILE },
- { "worldwritableforwardfile", DBS_WORLDWRITABLEFORWARDFILE },
- { "worldwritableincludefile", DBS_WORLDWRITABLEINCLUDEFILE },
- { "groupreadablekeyfile", DBS_GROUPREADABLEKEYFILE },
-#if _FFR_GROUPREADABLEAUTHINFOFILE
- { "groupreadableadefaultauthinfofile",
- DBS_GROUPREADABLEAUTHINFOFILE },
-#endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
- { NULL, 0 }
-};
-
-/*
-** Miscellaneous stuff.
-*/
-
-int DtableSize = 50; /* max open files; reset in 4.2bsd */
-/*
-** SETDEFAULTS -- set default values
-**
-** Some of these must be initialized using direct code since they
-** depend on run-time values. So let's do all of them this way.
-**
-** Parameters:
-** e -- the default envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Initializes a bunch of global variables to their
-** default values.
-*/
-
-#define MINUTES * 60
-#define HOURS * 60 MINUTES
-#define DAYS * 24 HOURS
-
-#ifndef MAXRULERECURSION
-# define MAXRULERECURSION 50 /* max ruleset recursion depth */
-#endif /* ! MAXRULERECURSION */
-
-void
-setdefaults(e)
- register ENVELOPE *e;
-{
- int i;
- int numprocs;
- struct passwd *pw;
-
- numprocs = get_num_procs_online();
- SpaceSub = ' '; /* option B */
- QueueLA = 8 * numprocs; /* option x */
- RefuseLA = 12 * numprocs; /* option X */
- WkRecipFact = 30000L; /* option y */
- WkClassFact = 1800L; /* option z */
- WkTimeFact = 90000L; /* option Z */
- QueueFactor = WkRecipFact * 20; /* option q */
- QueueMode = QM_NORMAL; /* what queue items to act upon */
- FileMode = (RealUid != geteuid()) ? 0644 : 0600;
- /* option F */
- QueueFileMode = (RealUid != geteuid()) ? 0644 : 0600;
- /* option QueueFileMode */
-
- if (((pw = sm_getpwnam("mailnull")) != NULL && pw->pw_uid != 0) ||
- ((pw = sm_getpwnam("sendmail")) != NULL && pw->pw_uid != 0) ||
- ((pw = sm_getpwnam("daemon")) != NULL && pw->pw_uid != 0))
- {
- DefUid = pw->pw_uid; /* option u */
- DefGid = pw->pw_gid; /* option g */
- DefUser = newstr(pw->pw_name);
- }
- else
- {
- DefUid = 1; /* option u */
- DefGid = 1; /* option g */
- setdefuser();
- }
- TrustedUid = 0;
- if (tTd(37, 4))
- sm_dprintf("setdefaults: DefUser=%s, DefUid=%d, DefGid=%d\n",
- DefUser != NULL ? DefUser : "<1:1>",
- (int) DefUid, (int) DefGid);
- CheckpointInterval = 10; /* option C */
- MaxHopCount = 25; /* option h */
- set_delivery_mode(SM_FORK, e); /* option d */
- e->e_errormode = EM_PRINT; /* option e */
- e->e_qgrp = NOQGRP;
- e->e_qdir = NOQDIR;
- e->e_xfqgrp = NOQGRP;
- e->e_xfqdir = NOQDIR;
- e->e_ctime = curtime();
- SevenBitInput = false; /* option 7 */
- MaxMciCache = 1; /* option k */
- MciCacheTimeout = 5 MINUTES; /* option K */
- LogLevel = 9; /* option L */
-#if MILTER
- MilterLogLevel = -1;
-#endif /* MILTER */
- inittimeouts(NULL, false); /* option r */
- PrivacyFlags = PRIV_PUBLIC; /* option p */
- MeToo = true; /* option m */
- SendMIMEErrors = true; /* option f */
- SuperSafe = SAFE_REALLY; /* option s */
- clrbitmap(DontBlameSendmail); /* DontBlameSendmail option */
-#if MIME8TO7
- MimeMode = MM_CVTMIME|MM_PASS8BIT; /* option 8 */
-#else /* MIME8TO7 */
- MimeMode = MM_PASS8BIT;
-#endif /* MIME8TO7 */
- for (i = 0; i < MAXTOCLASS; i++)
- {
- TimeOuts.to_q_return[i] = 5 DAYS; /* option T */
- TimeOuts.to_q_warning[i] = 0; /* option T */
- }
- ServiceSwitchFile = "/etc/mail/service.switch";
- ServiceCacheMaxAge = (time_t) 10;
- HostsFile = _PATH_HOSTS;
- PidFile = newstr(_PATH_SENDMAILPID);
- MustQuoteChars = "@,;:\\()[].'";
- MciInfoTimeout = 30 MINUTES;
- MaxRuleRecursion = MAXRULERECURSION;
- MaxAliasRecursion = 10;
- MaxMacroRecursion = 10;
- ColonOkInAddr = true;
- DontLockReadFiles = true;
- DontProbeInterfaces = DPI_PROBEALL;
- DoubleBounceAddr = "postmaster";
- MaxHeadersLength = MAXHDRSLEN;
- MaxMimeHeaderLength = MAXLINE;
- MaxMimeFieldLength = MaxMimeHeaderLength / 2;
- MaxForwardEntries = 0;
- FastSplit = 1;
- MaxNOOPCommands = MAXNOOPCOMMANDS;
-#if SASL
- AuthMechanisms = newstr(AUTH_MECHANISMS);
- AuthRealm = NULL;
- MaxSLBits = INT_MAX;
-#endif /* SASL */
-#if STARTTLS
- TLS_Srv_Opts = TLS_I_SRV;
-#endif /* STARTTLS */
-#ifdef HESIOD_INIT
- HesiodContext = NULL;
-#endif /* HESIOD_INIT */
-#if NETINET6
- /* Detect if IPv6 is available at run time */
- i = socket(AF_INET6, SOCK_STREAM, 0);
- if (i >= 0)
- {
- InetMode = AF_INET6;
- (void) close(i);
- }
- else
- InetMode = AF_INET;
-#else /* NETINET6 */
- InetMode = AF_INET;
-#endif /* NETINET6 */
- ControlSocketName = NULL;
- memset(&ConnectOnlyTo, '\0', sizeof(ConnectOnlyTo));
- DataFileBufferSize = 4096;
- XscriptFileBufferSize = 4096;
- for (i = 0; i < MAXRWSETS; i++)
- RuleSetNames[i] = NULL;
-#if MILTER
- InputFilters[0] = NULL;
-#endif /* MILTER */
- RejectLogInterval = 3 HOURS;
-#if REQUIRES_DIR_FSYNC
- RequiresDirfsync = true;
-#endif /* REQUIRES_DIR_FSYNC */
- ConnectionRateWindowSize = 60;
- setupmaps();
- setupqueues();
- setupmailers();
- setupheaders();
-}
-
-
-/*
-** SETDEFUSER -- set/reset DefUser using DefUid (for initgroups())
-*/
-
-void
-setdefuser()
-{
- struct passwd *defpwent;
- static char defuserbuf[40];
-
- DefUser = defuserbuf;
- defpwent = sm_getpwuid(DefUid);
- (void) sm_strlcpy(defuserbuf,
- (defpwent == NULL || defpwent->pw_name == NULL)
- ? "nobody" : defpwent->pw_name,
- sizeof(defuserbuf));
- if (tTd(37, 4))
- sm_dprintf("setdefuser: DefUid=%d, DefUser=%s\n",
- (int) DefUid, DefUser);
-}
-/*
-** SETUPQUEUES -- initialize default queues
-**
-** The mqueue QUEUE structure gets filled in after readcf() but
-** we need something to point to now for the mailer setup,
-** which use "mqueue" as default queue.
-*/
-
-static void
-setupqueues()
-{
- char buf[100];
-
- MaxRunnersPerQueue = 1;
- (void) sm_strlcpy(buf, "mqueue, P=/var/spool/mqueue", sizeof(buf));
- makequeue(buf, false);
-}
-/*
-** SETUPMAILERS -- initialize default mailers
-*/
-
-static void
-setupmailers()
-{
- char buf[100];
-
- (void) sm_strlcpy(buf, "prog, P=/bin/sh, F=lsouDq9, T=X-Unix/X-Unix/X-Unix, A=sh -c \201u",
- sizeof(buf));
- makemailer(buf);
-
- (void) sm_strlcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=X-Unix/X-Unix/X-Unix, A=FILE \201u",
- sizeof(buf));
- makemailer(buf);
-
- (void) sm_strlcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u",
- sizeof(buf));
- makemailer(buf);
- initerrmailers();
-}
-/*
-** SETUPMAPS -- set up map classes
-*/
-
-#define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \
- { \
- extern bool parse __P((MAP *, char *)); \
- extern bool open __P((MAP *, int)); \
- extern void close __P((MAP *)); \
- extern char *lookup __P((MAP *, char *, char **, int *)); \
- extern void store __P((MAP *, char *, char *)); \
- s = stab(name, ST_MAPCLASS, ST_ENTER); \
- s->s_mapclass.map_cname = name; \
- s->s_mapclass.map_ext = ext; \
- s->s_mapclass.map_cflags = flags; \
- s->s_mapclass.map_parse = parse; \
- s->s_mapclass.map_open = open; \
- s->s_mapclass.map_close = close; \
- s->s_mapclass.map_lookup = lookup; \
- s->s_mapclass.map_store = store; \
- }
-
-static void
-setupmaps()
-{
- register STAB *s;
-
-#if NEWDB
-# if DB_VERSION_MAJOR > 1
- int major_v, minor_v, patch_v;
-
- (void) db_version(&major_v, &minor_v, &patch_v);
- if (major_v != DB_VERSION_MAJOR || minor_v != DB_VERSION_MINOR)
- {
- errno = 0;
- syserr("Berkeley DB version mismatch: compiled against %d.%d.%d, run-time linked against %d.%d.%d",
- DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH,
- major_v, minor_v, patch_v);
- }
-# endif /* DB_VERSION_MAJOR > 1 */
-
- MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
- map_parseargs, hash_map_open, db_map_close,
- db_map_lookup, db_map_store);
-
- MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
- map_parseargs, bt_map_open, db_map_close,
- db_map_lookup, db_map_store);
-#endif /* NEWDB */
-
-#if NDBM
- MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE,
- map_parseargs, ndbm_map_open, ndbm_map_close,
- ndbm_map_lookup, ndbm_map_store);
-#endif /* NDBM */
-
-#if NIS
- MAPDEF("nis", NULL, MCF_ALIASOK,
- map_parseargs, nis_map_open, null_map_close,
- nis_map_lookup, null_map_store);
-#endif /* NIS */
-
-#if NISPLUS
- MAPDEF("nisplus", NULL, MCF_ALIASOK,
- map_parseargs, nisplus_map_open, null_map_close,
- nisplus_map_lookup, null_map_store);
-#endif /* NISPLUS */
-
-#if LDAPMAP
- MAPDEF("ldap", NULL, MCF_ALIASOK|MCF_NOTPERSIST,
- ldapmap_parseargs, ldapmap_open, ldapmap_close,
- ldapmap_lookup, null_map_store);
-#endif /* LDAPMAP */
-
-#if PH_MAP
- MAPDEF("ph", NULL, MCF_NOTPERSIST,
- ph_map_parseargs, ph_map_open, ph_map_close,
- ph_map_lookup, null_map_store);
-#endif /* PH_MAP */
-
-#if MAP_NSD
- /* IRIX 6.5 nsd support */
- MAPDEF("nsd", NULL, MCF_ALIASOK,
- map_parseargs, null_map_open, null_map_close,
- nsd_map_lookup, null_map_store);
-#endif /* MAP_NSD */
-
-#if HESIOD
- MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY,
- map_parseargs, hes_map_open, hes_map_close,
- hes_map_lookup, null_map_store);
-#endif /* HESIOD */
-
-#if NETINFO
- MAPDEF("netinfo", NULL, MCF_ALIASOK,
- map_parseargs, ni_map_open, null_map_close,
- ni_map_lookup, null_map_store);
-#endif /* NETINFO */
-
-#if 0
- MAPDEF("dns", NULL, 0,
- dns_map_init, null_map_open, null_map_close,
- dns_map_lookup, null_map_store);
-#endif /* 0 */
-
-#if NAMED_BIND
-# if DNSMAP
-# if _FFR_DNSMAP_ALIASABLE
- MAPDEF("dns", NULL, MCF_ALIASOK,
- dns_map_parseargs, dns_map_open, null_map_close,
- dns_map_lookup, null_map_store);
-# else /* _FFR_DNSMAP_ALIASABLE */
- MAPDEF("dns", NULL, 0,
- dns_map_parseargs, dns_map_open, null_map_close,
- dns_map_lookup, null_map_store);
-# endif /* _FFR_DNSMAP_ALIASABLE */
-# endif /* DNSMAP */
-#endif /* NAMED_BIND */
-
-#if NAMED_BIND
- /* best MX DNS lookup */
- MAPDEF("bestmx", NULL, MCF_OPTFILE,
- map_parseargs, null_map_open, null_map_close,
- bestmx_map_lookup, null_map_store);
-#endif /* NAMED_BIND */
-
- MAPDEF("host", NULL, 0,
- host_map_init, null_map_open, null_map_close,
- host_map_lookup, null_map_store);
-
- MAPDEF("text", NULL, MCF_ALIASOK,
- map_parseargs, text_map_open, null_map_close,
- text_map_lookup, null_map_store);
-
- MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY,
- map_parseargs, stab_map_open, null_map_close,
- stab_map_lookup, stab_map_store);
-
- MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE,
- map_parseargs, impl_map_open, impl_map_close,
- impl_map_lookup, impl_map_store);
-
- /* access to system passwd file */
- MAPDEF("user", NULL, MCF_OPTFILE,
- map_parseargs, user_map_open, null_map_close,
- user_map_lookup, null_map_store);
-
- /* dequote map */
- MAPDEF("dequote", NULL, 0,
- dequote_init, null_map_open, null_map_close,
- dequote_map, null_map_store);
-
-#if MAP_REGEX
- MAPDEF("regex", NULL, 0,
- regex_map_init, null_map_open, null_map_close,
- regex_map_lookup, null_map_store);
-#endif /* MAP_REGEX */
-
-#if USERDB
- /* user database */
- MAPDEF("userdb", ".db", 0,
- map_parseargs, null_map_open, null_map_close,
- udb_map_lookup, null_map_store);
-#endif /* USERDB */
-
- /* arbitrary programs */
- MAPDEF("program", NULL, MCF_ALIASOK,
- map_parseargs, null_map_open, null_map_close,
- prog_map_lookup, null_map_store);
-
- /* sequenced maps */
- MAPDEF("sequence", NULL, MCF_ALIASOK,
- seq_map_parse, null_map_open, null_map_close,
- seq_map_lookup, seq_map_store);
-
- /* switched interface to sequenced maps */
- MAPDEF("switch", NULL, MCF_ALIASOK,
- map_parseargs, switch_map_open, null_map_close,
- seq_map_lookup, seq_map_store);
-
- /* null map lookup -- really for internal use only */
- MAPDEF("null", NULL, MCF_ALIASOK|MCF_OPTFILE,
- map_parseargs, null_map_open, null_map_close,
- null_map_lookup, null_map_store);
-
- /* syslog map -- logs information to syslog */
- MAPDEF("syslog", NULL, 0,
- syslog_map_parseargs, null_map_open, null_map_close,
- syslog_map_lookup, null_map_store);
-
- /* macro storage map -- rulesets can set macros */
- MAPDEF("macro", NULL, 0,
- dequote_init, null_map_open, null_map_close,
- macro_map_lookup, null_map_store);
-
- /* arithmetic map -- add/subtract/compare */
- MAPDEF("arith", NULL, 0,
- dequote_init, null_map_open, null_map_close,
- arith_map_lookup, null_map_store);
-
-#if SOCKETMAP
- /* arbitrary daemons */
- MAPDEF("socket", NULL, MCF_ALIASOK,
- map_parseargs, socket_map_open, socket_map_close,
- socket_map_lookup, null_map_store);
-#endif /* SOCKETMAP */
-
-#if _FFR_DPRINTF_MAP
- /* dprintf map -- logs information to syslog */
- MAPDEF("dprintf", NULL, 0,
- dprintf_map_parseargs, null_map_open, null_map_close,
- dprintf_map_lookup, null_map_store);
-#endif /* _FFR_DPRINTF_MAP */
-
- if (tTd(38, 2))
- {
- /* bogus map -- always return tempfail */
- MAPDEF("bogus", NULL, MCF_ALIASOK|MCF_OPTFILE,
- map_parseargs, null_map_open, null_map_close,
- bogus_map_lookup, null_map_store);
- }
-}
-
-#undef MAPDEF
-/*
-** INITHOSTMAPS -- initial host-dependent maps
-**
-** This should act as an interface to any local service switch
-** provided by the host operating system.
-**
-** Parameters:
-** none
-**
-** Returns:
-** none
-**
-** Side Effects:
-** Should define maps "host" and "users" as necessary
-** for this OS. If they are not defined, they will get
-** a default value later. It should check to make sure
-** they are not defined first, since it's possible that
-** the config file has provided an override.
-*/
-
-void
-inithostmaps()
-{
- register int i;
- int nmaps;
- char *maptype[MAXMAPSTACK];
- short mapreturn[MAXMAPACTIONS];
- char buf[MAXLINE];
-
- /*
- ** Make sure we have a host map.
- */
-
- if (stab("host", ST_MAP, ST_FIND) == NULL)
- {
- /* user didn't initialize: set up host map */
- (void) sm_strlcpy(buf, "host host", sizeof(buf));
-#if NAMED_BIND
- if (ConfigLevel >= 2)
- (void) sm_strlcat(buf, " -a. -D", sizeof(buf));
-#endif /* NAMED_BIND */
- (void) makemapentry(buf);
- }
-
- /*
- ** Set up default aliases maps
- */
-
- nmaps = switch_map_find("aliases", maptype, mapreturn);
- for (i = 0; i < nmaps; i++)
- {
- if (strcmp(maptype[i], "files") == 0 &&
- stab("aliases.files", ST_MAP, ST_FIND) == NULL)
- {
- (void) sm_strlcpy(buf, "aliases.files null",
- sizeof(buf));
- (void) makemapentry(buf);
- }
-#if NISPLUS
- else if (strcmp(maptype[i], "nisplus") == 0 &&
- stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL)
- {
- (void) sm_strlcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion mail_aliases.org_dir",
- sizeof(buf));
- (void) makemapentry(buf);
- }
-#endif /* NISPLUS */
-#if NIS
- else if (strcmp(maptype[i], "nis") == 0 &&
- stab("aliases.nis", ST_MAP, ST_FIND) == NULL)
- {
- (void) sm_strlcpy(buf, "aliases.nis nis mail.aliases",
- sizeof(buf));
- (void) makemapentry(buf);
- }
-#endif /* NIS */
-#if NETINFO
- else if (strcmp(maptype[i], "netinfo") == 0 &&
- stab("aliases.netinfo", ST_MAP, ST_FIND) == NULL)
- {
- (void) sm_strlcpy(buf, "aliases.netinfo netinfo -z, /aliases",
- sizeof(buf));
- (void) makemapentry(buf);
- }
-#endif /* NETINFO */
-#if HESIOD
- else if (strcmp(maptype[i], "hesiod") == 0 &&
- stab("aliases.hesiod", ST_MAP, ST_FIND) == NULL)
- {
- (void) sm_strlcpy(buf, "aliases.hesiod hesiod aliases",
- sizeof(buf));
- (void) makemapentry(buf);
- }
-#endif /* HESIOD */
-#if LDAPMAP && defined(SUN_EXTENSIONS) && \
- defined(SUN_SIMPLIFIED_LDAP) && HASLDAPGETALIASBYNAME
- else if (strcmp(maptype[i], "ldap") == 0 &&
- stab("aliases.ldap", ST_MAP, ST_FIND) == NULL)
- {
- (void) strlcpy(buf, "aliases.ldap ldap -b . -h localhost -k mail=%0 -v mailgroup",
- sizeof buf);
- (void) makemapentry(buf);
- }
-#endif /* LDAPMAP && defined(SUN_EXTENSIONS) && ... */
- }
- if (stab("aliases", ST_MAP, ST_FIND) == NULL)
- {
- (void) sm_strlcpy(buf, "aliases switch aliases", sizeof(buf));
- (void) makemapentry(buf);
- }
-}
-
-/*
-** SWITCH_MAP_FIND -- find the list of types associated with a map
-**
-** This is the system-dependent interface to the service switch.
-**
-** Parameters:
-** service -- the name of the service of interest.
-** maptype -- an out-array of strings containing the types
-** of access to use for this service. There can
-** be at most MAXMAPSTACK types for a single service.
-** mapreturn -- an out-array of return information bitmaps
-** for the map.
-**
-** Returns:
-** The number of map types filled in, or -1 for failure.
-**
-** Side effects:
-** Preserves errno so nothing in the routine clobbers it.
-*/
-
-#if defined(SOLARIS) || (defined(sony_news) && defined(__svr4))
-# define _USE_SUN_NSSWITCH_
-#endif /* defined(SOLARIS) || (defined(sony_news) && defined(__svr4)) */
-
-#if _FFR_HPUX_NSSWITCH
-# ifdef __hpux
-# define _USE_SUN_NSSWITCH_
-# endif /* __hpux */
-#endif /* _FFR_HPUX_NSSWITCH */
-
-#ifdef _USE_SUN_NSSWITCH_
-# include <nsswitch.h>
-#endif /* _USE_SUN_NSSWITCH_ */
-
-#if defined(ultrix) || (defined(__osf__) && defined(__alpha))
-# define _USE_DEC_SVC_CONF_
-#endif /* defined(ultrix) || (defined(__osf__) && defined(__alpha)) */
-
-#ifdef _USE_DEC_SVC_CONF_
-# include <sys/svcinfo.h>
-#endif /* _USE_DEC_SVC_CONF_ */
-
-int
-switch_map_find(service, maptype, mapreturn)
- char *service;
- char *maptype[MAXMAPSTACK];
- short mapreturn[MAXMAPACTIONS];
-{
- int svcno = 0;
- int save_errno = errno;
-
-#ifdef _USE_SUN_NSSWITCH_
- struct __nsw_switchconfig *nsw_conf;
- enum __nsw_parse_err pserr;
- struct __nsw_lookup *lk;
- static struct __nsw_lookup lkp0 =
- { "files", {1, 0, 0, 0}, NULL, NULL };
- static struct __nsw_switchconfig lkp_default =
- { 0, "sendmail", 3, &lkp0 };
-
- for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
- mapreturn[svcno] = 0;
-
- if ((nsw_conf = __nsw_getconfig(service, &pserr)) == NULL)
- lk = lkp_default.lookups;
- else
- lk = nsw_conf->lookups;
- svcno = 0;
- while (lk != NULL && svcno < MAXMAPSTACK)
- {
- maptype[svcno] = lk->service_name;
- if (lk->actions[__NSW_NOTFOUND] == __NSW_RETURN)
- mapreturn[MA_NOTFOUND] |= 1 << svcno;
- if (lk->actions[__NSW_TRYAGAIN] == __NSW_RETURN)
- mapreturn[MA_TRYAGAIN] |= 1 << svcno;
- if (lk->actions[__NSW_UNAVAIL] == __NSW_RETURN)
- mapreturn[MA_TRYAGAIN] |= 1 << svcno;
- svcno++;
- lk = lk->next;
- }
- errno = save_errno;
- return svcno;
-#endif /* _USE_SUN_NSSWITCH_ */
-
-#ifdef _USE_DEC_SVC_CONF_
- struct svcinfo *svcinfo;
- int svc;
-
- for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
- mapreturn[svcno] = 0;
-
- svcinfo = getsvc();
- if (svcinfo == NULL)
- goto punt;
- if (strcmp(service, "hosts") == 0)
- svc = SVC_HOSTS;
- else if (strcmp(service, "aliases") == 0)
- svc = SVC_ALIASES;
- else if (strcmp(service, "passwd") == 0)
- svc = SVC_PASSWD;
- else
- {
- errno = save_errno;
- return -1;
- }
- for (svcno = 0; svcno < SVC_PATHSIZE && svcno < MAXMAPSTACK; svcno++)
- {
- switch (svcinfo->svcpath[svc][svcno])
- {
- case SVC_LOCAL:
- maptype[svcno] = "files";
- break;
-
- case SVC_YP:
- maptype[svcno] = "nis";
- break;
-
- case SVC_BIND:
- maptype[svcno] = "dns";
- break;
-
-# ifdef SVC_HESIOD
- case SVC_HESIOD:
- maptype[svcno] = "hesiod";
- break;
-# endif /* SVC_HESIOD */
-
- case SVC_LAST:
- errno = save_errno;
- return svcno;
- }
- }
- errno = save_errno;
- return svcno;
-#endif /* _USE_DEC_SVC_CONF_ */
-
-#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
- /*
- ** Fall-back mechanism.
- */
-
- STAB *st;
- static time_t servicecachetime; /* time service switch was cached */
- time_t now = curtime();
-
- for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
- mapreturn[svcno] = 0;
-
- if ((now - servicecachetime) > (time_t) ServiceCacheMaxAge)
- {
- /* (re)read service switch */
- register SM_FILE_T *fp;
- long sff = SFF_REGONLY|SFF_OPENASROOT|SFF_NOLOCK;
-
- if (!bitnset(DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR,
- DontBlameSendmail))
- sff |= SFF_NOWLINK;
-
- if (ConfigFileRead)
- servicecachetime = now;
- fp = safefopen(ServiceSwitchFile, O_RDONLY, 0, sff);
- if (fp != NULL)
- {
- char buf[MAXLINE];
-
- while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf,
- sizeof(buf)) != NULL)
- {
- register char *p;
-
- p = strpbrk(buf, "#\n");
- if (p != NULL)
- *p = '\0';
- p = strpbrk(buf, " \t");
- if (p != NULL)
- *p++ = '\0';
- if (buf[0] == '\0')
- continue;
- if (p == NULL)
- {
- sm_syslog(LOG_ERR, NOQID,
- "Bad line on %.100s: %.100s",
- ServiceSwitchFile,
- buf);
- continue;
- }
- while (isspace(*p))
- p++;
- if (*p == '\0')
- continue;
-
- /*
- ** Find/allocate space for this service entry.
- ** Space for all of the service strings
- ** are allocated at once. This means
- ** that we only have to free the first
- ** one to free all of them.
- */
-
- st = stab(buf, ST_SERVICE, ST_ENTER);
- if (st->s_service[0] != NULL)
- sm_free((void *) st->s_service[0]); /* XXX */
- p = newstr(p);
- for (svcno = 0; svcno < MAXMAPSTACK; )
- {
- if (*p == '\0')
- break;
- st->s_service[svcno++] = p;
- p = strpbrk(p, " \t");
- if (p == NULL)
- break;
- *p++ = '\0';
- while (isspace(*p))
- p++;
- }
- if (svcno < MAXMAPSTACK)
- st->s_service[svcno] = NULL;
- }
- (void) sm_io_close(fp, SM_TIME_DEFAULT);
- }
- }
-
- /* look up entry in cache */
- st = stab(service, ST_SERVICE, ST_FIND);
- if (st != NULL && st->s_service[0] != NULL)
- {
- /* extract data */
- svcno = 0;
- while (svcno < MAXMAPSTACK)
- {
- maptype[svcno] = st->s_service[svcno];
- if (maptype[svcno++] == NULL)
- break;
- }
- errno = save_errno;
- return --svcno;
- }
-#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
-
-#if !defined(_USE_SUN_NSSWITCH_)
- /* if the service file doesn't work, use an absolute fallback */
-# ifdef _USE_DEC_SVC_CONF_
- punt:
-# endif /* _USE_DEC_SVC_CONF_ */
- for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
- mapreturn[svcno] = 0;
- svcno = 0;
- if (strcmp(service, "aliases") == 0)
- {
- maptype[svcno++] = "files";
-# if defined(AUTO_NETINFO_ALIASES) && defined (NETINFO)
- maptype[svcno++] = "netinfo";
-# endif /* defined(AUTO_NETINFO_ALIASES) && defined (NETINFO) */
-# ifdef AUTO_NIS_ALIASES
-# if NISPLUS
- maptype[svcno++] = "nisplus";
-# endif /* NISPLUS */
-# if NIS
- maptype[svcno++] = "nis";
-# endif /* NIS */
-# endif /* AUTO_NIS_ALIASES */
- errno = save_errno;
- return svcno;
- }
- if (strcmp(service, "hosts") == 0)
- {
-# if NAMED_BIND
- maptype[svcno++] = "dns";
-# else /* NAMED_BIND */
-# if defined(sun) && !defined(BSD)
- /* SunOS */
- maptype[svcno++] = "nis";
-# endif /* defined(sun) && !defined(BSD) */
-# endif /* NAMED_BIND */
-# if defined(AUTO_NETINFO_HOSTS) && defined (NETINFO)
- maptype[svcno++] = "netinfo";
-# endif /* defined(AUTO_NETINFO_HOSTS) && defined (NETINFO) */
- maptype[svcno++] = "files";
- errno = save_errno;
- return svcno;
- }
- errno = save_errno;
- return -1;
-#endif /* !defined(_USE_SUN_NSSWITCH_) */
-}
-/*
-** USERNAME -- return the user id of the logged in user.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** The login name of the logged in user.
-**
-** Side Effects:
-** none.
-**
-** Notes:
-** The return value is statically allocated.
-*/
-
-char *
-username()
-{
- static char *myname = NULL;
- extern char *getlogin();
- register struct passwd *pw;
-
- /* cache the result */
- if (myname == NULL)
- {
- myname = getlogin();
- if (myname == NULL || myname[0] == '\0')
- {
- pw = sm_getpwuid(RealUid);
- if (pw != NULL)
- myname = pw->pw_name;
- }
- else
- {
- uid_t uid = RealUid;
-
- if ((pw = sm_getpwnam(myname)) == NULL ||
- (uid != 0 && uid != pw->pw_uid))
- {
- pw = sm_getpwuid(uid);
- if (pw != NULL)
- myname = pw->pw_name;
- }
- }
- if (myname == NULL || myname[0] == '\0')
- {
- syserr("554 5.3.0 Who are you?");
- myname = "postmaster";
- }
- else if (strpbrk(myname, ",;:/|\"\\") != NULL)
- myname = addquotes(myname, NULL);
- else
- myname = sm_pstrdup_x(myname);
- }
- return myname;
-}
-/*
-** TTYPATH -- Get the path of the user's tty
-**
-** Returns the pathname of the user's tty. Returns NULL if
-** the user is not logged in or if s/he has write permission
-** denied.
-**
-** Parameters:
-** none
-**
-** Returns:
-** pathname of the user's tty.
-** NULL if not logged in or write permission denied.
-**
-** Side Effects:
-** none.
-**
-** WARNING:
-** Return value is in a local buffer.
-**
-** Called By:
-** savemail
-*/
-
-char *
-ttypath()
-{
- struct stat stbuf;
- register char *pathn;
- extern char *ttyname();
- extern char *getlogin();
-
- /* compute the pathname of the controlling tty */
- if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL &&
- (pathn = ttyname(0)) == NULL)
- {
- errno = 0;
- return NULL;
- }
-
- /* see if we have write permission */
- if (stat(pathn, &stbuf) < 0 || !bitset(S_IWOTH, stbuf.st_mode))
- {
- errno = 0;
- return NULL;
- }
-
- /* see if the user is logged in */
- if (getlogin() == NULL)
- return NULL;
-
- /* looks good */
- return pathn;
-}
-/*
-** CHECKCOMPAT -- check for From and To person compatible.
-**
-** This routine can be supplied on a per-installation basis
-** to determine whether a person is allowed to send a message.
-** This allows restriction of certain types of internet
-** forwarding or registration of users.
-**
-** If the hosts are found to be incompatible, an error
-** message should be given using "usrerr" and an EX_ code
-** should be returned. You can also set to->q_status to
-** a DSN-style status code.
-**
-** EF_NO_BODY_RETN can be set in e->e_flags to suppress the
-** body during the return-to-sender function; this should be done
-** on huge messages. This bit may already be set by the ESMTP
-** protocol.
-**
-** Parameters:
-** to -- the person being sent to.
-**
-** Returns:
-** an exit status
-**
-** Side Effects:
-** none (unless you include the usrerr stuff)
-*/
-
-int
-checkcompat(to, e)
- register ADDRESS *to;
- register ENVELOPE *e;
-{
- if (tTd(49, 1))
- sm_dprintf("checkcompat(to=%s, from=%s)\n",
- to->q_paddr, e->e_from.q_paddr);
-
-#ifdef EXAMPLE_CODE
- /* this code is intended as an example only */
- register STAB *s;
-
- s = stab("arpa", ST_MAILER, ST_FIND);
- if (s != NULL && strcmp(e->e_from.q_mailer->m_name, "local") != 0 &&
- to->q_mailer == s->s_mailer)
- {
- usrerr("553 No ARPA mail through this machine: see your system administration");
- /* e->e_flags |= EF_NO_BODY_RETN; to suppress body on return */
- to->q_status = "5.7.1";
- return EX_UNAVAILABLE;
- }
-#endif /* EXAMPLE_CODE */
- return EX_OK;
-}
-
-#ifdef SUN_EXTENSIONS
-static void
-init_md_sun()
-{
- struct stat sbuf;
-
- /* Check for large file descriptor */
- if (fstat(fileno(stdin), &sbuf) < 0)
- {
- if (errno == EOVERFLOW)
- {
- perror("stdin");
- exit(EX_NOINPUT);
- }
- }
-}
-#endif /* SUN_EXTENSIONS */
-
-/*
-** INIT_MD -- do machine dependent initializations
-**
-** Systems that have global modes that should be set should do
-** them here rather than in main.
-*/
-
-#ifdef _AUX_SOURCE
-# include <compat.h>
-#endif /* _AUX_SOURCE */
-
-#if SHARE_V1
-# include <shares.h>
-#endif /* SHARE_V1 */
-
-void
-init_md(argc, argv)
- int argc;
- char **argv;
-{
-#ifdef _AUX_SOURCE
- setcompat(getcompat() | COMPAT_BSDPROT);
-#endif /* _AUX_SOURCE */
-
-#ifdef SUN_EXTENSIONS
- init_md_sun();
-#endif /* SUN_EXTENSIONS */
-
-#if _CONVEX_SOURCE
- /* keep gethostby*() from stripping the local domain name */
- set_domain_trim_off();
-#endif /* _CONVEX_SOURCE */
-#if defined(__QNX__) && !defined(__QNXNTO__)
- /*
- ** Due to QNX's network distributed nature, you can target a tcpip
- ** stack on a different node in the qnx network; this patch lets
- ** this feature work. The __sock_locate() must be done before the
- ** environment is clear.
- */
- __sock_locate();
-#endif /* __QNX__ */
-#if SECUREWARE || defined(_SCO_unix_)
- set_auth_parameters(argc, argv);
-
-# ifdef _SCO_unix_
- /*
- ** This is required for highest security levels (the kernel
- ** won't let it call set*uid() or run setuid binaries without
- ** it). It may be necessary on other SECUREWARE systems.
- */
-
- if (getluid() == -1)
- setluid(0);
-# endif /* _SCO_unix_ */
-#endif /* SECUREWARE || defined(_SCO_unix_) */
-
-
-#ifdef VENDOR_DEFAULT
- VendorCode = VENDOR_DEFAULT;
-#else /* VENDOR_DEFAULT */
- VendorCode = VENDOR_BERKELEY;
-#endif /* VENDOR_DEFAULT */
-}
-/*
-** INIT_VENDOR_MACROS -- vendor-dependent macro initializations
-**
-** Called once, on startup.
-**
-** Parameters:
-** e -- the global envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** vendor-dependent.
-*/
-
-void
-init_vendor_macros(e)
- register ENVELOPE *e;
-{
-}
-/*
-** GETLA -- get the current load average
-**
-** This code stolen from la.c.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** The current load average as an integer.
-**
-** Side Effects:
-** none.
-*/
-
-/* try to guess what style of load average we have */
-#define LA_ZERO 1 /* always return load average as zero */
-#define LA_INT 2 /* read kmem for avenrun; interpret as long */
-#define LA_FLOAT 3 /* read kmem for avenrun; interpret as float */
-#define LA_SUBR 4 /* call getloadavg */
-#define LA_MACH 5 /* MACH load averages (as on NeXT boxes) */
-#define LA_SHORT 6 /* read kmem for avenrun; interpret as short */
-#define LA_PROCSTR 7 /* read string ("1.17") from /proc/loadavg */
-#define LA_READKSYM 8 /* SVR4: use MIOC_READKSYM ioctl call */
-#define LA_DGUX 9 /* special DGUX implementation */
-#define LA_HPUX 10 /* special HPUX implementation */
-#define LA_IRIX6 11 /* special IRIX 6.2 implementation */
-#define LA_KSTAT 12 /* special Solaris kstat(3k) implementation */
-#define LA_DEVSHORT 13 /* read short from a device */
-#define LA_ALPHAOSF 14 /* Digital UNIX (OSF/1 on Alpha) table() call */
-#define LA_PSET 15 /* Solaris per-processor-set load average */
-#define LA_LONGLONG 17 /* read kmem for avenrun; interpret as long long */
-
-/* do guesses based on general OS type */
-#ifndef LA_TYPE
-# define LA_TYPE LA_ZERO
-#endif /* ! LA_TYPE */
-
-#ifndef FSHIFT
-# if defined(unixpc)
-# define FSHIFT 5
-# endif /* defined(unixpc) */
-
-# if defined(__alpha) || defined(IRIX)
-# define FSHIFT 10
-# endif /* defined(__alpha) || defined(IRIX) */
-
-#endif /* ! FSHIFT */
-
-#ifndef FSHIFT
-# define FSHIFT 8
-#endif /* ! FSHIFT */
-
-#ifndef FSCALE
-# define FSCALE (1 << FSHIFT)
-#endif /* ! FSCALE */
-
-#ifndef LA_AVENRUN
-# ifdef SYSTEM5
-# define LA_AVENRUN "avenrun"
-# else /* SYSTEM5 */
-# define LA_AVENRUN "_avenrun"
-# endif /* SYSTEM5 */
-#endif /* ! LA_AVENRUN */
-
-/* _PATH_KMEM should be defined in <paths.h> */
-#ifndef _PATH_KMEM
-# define _PATH_KMEM "/dev/kmem"
-#endif /* ! _PATH_KMEM */
-
-#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT) || (LA_TYPE == LA_LONGLONG)
-
-# include <nlist.h>
-
-/* _PATH_UNIX should be defined in <paths.h> */
-# ifndef _PATH_UNIX
-# if defined(SYSTEM5)
-# define _PATH_UNIX "/unix"
-# else /* defined(SYSTEM5) */
-# define _PATH_UNIX "/vmunix"
-# endif /* defined(SYSTEM5) */
-# endif /* ! _PATH_UNIX */
-
-# ifdef _AUX_SOURCE
-struct nlist Nl[2];
-# else /* _AUX_SOURCE */
-struct nlist Nl[] =
-{
- { LA_AVENRUN },
- { 0 },
-};
-# endif /* _AUX_SOURCE */
-# define X_AVENRUN 0
-
-int
-getla()
-{
- int j;
- static int kmem = -1;
-# if LA_TYPE == LA_INT
- long avenrun[3];
-# else /* LA_TYPE == LA_INT */
-# if LA_TYPE == LA_SHORT
- short avenrun[3];
-# else
-# if LA_TYPE == LA_LONGLONG
- long long avenrun[3];
-# else /* LA_TYPE == LA_LONGLONG */
- double avenrun[3];
-# endif /* LA_TYPE == LA_LONGLONG */
-# endif /* LA_TYPE == LA_SHORT */
-# endif /* LA_TYPE == LA_INT */
- extern off_t lseek();
-
- if (kmem < 0)
- {
-# ifdef _AUX_SOURCE
- (void) sm_strlcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN,
- sizeof(Nl[X_AVENRUN].n_name));
- Nl[1].n_name[0] = '\0';
-# endif /* _AUX_SOURCE */
-
-# if defined(_AIX3) || defined(_AIX4)
- if (knlist(Nl, 1, sizeof(Nl[0])) < 0)
-# else /* defined(_AIX3) || defined(_AIX4) */
- if (nlist(_PATH_UNIX, Nl) < 0)
-# endif /* defined(_AIX3) || defined(_AIX4) */
- {
- if (tTd(3, 1))
- sm_dprintf("getla: nlist(%s): %s\n", _PATH_UNIX,
- sm_errstring(errno));
- return -1;
- }
- if (Nl[X_AVENRUN].n_value == 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: nlist(%s, %s) ==> 0\n",
- _PATH_UNIX, LA_AVENRUN);
- return -1;
- }
-# ifdef NAMELISTMASK
- Nl[X_AVENRUN].n_value &= NAMELISTMASK;
-# endif /* NAMELISTMASK */
-
- kmem = open(_PATH_KMEM, 0, 0);
- if (kmem < 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: open(/dev/kmem): %s\n",
- sm_errstring(errno));
- return -1;
- }
- if ((j = fcntl(kmem, F_GETFD, 0)) < 0 ||
- fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: fcntl(/dev/kmem, FD_CLOEXEC): %s\n",
- sm_errstring(errno));
- (void) close(kmem);
- kmem = -1;
- return -1;
- }
- }
- if (tTd(3, 20))
- sm_dprintf("getla: symbol address = %#lx\n",
- (unsigned long) Nl[X_AVENRUN].n_value);
- if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 ||
- read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
- {
- /* thank you Ian */
- if (tTd(3, 1))
- sm_dprintf("getla: lseek or read: %s\n",
- sm_errstring(errno));
- return -1;
- }
-# if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) || (LA_TYPE == LA_LONGLONG)
- if (tTd(3, 5))
- {
-# if LA_TYPE == LA_SHORT
- sm_dprintf("getla: avenrun = %d", avenrun[0]);
- if (tTd(3, 15))
- sm_dprintf(", %d, %d", avenrun[1], avenrun[2]);
-# else /* LA_TYPE == LA_SHORT */
-# if LA_TYPE == LA_LONGLONG
- sm_dprintf("getla: avenrun = %lld", avenrun[0]);
- if (tTd(3, 15))
- sm_dprintf(", %lld, %lld", avenrun[1], avenrun[2]);
-# else /* LA_TYPE == LA_LONGLONG */
- sm_dprintf("getla: avenrun = %ld", avenrun[0]);
- if (tTd(3, 15))
- sm_dprintf(", %ld, %ld", avenrun[1], avenrun[2]);
-# endif /* LA_TYPE == LA_LONGLONG */
-# endif /* LA_TYPE == LA_SHORT */
- sm_dprintf("\n");
- }
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n",
- (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
- return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
-# else /* (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) || (LA_TYPE == LA_LONGLONG) */
- if (tTd(3, 5))
- {
- sm_dprintf("getla: avenrun = %g", avenrun[0]);
- if (tTd(3, 15))
- sm_dprintf(", %g, %g", avenrun[1], avenrun[2]);
- sm_dprintf("\n");
- }
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n", (int) (avenrun[0] +0.5));
- return ((int) (avenrun[0] + 0.5));
-# endif /* (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) || (LA_TYPE == LA_LONGLONG) */
-}
-
-#endif /* (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT) || (LA_TYPE == LA_LONGLONG) */
-
-#if LA_TYPE == LA_READKSYM
-
-# include <sys/ksym.h>
-
-int
-getla()
-{
- int j;
- static int kmem = -1;
- long avenrun[3];
- struct mioc_rksym mirk;
-
- if (kmem < 0)
- {
- kmem = open("/dev/kmem", 0, 0);
- if (kmem < 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: open(/dev/kmem): %s\n",
- sm_errstring(errno));
- return -1;
- }
- if ((j = fcntl(kmem, F_GETFD, 0)) < 0 ||
- fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: fcntl(/dev/kmem, FD_CLOEXEC): %s\n",
- sm_errstring(errno));
- (void) close(kmem);
- kmem = -1;
- return -1;
- }
- }
- mirk.mirk_symname = LA_AVENRUN;
- mirk.mirk_buf = avenrun;
- mirk.mirk_buflen = sizeof(avenrun);
- if (ioctl(kmem, MIOC_READKSYM, &mirk) < 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: ioctl(MIOC_READKSYM) failed: %s\n",
- sm_errstring(errno));
- return -1;
- }
- if (tTd(3, 5))
- {
- sm_dprintf("getla: avenrun = %d", avenrun[0]);
- if (tTd(3, 15))
- sm_dprintf(", %d, %d", avenrun[1], avenrun[2]);
- sm_dprintf("\n");
- }
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n",
- (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
- return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
-}
-
-#endif /* LA_TYPE == LA_READKSYM */
-
-#if LA_TYPE == LA_DGUX
-
-# include <sys/dg_sys_info.h>
-
-int
-getla()
-{
- struct dg_sys_info_load_info load_info;
-
- dg_sys_info((long *)&load_info,
- DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0);
-
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n", (int) (load_info.one_minute + 0.5));
-
- return ((int) (load_info.one_minute + 0.5));
-}
-
-#endif /* LA_TYPE == LA_DGUX */
-
-#if LA_TYPE == LA_HPUX
-
-/* forward declarations to keep gcc from complaining */
-struct pst_dynamic;
-struct pst_status;
-struct pst_static;
-struct pst_vminfo;
-struct pst_diskinfo;
-struct pst_processor;
-struct pst_lv;
-struct pst_swapinfo;
-
-# include <sys/param.h>
-# include <sys/pstat.h>
-
-int
-getla()
-{
- struct pst_dynamic pstd;
-
- if (pstat_getdynamic(&pstd, sizeof(struct pst_dynamic),
- (size_t) 1, 0) == -1)
- return 0;
-
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5));
-
- return (int) (pstd.psd_avg_1_min + 0.5);
-}
-
-#endif /* LA_TYPE == LA_HPUX */
-
-#if LA_TYPE == LA_SUBR
-
-int
-getla()
-{
- double avenrun[3];
-
- if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: getloadavg failed: %s",
- sm_errstring(errno));
- return -1;
- }
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n", (int) (avenrun[0] +0.5));
- return ((int) (avenrun[0] + 0.5));
-}
-
-#endif /* LA_TYPE == LA_SUBR */
-
-#if LA_TYPE == LA_MACH
-
-/*
-** This has been tested on NEXTSTEP release 2.1/3.X.
-*/
-
-# if defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0
-# include <mach/mach.h>
-# else /* defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0 */
-# include <mach.h>
-# endif /* defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0 */
-
-int
-getla()
-{
- processor_set_t default_set;
- kern_return_t error;
- unsigned int info_count;
- struct processor_set_basic_info info;
- host_t host;
-
- error = processor_set_default(host_self(), &default_set);
- if (error != KERN_SUCCESS)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: processor_set_default failed: %s",
- sm_errstring(errno));
- return -1;
- }
- info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
- if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO,
- &host, (processor_set_info_t)&info,
- &info_count) != KERN_SUCCESS)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: processor_set_info failed: %s",
- sm_errstring(errno));
- return -1;
- }
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n",
- (int) ((info.load_average + (LOAD_SCALE / 2)) /
- LOAD_SCALE));
- return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE;
-}
-
-#endif /* LA_TYPE == LA_MACH */
-
-#if LA_TYPE == LA_PROCSTR
-# if SM_CONF_BROKEN_STRTOD
- ERROR: This OS has most likely a broken strtod() implemenentation.
- ERROR: The function is required for getla().
- ERROR: Check the compilation options _LA_PROCSTR and
- ERROR: _SM_CONF_BROKEN_STRTOD (without the leading _).
-# endif /* SM_CONF_BROKEN_STRTOD */
-
-/*
-** Read /proc/loadavg for the load average. This is assumed to be
-** in a format like "0.15 0.12 0.06".
-**
-** Initially intended for Linux. This has been in the kernel
-** since at least 0.99.15.
-*/
-
-# ifndef _PATH_LOADAVG
-# define _PATH_LOADAVG "/proc/loadavg"
-# endif /* ! _PATH_LOADAVG */
-
-int
-getla()
-{
- double avenrun;
- register int result;
- SM_FILE_T *fp;
-
- fp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, _PATH_LOADAVG, SM_IO_RDONLY,
- NULL);
- if (fp == NULL)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: sm_io_open(%s): %s\n",
- _PATH_LOADAVG, sm_errstring(errno));
- return -1;
- }
- result = sm_io_fscanf(fp, SM_TIME_DEFAULT, "%lf", &avenrun);
- (void) sm_io_close(fp, SM_TIME_DEFAULT);
- if (result != 1)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: sm_io_fscanf() = %d: %s\n",
- result, sm_errstring(errno));
- return -1;
- }
-
- if (tTd(3, 1))
- sm_dprintf("getla(): %.2f\n", avenrun);
-
- return ((int) (avenrun + 0.5));
-}
-
-#endif /* LA_TYPE == LA_PROCSTR */
-
-#if LA_TYPE == LA_IRIX6
-
-# include <sys/sysmp.h>
-
-# ifdef _UNICOSMP
-# define CAST_SYSMP(x) (x)
-# else /* _UNICOSMP */
-# define CAST_SYSMP(x) ((x) & 0x7fffffff)
-# endif /* _UNICOSMP */
-
-int
-getla(void)
-{
- int j;
- static int kmem = -1;
- int avenrun[3];
-
- if (kmem < 0)
- {
- kmem = open(_PATH_KMEM, 0, 0);
- if (kmem < 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: open(%s): %s\n", _PATH_KMEM,
- sm_errstring(errno));
- return -1;
- }
- if ((j = fcntl(kmem, F_GETFD, 0)) < 0 ||
- fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: fcntl(/dev/kmem, FD_CLOEXEC): %s\n",
- sm_errstring(errno));
- (void) close(kmem);
- kmem = -1;
- return -1;
- }
- }
-
- if (lseek(kmem, CAST_SYSMP(sysmp(MP_KERNADDR, MPKA_AVENRUN)), SEEK_SET)
- == -1 ||
- read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
- {
- if (tTd(3, 1))
- sm_dprintf("getla: lseek or read: %s\n",
- sm_errstring(errno));
- return -1;
- }
- if (tTd(3, 5))
- {
- sm_dprintf("getla: avenrun = %ld", (long int) avenrun[0]);
- if (tTd(3, 15))
- sm_dprintf(", %ld, %ld",
- (long int) avenrun[1], (long int) avenrun[2]);
- sm_dprintf("\n");
- }
-
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n",
- (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
- return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
-
-}
-#endif /* LA_TYPE == LA_IRIX6 */
-
-#if LA_TYPE == LA_KSTAT
-
-# include <kstat.h>
-
-int
-getla()
-{
- static kstat_ctl_t *kc = NULL;
- static kstat_t *ksp = NULL;
- kstat_named_t *ksn;
- int la;
-
- if (kc == NULL) /* if not initialized before */
- kc = kstat_open();
- if (kc == NULL)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: kstat_open(): %s\n",
- sm_errstring(errno));
- return -1;
- }
- if (ksp == NULL)
- ksp = kstat_lookup(kc, "unix", 0, "system_misc");
- if (ksp == NULL)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: kstat_lookup(): %s\n",
- sm_errstring(errno));
- return -1;
- }
- if (kstat_read(kc, ksp, NULL) < 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: kstat_read(): %s\n",
- sm_errstring(errno));
- return -1;
- }
- ksn = (kstat_named_t *) kstat_data_lookup(ksp, "avenrun_1min");
- la = ((double) ksn->value.ul + FSCALE/2) / FSCALE;
- /* kstat_close(kc); /o do not close for fast access */
- return la;
-}
-
-#endif /* LA_TYPE == LA_KSTAT */
-
-#if LA_TYPE == LA_DEVSHORT
-
-/*
-** Read /dev/table/avenrun for the load average. This should contain
-** three shorts for the 1, 5, and 15 minute loads. We only read the
-** first, since that's all we care about.
-**
-** Intended for SCO OpenServer 5.
-*/
-
-# ifndef _PATH_AVENRUN
-# define _PATH_AVENRUN "/dev/table/avenrun"
-# endif /* ! _PATH_AVENRUN */
-
-int
-getla()
-{
- static int afd = -1;
- short avenrun;
- int loadav;
- int r;
-
- errno = EBADF;
-
- if (afd == -1 || lseek(afd, 0L, SEEK_SET) == -1)
- {
- if (errno != EBADF)
- return -1;
- afd = open(_PATH_AVENRUN, O_RDONLY|O_SYNC);
- if (afd < 0)
- {
- sm_syslog(LOG_ERR, NOQID,
- "can't open %s: %s",
- _PATH_AVENRUN, sm_errstring(errno));
- return -1;
- }
- }
-
- r = read(afd, &avenrun, sizeof(avenrun));
-
- if (tTd(3, 5))
- sm_dprintf("getla: avenrun = %d\n", avenrun);
- loadav = (int) (avenrun + FSCALE/2) >> FSHIFT;
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n", loadav);
- return loadav;
-}
-
-#endif /* LA_TYPE == LA_DEVSHORT */
-
-#if LA_TYPE == LA_ALPHAOSF
-struct rtentry;
-struct mbuf;
-# include <sys/table.h>
-
-int
-getla()
-{
- int ave = 0;
- struct tbl_loadavg tab;
-
- if (table(TBL_LOADAVG, 0, &tab, 1, sizeof(tab)) == -1)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: table %s\n", sm_errstring(errno));
- return -1;
- }
-
- if (tTd(3, 1))
- sm_dprintf("getla: scale = %d\n", tab.tl_lscale);
-
- if (tab.tl_lscale)
- ave = ((tab.tl_avenrun.l[2] + (tab.tl_lscale/2)) /
- tab.tl_lscale);
- else
- ave = (int) (tab.tl_avenrun.d[2] + 0.5);
-
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n", ave);
-
- return ave;
-}
-
-#endif /* LA_TYPE == LA_ALPHAOSF */
-
-#if LA_TYPE == LA_PSET
-
-int
-getla()
-{
- double avenrun[3];
-
- if (pset_getloadavg(PS_MYID, avenrun,
- sizeof(avenrun) / sizeof(avenrun[0])) < 0)
- {
- if (tTd(3, 1))
- sm_dprintf("getla: pset_getloadavg failed: %s",
- sm_errstring(errno));
- return -1;
- }
- if (tTd(3, 1))
- sm_dprintf("getla: %d\n", (int) (avenrun[0] +0.5));
- return ((int) (avenrun[0] + 0.5));
-}
-
-#endif /* LA_TYPE == LA_PSET */
-
-#if LA_TYPE == LA_ZERO
-
-int
-getla()
-{
- if (tTd(3, 1))
- sm_dprintf("getla: ZERO\n");
- return 0;
-}
-
-#endif /* LA_TYPE == LA_ZERO */
-
-/*
- * Copyright 1989 Massachusetts Institute of Technology
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation, and that the name of M.I.T. not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission. M.I.T. makes no representations about the
- * suitability of this software for any purpose. It is provided "as is"
- * without express or implied warranty.
- *
- * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
- * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors: Many and varied...
- */
-
-/* Non Apollo stuff removed by Don Lewis 11/15/93 */
-#ifndef lint
-SM_UNUSED(static char rcsid[]) = "@(#)$OrigId: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $";
-#endif /* ! lint */
-
-#ifdef apollo
-# undef volatile
-# include <apollo/base.h>
-
-/* ARGSUSED */
-int getloadavg( call_data )
- caddr_t call_data; /* pointer to (double) return value */
-{
- double *avenrun = (double *) call_data;
- int i;
- status_$t st;
- long loadav[3];
-
- proc1_$get_loadav(loadav, &st);
- *avenrun = loadav[0] / (double) (1 << 16);
- return 0;
-}
-#endif /* apollo */
-/*
-** SM_GETLA -- get the current load average
-**
-** Parameters:
-** none
-**
-** Returns:
-** none
-**
-** Side Effects:
-** Set CurrentLA to the current load average.
-** Set {load_avg} in GlobalMacros to the current load average.
-*/
-
-void
-sm_getla()
-{
- char labuf[8];
-
- CurrentLA = getla();
- (void) sm_snprintf(labuf, sizeof(labuf), "%d", CurrentLA);
- macdefine(&GlobalMacros, A_TEMP, macid("{load_avg}"), labuf);
-}
-/*
-** SHOULDQUEUE -- should this message be queued or sent?
-**
-** Compares the message cost to the load average to decide.
-**
-** Note: Do NOT change this API! It is documented in op.me
-** and theoretically the user can change this function...
-**
-** Parameters:
-** pri -- the priority of the message in question.
-** ct -- the message creation time (unused, but see above).
-**
-** Returns:
-** true -- if this message should be queued up for the
-** time being.
-** false -- if the load is low enough to send this message.
-**
-** Side Effects:
-** none.
-*/
-
-/* ARGSUSED1 */
-bool
-shouldqueue(pri, ct)
- long pri;
- time_t ct;
-{
- bool rval;
-#if _FFR_MEMSTAT
- long memfree;
-#endif /* _FFR_MEMSTAT */
-
- if (tTd(3, 30))
- sm_dprintf("shouldqueue: CurrentLA=%d, pri=%ld: ",
- CurrentLA, pri);
-
-#if _FFR_MEMSTAT
- if (QueueLowMem > 0 &&
- sm_memstat_get(MemoryResource, &memfree) >= 0 &&
- memfree < QueueLowMem)
- {
- if (tTd(3, 30))
- sm_dprintf("true (memfree=%ld < QueueLowMem=%ld)\n",
- memfree, QueueLowMem);
- return true;
- }
-#endif /* _FFR_MEMSTAT */
- if (CurrentLA < QueueLA)
- {
- if (tTd(3, 30))
- sm_dprintf("false (CurrentLA < QueueLA)\n");
- return false;
- }
- rval = pri > (QueueFactor / (CurrentLA - QueueLA + 1));
- if (tTd(3, 30))
- sm_dprintf("%s (by calculation)\n", rval ? "true" : "false");
- return rval;
-}
-
-/*
-** REFUSECONNECTIONS -- decide if connections should be refused
-**
-** Parameters:
-** e -- the current envelope.
-** dn -- number of daemon.
-** active -- was this daemon actually active?
-**
-** Returns:
-** true if incoming SMTP connections should be refused
-** (for now).
-** false if we should accept new work.
-**
-** Side Effects:
-** Sets process title when it is rejecting connections.
-*/
-
-bool
-refuseconnections(e, dn, active)
- ENVELOPE *e;
- int dn;
- bool active;
-{
- static time_t lastconn[MAXDAEMONS];
- static int conncnt[MAXDAEMONS];
- static time_t firstrejtime[MAXDAEMONS];
- static time_t nextlogtime[MAXDAEMONS];
- int limit;
-#if _FFR_MEMSTAT
- long memfree;
-#endif /* _FFR_MEMSTAT */
-
-#if XLA
- if (!xla_smtp_ok())
- return true;
-#endif /* XLA */
-
- SM_ASSERT(dn >= 0);
- SM_ASSERT(dn < MAXDAEMONS);
- if (ConnRateThrottle > 0)
- {
- time_t now;
-
- now = curtime();
- if (active)
- {
- if (now != lastconn[dn])
- {
- lastconn[dn] = now;
- conncnt[dn] = 1;
- }
- else if (conncnt[dn]++ > ConnRateThrottle)
- {
-#define D_MSG_CRT "deferring connections on daemon %s: %d per second"
- /* sleep to flatten out connection load */
- sm_setproctitle(true, e, D_MSG_CRT,
- Daemons[dn].d_name,
- ConnRateThrottle);
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID, D_MSG_CRT,
- Daemons[dn].d_name,
- ConnRateThrottle);
- (void) sleep(1);
- }
- }
- else if (now != lastconn[dn])
- conncnt[dn] = 0;
- }
-
-
-#if _FFR_MEMSTAT
- if (RefuseLowMem > 0 &&
- sm_memstat_get(MemoryResource, &memfree) >= 0 &&
- memfree < RefuseLowMem)
- {
-# define R_MSG_LM "rejecting connections on daemon %s: free memory: %ld"
- sm_setproctitle(true, e, R_MSG_LM, Daemons[dn].d_name, memfree);
- if (LogLevel > 8)
- sm_syslog(LOG_NOTICE, NOQID, R_MSG_LM,
- Daemons[dn].d_name, memfree);
- return true;
- }
-#endif /* _FFR_MEMSTAT */
- sm_getla();
- limit = (Daemons[dn].d_refuseLA != DPO_NOTSET) ?
- Daemons[dn].d_refuseLA : RefuseLA;
- if (limit > 0 && CurrentLA >= limit)
- {
- time_t now;
-
-# define R_MSG_LA "rejecting connections on daemon %s: load average: %d"
-# define R2_MSG_LA "have been rejecting connections on daemon %s for %s"
- sm_setproctitle(true, e, R_MSG_LA, Daemons[dn].d_name,
- CurrentLA);
- if (LogLevel > 8)
- sm_syslog(LOG_NOTICE, NOQID, R_MSG_LA,
- Daemons[dn].d_name, CurrentLA);
- now = curtime();
- if (firstrejtime[dn] == 0)
- {
- firstrejtime[dn] = now;
- nextlogtime[dn] = now + RejectLogInterval;
- }
- else if (nextlogtime[dn] < now)
- {
- sm_syslog(LOG_ERR, NOQID, R2_MSG_LA, Daemons[dn].d_name,
- pintvl(now - firstrejtime[dn], true));
- nextlogtime[dn] = now + RejectLogInterval;
- }
- return true;
- }
- else
- firstrejtime[dn] = 0;
-
- limit = (Daemons[dn].d_delayLA != DPO_NOTSET) ?
- Daemons[dn].d_delayLA : DelayLA;
- if (limit > 0 && CurrentLA >= limit)
- {
- time_t now;
- static time_t log_delay = (time_t) 0;
-
-# define MIN_DELAY_LOG 90 /* wait before logging this again */
-# define D_MSG_LA "delaying connections on daemon %s: load average=%d >= %d"
- /* sleep to flatten out connection load */
- sm_setproctitle(true, e, D_MSG_LA, Daemons[dn].d_name, limit);
- if (LogLevel > 8 && (now = curtime()) > log_delay)
- {
- sm_syslog(LOG_INFO, NOQID, D_MSG_LA,
- Daemons[dn].d_name, CurrentLA, limit);
- log_delay = now + MIN_DELAY_LOG;
- }
- (void) sleep(1);
- }
-
- limit = (Daemons[dn].d_maxchildren != DPO_NOTSET) ?
- Daemons[dn].d_maxchildren : MaxChildren;
- if (limit > 0 && CurChildren >= limit)
- {
- proc_list_probe();
- if (CurChildren >= limit)
- {
-#define R_MSG_CHILD "rejecting connections on daemon %s: %d children, max %d"
- sm_setproctitle(true, e, R_MSG_CHILD,
- Daemons[dn].d_name, CurChildren,
- limit);
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID, R_MSG_CHILD,
- Daemons[dn].d_name, CurChildren,
- limit);
- return true;
- }
- }
- return false;
-}
-
-/*
-** SETPROCTITLE -- set process title for ps
-**
-** Parameters:
-** fmt -- a printf style format string.
-** a, b, c -- possible parameters to fmt.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Clobbers argv of our main procedure so ps(1) will
-** display the title.
-*/
-
-#define SPT_NONE 0 /* don't use it at all */
-#define SPT_REUSEARGV 1 /* cover argv with title information */
-#define SPT_BUILTIN 2 /* use libc builtin */
-#define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */
-#define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */
-#define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */
-#define SPT_SCO 6 /* write kernel u. area */
-#define SPT_CHANGEARGV 7 /* write our own strings into argv[] */
-
-#ifndef SPT_TYPE
-# define SPT_TYPE SPT_REUSEARGV
-#endif /* ! SPT_TYPE */
-
-
-#if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
-
-# if SPT_TYPE == SPT_PSTAT
-# include <sys/pstat.h>
-# endif /* SPT_TYPE == SPT_PSTAT */
-# if SPT_TYPE == SPT_PSSTRINGS
-# include <machine/vmparam.h>
-# include <sys/exec.h>
-# ifndef PS_STRINGS /* hmmmm.... apparently not available after all */
-# undef SPT_TYPE
-# define SPT_TYPE SPT_REUSEARGV
-# else /* ! PS_STRINGS */
-# ifndef NKPDE /* FreeBSD 2.0 */
-# define NKPDE 63
-typedef unsigned int *pt_entry_t;
-# endif /* ! NKPDE */
-# endif /* ! PS_STRINGS */
-# endif /* SPT_TYPE == SPT_PSSTRINGS */
-
-# if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV
-# define SETPROC_STATIC static
-# else /* SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV */
-# define SETPROC_STATIC
-# endif /* SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV */
-
-# if SPT_TYPE == SPT_SYSMIPS
-# include <sys/sysmips.h>
-# include <sys/sysnews.h>
-# endif /* SPT_TYPE == SPT_SYSMIPS */
-
-# if SPT_TYPE == SPT_SCO
-# include <sys/immu.h>
-# include <sys/dir.h>
-# include <sys/user.h>
-# include <sys/fs/s5param.h>
-# if PSARGSZ > MAXLINE
-# define SPT_BUFSIZE PSARGSZ
-# endif /* PSARGSZ > MAXLINE */
-# endif /* SPT_TYPE == SPT_SCO */
-
-# ifndef SPT_PADCHAR
-# define SPT_PADCHAR ' '
-# endif /* ! SPT_PADCHAR */
-
-#endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
-
-#ifndef SPT_BUFSIZE
-# define SPT_BUFSIZE MAXLINE
-#endif /* ! SPT_BUFSIZE */
-
-#if _FFR_SPT_ALIGN
-
-/*
-** It looks like the Compaq Tru64 5.1A now aligns argv and envp to
-** 64 bit alignment, so unless each piece of argv and envp is a multiple
-** of 8 bytes (including terminating NULL), initsetproctitle() won't use
-** any of the space beyond argv[0]. Be sure to set SPT_ALIGN_SIZE if
-** you use this FFR.
-*/
-
-# ifdef SPT_ALIGN_SIZE
-# define SPT_ALIGN(x, align) (((((x) + SPT_ALIGN_SIZE) >> (align)) << (align)) - 1)
-# else /* SPT_ALIGN_SIZE */
-# define SPT_ALIGN(x, align) (x)
-# endif /* SPT_ALIGN_SIZE */
-#else /* _FFR_SPT_ALIGN */
-# define SPT_ALIGN(x, align) (x)
-#endif /* _FFR_SPT_ALIGN */
-
-/*
-** Pointers for setproctitle.
-** This allows "ps" listings to give more useful information.
-*/
-
-static char **Argv = NULL; /* pointer to argument vector */
-static char *LastArgv = NULL; /* end of argv */
-#if SPT_TYPE != SPT_BUILTIN
-static void setproctitle __P((const char *, ...));
-#endif /* SPT_TYPE != SPT_BUILTIN */
-
-void
-initsetproctitle(argc, argv, envp)
- int argc;
- char **argv;
- char **envp;
-{
- register int i;
- int align;
- extern char **environ;
-
- /*
- ** Move the environment so setproctitle can use the space at
- ** the top of memory.
- */
-
- if (envp != NULL)
- {
- for (i = 0; envp[i] != NULL; i++)
- continue;
- environ = (char **) xalloc(sizeof(char *) * (i + 1));
- for (i = 0; envp[i] != NULL; i++)
- environ[i] = newstr(envp[i]);
- environ[i] = NULL;
- }
-
- /*
- ** Save start and extent of argv for setproctitle.
- */
-
- Argv = argv;
-
- /*
- ** Determine how much space we can use for setproctitle.
- ** Use all contiguous argv and envp pointers starting at argv[0]
- */
-
- align = -1;
-# if _FFR_SPT_ALIGN
-# ifdef SPT_ALIGN_SIZE
- for (i = SPT_ALIGN_SIZE; i > 0; i >>= 1)
- align++;
-# endif /* SPT_ALIGN_SIZE */
-# endif /* _FFR_SPT_ALIGN */
-
- for (i = 0; i < argc; i++)
- {
- if (i == 0 || LastArgv + 1 == argv[i])
- LastArgv = argv[i] + SPT_ALIGN(strlen(argv[i]), align);
- }
- for (i = 0; LastArgv != NULL && envp != NULL && envp[i] != NULL; i++)
- {
- if (LastArgv + 1 == envp[i])
- LastArgv = envp[i] + SPT_ALIGN(strlen(envp[i]), align);
- }
-}
-
-#if SPT_TYPE != SPT_BUILTIN
-
-/*VARARGS1*/
-static void
-# ifdef __STDC__
-setproctitle(const char *fmt, ...)
-# else /* __STDC__ */
-setproctitle(fmt, va_alist)
- const char *fmt;
- va_dcl
-# endif /* __STDC__ */
-{
-# if SPT_TYPE != SPT_NONE
- register int i;
- register char *p;
- SETPROC_STATIC char buf[SPT_BUFSIZE];
- SM_VA_LOCAL_DECL
-# if SPT_TYPE == SPT_PSTAT
- union pstun pst;
-# endif /* SPT_TYPE == SPT_PSTAT */
-# if SPT_TYPE == SPT_SCO
- int j;
- off_t seek_off;
- static int kmem = -1;
- static pid_t kmempid = -1;
- struct user u;
-# endif /* SPT_TYPE == SPT_SCO */
-
- p = buf;
-
- /* print sendmail: heading for grep */
- (void) sm_strlcpy(p, "sendmail: ", SPACELEFT(buf, p));
- p += strlen(p);
-
- /* print the argument string */
- SM_VA_START(ap, fmt);
- (void) sm_vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
- SM_VA_END(ap);
-
- i = (int) strlen(buf);
- if (i < 0)
- return;
-
-# if SPT_TYPE == SPT_PSTAT
- pst.pst_command = buf;
- pstat(PSTAT_SETCMD, pst, i, 0, 0);
-# endif /* SPT_TYPE == SPT_PSTAT */
-# if SPT_TYPE == SPT_PSSTRINGS
- PS_STRINGS->ps_nargvstr = 1;
- PS_STRINGS->ps_argvstr = buf;
-# endif /* SPT_TYPE == SPT_PSSTRINGS */
-# if SPT_TYPE == SPT_SYSMIPS
- sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
-# endif /* SPT_TYPE == SPT_SYSMIPS */
-# if SPT_TYPE == SPT_SCO
- if (kmem < 0 || kmempid != CurrentPid)
- {
- if (kmem >= 0)
- (void) close(kmem);
- kmem = open(_PATH_KMEM, O_RDWR, 0);
- if (kmem < 0)
- return;
- if ((j = fcntl(kmem, F_GETFD, 0)) < 0 ||
- fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0)
- {
- (void) close(kmem);
- kmem = -1;
- return;
- }
- kmempid = CurrentPid;
- }
- buf[PSARGSZ - 1] = '\0';
- seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u;
- if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off)
- (void) write(kmem, buf, PSARGSZ);
-# endif /* SPT_TYPE == SPT_SCO */
-# if SPT_TYPE == SPT_REUSEARGV
- if (LastArgv == NULL)
- return;
-
- if (i > LastArgv - Argv[0] - 2)
- {
- i = LastArgv - Argv[0] - 2;
- buf[i] = '\0';
- }
- (void) sm_strlcpy(Argv[0], buf, i + 1);
- p = &Argv[0][i];
- while (p < LastArgv)
- *p++ = SPT_PADCHAR;
- Argv[1] = NULL;
-# endif /* SPT_TYPE == SPT_REUSEARGV */
-# if SPT_TYPE == SPT_CHANGEARGV
- Argv[0] = buf;
- Argv[1] = 0;
-# endif /* SPT_TYPE == SPT_CHANGEARGV */
-# endif /* SPT_TYPE != SPT_NONE */
-}
-
-#endif /* SPT_TYPE != SPT_BUILTIN */
-/*
-** SM_SETPROCTITLE -- set process task and set process title for ps
-**
-** Possibly set process status and call setproctitle() to
-** change the ps display.
-**
-** Parameters:
-** status -- whether or not to store as process status
-** e -- the current envelope.
-** fmt -- a printf style format string.
-** a, b, c -- possible parameters to fmt.
-**
-** Returns:
-** none.
-*/
-
-/*VARARGS2*/
-void
-#ifdef __STDC__
-sm_setproctitle(bool status, ENVELOPE *e, const char *fmt, ...)
-#else /* __STDC__ */
-sm_setproctitle(status, e, fmt, va_alist)
- bool status;
- ENVELOPE *e;
- const char *fmt;
- va_dcl
-#endif /* __STDC__ */
-{
- char buf[SPT_BUFSIZE];
- SM_VA_LOCAL_DECL
-
- /* print the argument string */
- SM_VA_START(ap, fmt);
- (void) sm_vsnprintf(buf, sizeof(buf), fmt, ap);
- SM_VA_END(ap);
-
- if (status)
- proc_list_set(CurrentPid, buf);
-
- if (ProcTitlePrefix != NULL)
- {
- char prefix[SPT_BUFSIZE];
-
- expand(ProcTitlePrefix, prefix, sizeof(prefix), e);
- setproctitle("%s: %s", prefix, buf);
- }
- else
- setproctitle("%s", buf);
-}
-/*
-** WAITFOR -- wait for a particular process id.
-**
-** Parameters:
-** pid -- process id to wait for.
-**
-** Returns:
-** status of pid.
-** -1 if pid never shows up.
-**
-** Side Effects:
-** none.
-*/
-
-int
-waitfor(pid)
- pid_t pid;
-{
- int st;
- pid_t i;
-
- do
- {
- errno = 0;
- i = sm_wait(&st);
- if (i > 0)
- proc_list_drop(i, st, NULL);
- } while ((i >= 0 || errno == EINTR) && i != pid);
- if (i < 0)
- return -1;
- return st;
-}
-/*
-** SM_WAIT -- wait
-**
-** Parameters:
-** status -- pointer to status (return value)
-**
-** Returns:
-** pid
-*/
-
-pid_t
-sm_wait(status)
- int *status;
-{
-# ifdef WAITUNION
- union wait st;
-# else /* WAITUNION */
- auto int st;
-# endif /* WAITUNION */
- pid_t i;
-# if defined(ISC_UNIX) || defined(_SCO_unix_)
- int savesig;
-# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
-
-# if defined(ISC_UNIX) || defined(_SCO_unix_)
- savesig = sm_releasesignal(SIGCHLD);
-# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
- i = wait(&st);
-# if defined(ISC_UNIX) || defined(_SCO_unix_)
- if (savesig > 0)
- sm_blocksignal(SIGCHLD);
-# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
-# ifdef WAITUNION
- *status = st.w_status;
-# else /* WAITUNION */
- *status = st;
-# endif /* WAITUNION */
- return i;
-}
-/*
-** REAPCHILD -- pick up the body of my child, lest it become a zombie
-**
-** Parameters:
-** sig -- the signal that got us here (unused).
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Picks up extant zombies.
-** Control socket exits may restart/shutdown daemon.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-/* ARGSUSED0 */
-SIGFUNC_DECL
-reapchild(sig)
- int sig;
-{
- int save_errno = errno;
- int st;
- pid_t pid;
-# if HASWAITPID
- auto int status;
- int count;
-
- count = 0;
- while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
- {
- st = status;
- if (count++ > 1000)
- break;
-# else /* HASWAITPID */
-# ifdef WNOHANG
- union wait status;
-
- while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0)
- {
- st = status.w_status;
-# else /* WNOHANG */
- auto int status;
-
- /*
- ** Catch one zombie -- we will be re-invoked (we hope) if there
- ** are more. Unreliable signals probably break this, but this
- ** is the "old system" situation -- waitpid or wait3 are to be
- ** strongly preferred.
- */
-
- if ((pid = wait(&status)) > 0)
- {
- st = status;
-# endif /* WNOHANG */
-# endif /* HASWAITPID */
- /* Drop PID and check if it was a control socket child */
- proc_list_drop(pid, st, NULL);
- }
- FIX_SYSV_SIGNAL(sig, reapchild);
- errno = save_errno;
- return SIGFUNC_RETURN;
-}
-/*
-** GETDTABLESIZE -- return number of file descriptors
-**
-** Only on non-BSD systems
-**
-** Parameters:
-** none
-**
-** Returns:
-** size of file descriptor table
-**
-** Side Effects:
-** none
-*/
-
-#ifdef SOLARIS
-# include <sys/resource.h>
-#endif /* SOLARIS */
-
-int
-getdtsize()
-{
-# ifdef RLIMIT_NOFILE
- struct rlimit rl;
-
- if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
- return rl.rlim_cur;
-# endif /* RLIMIT_NOFILE */
-
-# if HASGETDTABLESIZE
- return getdtablesize();
-# else /* HASGETDTABLESIZE */
-# ifdef _SC_OPEN_MAX
- return sysconf(_SC_OPEN_MAX);
-# else /* _SC_OPEN_MAX */
- return NOFILE;
-# endif /* _SC_OPEN_MAX */
-# endif /* HASGETDTABLESIZE */
-}
-/*
-** UNAME -- get the UUCP name of this system.
-*/
-
-#if !HASUNAME
-
-int
-uname(name)
- struct utsname *name;
-{
- SM_FILE_T *file;
- char *n;
-
- name->nodename[0] = '\0';
-
- /* try /etc/whoami -- one line with the node name */
- if ((file = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, "/etc/whoami",
- SM_IO_RDONLY, NULL)) != NULL)
- {
- (void) sm_io_fgets(file, SM_TIME_DEFAULT, name->nodename,
- NODE_LENGTH + 1);
- (void) sm_io_close(file, SM_TIME_DEFAULT);
- n = strchr(name->nodename, '\n');
- if (n != NULL)
- *n = '\0';
- if (name->nodename[0] != '\0')
- return 0;
- }
-
- /* try /usr/include/whoami.h -- has a #define somewhere */
- if ((file = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
- "/usr/include/whoami.h", SM_IO_RDONLY, NULL))
- != NULL)
- {
- char buf[MAXLINE];
-
- while (sm_io_fgets(file, SM_TIME_DEFAULT,
- buf, sizeof(buf)) != NULL)
- {
- if (sm_io_sscanf(buf, "#define sysname \"%*[^\"]\"",
- NODE_LENGTH, name->nodename) > 0)
- break;
- }
- (void) sm_io_close(file, SM_TIME_DEFAULT);
- if (name->nodename[0] != '\0')
- return 0;
- }
-
- return -1;
-}
-#endif /* !HASUNAME */
-/*
-** INITGROUPS -- initialize groups
-**
-** Stub implementation for System V style systems
-*/
-
-#if !HASINITGROUPS
-
-initgroups(name, basegid)
- char *name;
- int basegid;
-{
- return 0;
-}
-
-#endif /* !HASINITGROUPS */
-/*
-** SETGROUPS -- set group list
-**
-** Stub implementation for systems that don't have group lists
-*/
-
-#ifndef NGROUPS_MAX
-
-int
-setgroups(ngroups, grouplist)
- int ngroups;
- GIDSET_T grouplist[];
-{
- return 0;
-}
-
-#endif /* ! NGROUPS_MAX */
-/*
-** SETSID -- set session id (for non-POSIX systems)
-*/
-
-#if !HASSETSID
-
-pid_t
-setsid __P ((void))
-{
-# ifdef TIOCNOTTY
- int fd;
-
- fd = open("/dev/tty", O_RDWR, 0);
- if (fd >= 0)
- {
- (void) ioctl(fd, TIOCNOTTY, (char *) 0);
- (void) close(fd);
- }
-# endif /* TIOCNOTTY */
-# ifdef SYS5SETPGRP
- return setpgrp();
-# else /* SYS5SETPGRP */
- return setpgid(0, CurrentPid);
-# endif /* SYS5SETPGRP */
-}
-
-#endif /* !HASSETSID */
-/*
-** FSYNC -- dummy fsync
-*/
-
-#if NEEDFSYNC
-
-fsync(fd)
- int fd;
-{
-# ifdef O_SYNC
- return fcntl(fd, F_SETFL, O_SYNC);
-# else /* O_SYNC */
- /* nothing we can do */
- return 0;
-# endif /* O_SYNC */
-}
-
-#endif /* NEEDFSYNC */
-/*
-** DGUX_INET_ADDR -- inet_addr for DG/UX
-**
-** Data General DG/UX version of inet_addr returns a struct in_addr
-** instead of a long. This patches things. Only needed on versions
-** prior to 5.4.3.
-*/
-
-#ifdef DGUX_5_4_2
-
-# undef inet_addr
-
-long
-dgux_inet_addr(host)
- char *host;
-{
- struct in_addr haddr;
-
- haddr = inet_addr(host);
- return haddr.s_addr;
-}
-
-#endif /* DGUX_5_4_2 */
-/*
-** GETOPT -- for old systems or systems with bogus implementations
-*/
-
-#if !SM_CONF_GETOPT
-
-/*
- * Copyright (c) 1985 Regents of the University of California.
- * All rights reserved. The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
- */
-
-
-/*
-** this version hacked to add `atend' flag to allow state machine
-** to reset if invoked by the program to scan args for a 2nd time
-*/
-
-# if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)getopt.c 4.3 (Berkeley) 3/9/86";
-# endif /* defined(LIBC_SCCS) && !defined(lint) */
-
-/*
-** get option letter from argument vector
-*/
-# ifdef _CONVEX_SOURCE
-extern int optind, opterr, optopt;
-extern char *optarg;
-# else /* _CONVEX_SOURCE */
-int opterr = 1; /* if error message should be printed */
-int optind = 1; /* index into parent argv vector */
-int optopt = 0; /* character checked for validity */
-char *optarg = NULL; /* argument associated with option */
-# endif /* _CONVEX_SOURCE */
-
-# define BADCH (int)'?'
-# define EMSG ""
-# define tell(s) if (opterr) \
- {sm_io_fputs(smioerr, SM_TIME_DEFAULT, *nargv); \
- (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, s); \
- (void) sm_io_putc(smioerr, SM_TIME_DEFAULT, optopt); \
- (void) sm_io_putc(smioerr, SM_TIME_DEFAULT, '\n'); \
- return BADCH;}
-
-int
-getopt(nargc,nargv,ostr)
- int nargc;
- char *const *nargv;
- const char *ostr;
-{
- static char *place = EMSG; /* option letter processing */
- static char atend = 0;
- register char *oli = NULL; /* option letter list index */
-
- if (atend) {
- atend = 0;
- place = EMSG;
- }
- if(!*place) { /* update scanning pointer */
- if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) {
- atend++;
- return -1;
- }
- if (*place == '-') { /* found "--" */
- ++optind;
- atend++;
- return -1;
- }
- } /* option letter okay? */
- if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) {
- if (!*place) ++optind;
- tell(": illegal option -- ");
- }
- if (oli && *++oli != ':') { /* don't need argument */
- optarg = NULL;
- if (!*place) ++optind;
- }
- else { /* need an argument */
- if (*place) optarg = place; /* no white space */
- else if (nargc <= ++optind) { /* no arg */
- place = EMSG;
- tell(": option requires an argument -- ");
- }
- else optarg = nargv[optind]; /* white space */
- place = EMSG;
- ++optind;
- }
- return optopt; /* dump back option letter */
-}
-
-#endif /* !SM_CONF_GETOPT */
-/*
-** USERSHELLOK -- tell if a user's shell is ok for unrestricted use
-**
-** Parameters:
-** user -- the name of the user we are checking.
-** shell -- the user's shell from /etc/passwd
-**
-** Returns:
-** true -- if it is ok to use this for unrestricted access.
-** false -- if the shell is restricted.
-*/
-
-#if !HASGETUSERSHELL
-
-# ifndef _PATH_SHELLS
-# define _PATH_SHELLS "/etc/shells"
-# endif /* ! _PATH_SHELLS */
-
-# if defined(_AIX3) || defined(_AIX4)
-# include <userconf.h>
-# if _AIX4 >= 40200
-# include <userpw.h>
-# endif /* _AIX4 >= 40200 */
-# include <usersec.h>
-# endif /* defined(_AIX3) || defined(_AIX4) */
-
-static char *DefaultUserShells[] =
-{
- "/bin/sh", /* standard shell */
-# ifdef MPE
- "/SYS/PUB/CI",
-# else /* MPE */
- "/usr/bin/sh",
- "/bin/csh", /* C shell */
- "/usr/bin/csh",
-# endif /* MPE */
-# ifdef __hpux
-# ifdef V4FS
- "/usr/bin/rsh", /* restricted Bourne shell */
- "/usr/bin/ksh", /* Korn shell */
- "/usr/bin/rksh", /* restricted Korn shell */
- "/usr/bin/pam",
- "/usr/bin/keysh", /* key shell (extended Korn shell) */
- "/usr/bin/posix/sh",
-# else /* V4FS */
- "/bin/rsh", /* restricted Bourne shell */
- "/bin/ksh", /* Korn shell */
- "/bin/rksh", /* restricted Korn shell */
- "/bin/pam",
- "/usr/bin/keysh", /* key shell (extended Korn shell) */
- "/bin/posix/sh",
- "/sbin/sh",
-# endif /* V4FS */
-# endif /* __hpux */
-# if defined(_AIX3) || defined(_AIX4)
- "/bin/ksh", /* Korn shell */
- "/usr/bin/ksh",
- "/bin/tsh", /* trusted shell */
- "/usr/bin/tsh",
- "/bin/bsh", /* Bourne shell */
- "/usr/bin/bsh",
-# endif /* defined(_AIX3) || defined(_AIX4) */
-# if defined(__svr4__) || defined(__svr5__)
- "/bin/ksh", /* Korn shell */
- "/usr/bin/ksh",
-# endif /* defined(__svr4__) || defined(__svr5__) */
-# ifdef sgi
- "/sbin/sh", /* SGI's shells really live in /sbin */
- "/usr/bin/sh",
- "/sbin/bsh", /* classic Bourne shell */
- "/bin/bsh",
- "/usr/bin/bsh",
- "/sbin/csh", /* standard csh */
- "/bin/csh",
- "/usr/bin/csh",
- "/sbin/jsh", /* classic Bourne shell w/ job control*/
- "/bin/jsh",
- "/usr/bin/jsh",
- "/bin/ksh", /* Korn shell */
- "/sbin/ksh",
- "/usr/bin/ksh",
- "/sbin/tcsh", /* Extended csh */
- "/bin/tcsh",
- "/usr/bin/tcsh",
-# endif /* sgi */
- NULL
-};
-
-#endif /* !HASGETUSERSHELL */
-
-#define WILDCARD_SHELL "/SENDMAIL/ANY/SHELL/"
-
-bool
-usershellok(user, shell)
- char *user;
- char *shell;
-{
-# if HASGETUSERSHELL
- register char *p;
- extern char *getusershell();
-
- if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
- ConfigLevel <= 1)
- return true;
-
- setusershell();
- while ((p = getusershell()) != NULL)
- if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0)
- break;
- endusershell();
- return p != NULL;
-# else /* HASGETUSERSHELL */
-# if USEGETCONFATTR
- auto char *v;
-# endif /* USEGETCONFATTR */
- register SM_FILE_T *shellf;
- char buf[MAXLINE];
-
- if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
- ConfigLevel <= 1)
- return true;
-
-# if USEGETCONFATTR
- /*
- ** Naturally IBM has a "better" idea.....
- **
- ** What a crock. This interface isn't documented, it is
- ** considered part of the security library (-ls), and it
- ** only works if you are running as root (since the list
- ** of valid shells is obviously a source of great concern).
- ** I recommend that you do NOT define USEGETCONFATTR,
- ** especially since you are going to have to set up an
- ** /etc/shells anyhow to handle the cases where getconfattr
- ** fails.
- */
-
- if (getconfattr(SC_SYS_LOGIN, SC_SHELLS, &v, SEC_LIST) == 0 && v != NULL)
- {
- while (*v != '\0')
- {
- if (strcmp(v, shell) == 0 || strcmp(v, WILDCARD_SHELL) == 0)
- return true;
- v += strlen(v) + 1;
- }
- return false;
- }
-# endif /* USEGETCONFATTR */
-
- shellf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, _PATH_SHELLS,
- SM_IO_RDONLY, NULL);
- if (shellf == NULL)
- {
- /* no /etc/shells; see if it is one of the std shells */
- char **d;
-
- if (errno != ENOENT && LogLevel > 3)
- sm_syslog(LOG_ERR, NOQID,
- "usershellok: cannot open %s: %s",
- _PATH_SHELLS, sm_errstring(errno));
-
- for (d = DefaultUserShells; *d != NULL; d++)
- {
- if (strcmp(shell, *d) == 0)
- return true;
- }
- return false;
- }
-
- while (sm_io_fgets(shellf, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
- {
- register char *p, *q;
-
- p = buf;
- while (*p != '\0' && *p != '#' && *p != '/')
- p++;
- if (*p == '#' || *p == '\0')
- continue;
- q = p;
- while (*p != '\0' && *p != '#' && !(isascii(*p) && isspace(*p)))
- p++;
- *p = '\0';
- if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0)
- {
- (void) sm_io_close(shellf, SM_TIME_DEFAULT);
- return true;
- }
- }
- (void) sm_io_close(shellf, SM_TIME_DEFAULT);
- return false;
-# endif /* HASGETUSERSHELL */
-}
-/*
-** FREEDISKSPACE -- see how much free space is on the queue filesystem
-**
-** Only implemented if you have statfs.
-**
-** Parameters:
-** dir -- the directory in question.
-** bsize -- a variable into which the filesystem
-** block size is stored.
-**
-** Returns:
-** The number of blocks free on the queue filesystem.
-** -1 if the statfs call fails.
-**
-** Side effects:
-** Puts the filesystem block size into bsize.
-*/
-
-/* statfs types */
-# define SFS_NONE 0 /* no statfs implementation */
-# define SFS_USTAT 1 /* use ustat */
-# define SFS_4ARGS 2 /* use four-argument statfs call */
-# define SFS_VFS 3 /* use <sys/vfs.h> implementation */
-# define SFS_MOUNT 4 /* use <sys/mount.h> implementation */
-# define SFS_STATFS 5 /* use <sys/statfs.h> implementation */
-# define SFS_STATVFS 6 /* use <sys/statvfs.h> implementation */
-
-# ifndef SFS_TYPE
-# define SFS_TYPE SFS_NONE
-# endif /* ! SFS_TYPE */
-
-# if SFS_TYPE == SFS_USTAT
-# include <ustat.h>
-# endif /* SFS_TYPE == SFS_USTAT */
-# if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS
-# include <sys/statfs.h>
-# endif /* SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS */
-# if SFS_TYPE == SFS_VFS
-# include <sys/vfs.h>
-# endif /* SFS_TYPE == SFS_VFS */
-# if SFS_TYPE == SFS_MOUNT
-# include <sys/mount.h>
-# endif /* SFS_TYPE == SFS_MOUNT */
-# if SFS_TYPE == SFS_STATVFS
-# include <sys/statvfs.h>
-# endif /* SFS_TYPE == SFS_STATVFS */
-
-long
-freediskspace(dir, bsize)
- const char *dir;
- long *bsize;
-{
-# if SFS_TYPE == SFS_NONE
- if (bsize != NULL)
- *bsize = 4096L;
-
- /* assume free space is plentiful */
- return (long) LONG_MAX;
-# else /* SFS_TYPE == SFS_NONE */
-# if SFS_TYPE == SFS_USTAT
- struct ustat fs;
- struct stat statbuf;
-# define FSBLOCKSIZE DEV_BSIZE
-# define SFS_BAVAIL f_tfree
-# else /* SFS_TYPE == SFS_USTAT */
-# if defined(ultrix)
- struct fs_data fs;
-# define SFS_BAVAIL fd_bfreen
-# define FSBLOCKSIZE 1024L
-# else /* defined(ultrix) */
-# if SFS_TYPE == SFS_STATVFS
- struct statvfs fs;
-# define FSBLOCKSIZE fs.f_frsize
-# else /* SFS_TYPE == SFS_STATVFS */
- struct statfs fs;
-# define FSBLOCKSIZE fs.f_bsize
-# endif /* SFS_TYPE == SFS_STATVFS */
-# endif /* defined(ultrix) */
-# endif /* SFS_TYPE == SFS_USTAT */
-# ifndef SFS_BAVAIL
-# define SFS_BAVAIL f_bavail
-# endif /* ! SFS_BAVAIL */
-
-# if SFS_TYPE == SFS_USTAT
- if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0)
-# else /* SFS_TYPE == SFS_USTAT */
-# if SFS_TYPE == SFS_4ARGS
- if (statfs(dir, &fs, sizeof(fs), 0) == 0)
-# else /* SFS_TYPE == SFS_4ARGS */
-# if SFS_TYPE == SFS_STATVFS
- if (statvfs(dir, &fs) == 0)
-# else /* SFS_TYPE == SFS_STATVFS */
-# if defined(ultrix)
- if (statfs(dir, &fs) > 0)
-# else /* defined(ultrix) */
- if (statfs(dir, &fs) == 0)
-# endif /* defined(ultrix) */
-# endif /* SFS_TYPE == SFS_STATVFS */
-# endif /* SFS_TYPE == SFS_4ARGS */
-# endif /* SFS_TYPE == SFS_USTAT */
- {
- if (bsize != NULL)
- *bsize = FSBLOCKSIZE;
- if (fs.SFS_BAVAIL <= 0)
- return 0;
- else if (fs.SFS_BAVAIL > LONG_MAX)
- return (long) LONG_MAX;
- else
- return (long) fs.SFS_BAVAIL;
- }
- return -1;
-# endif /* SFS_TYPE == SFS_NONE */
-}
-/*
-** ENOUGHDISKSPACE -- is there enough free space on the queue file systems?
-**
-** Parameters:
-** msize -- the size to check against. If zero, we don't yet
-** know how big the message will be, so just check for
-** a "reasonable" amount.
-** e -- envelope, or NULL -- controls logging
-**
-** Returns:
-** true if in every queue group there is at least one
-** queue directory whose file system contains enough free space.
-** false otherwise.
-**
-** Side Effects:
-** If there is not enough disk space and e != NULL
-** then sm_syslog is called.
-*/
-
-bool
-enoughdiskspace(msize, e)
- long msize;
- ENVELOPE *e;
-{
- int i;
-
- if (MinBlocksFree <= 0 && msize <= 0)
- {
- if (tTd(4, 80))
- sm_dprintf("enoughdiskspace: no threshold\n");
- return true;
- }
-
- filesys_update();
- for (i = 0; i < NumQueue; ++i)
- {
- if (pickqdir(Queue[i], msize, e) < 0)
- return false;
- }
- return true;
-}
-/*
-** TRANSIENTERROR -- tell if an error code indicates a transient failure
-**
-** This looks at an errno value and tells if this is likely to
-** go away if retried later.
-**
-** Parameters:
-** err -- the errno code to classify.
-**
-** Returns:
-** true if this is probably transient.
-** false otherwise.
-*/
-
-bool
-transienterror(err)
- int err;
-{
- switch (err)
- {
- case EIO: /* I/O error */
- case ENXIO: /* Device not configured */
- case EAGAIN: /* Resource temporarily unavailable */
- case ENOMEM: /* Cannot allocate memory */
- case ENODEV: /* Operation not supported by device */
- case ENFILE: /* Too many open files in system */
- case EMFILE: /* Too many open files */
- case ENOSPC: /* No space left on device */
- case ETIMEDOUT: /* Connection timed out */
-#ifdef ESTALE
- case ESTALE: /* Stale NFS file handle */
-#endif /* ESTALE */
-#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 ECONNRESET
- case ECONNRESET: /* Connection reset by peer */
-#endif /* ECONNRESET */
-#ifdef ENOBUFS
- case ENOBUFS: /* No buffer space available */
-#endif /* ENOBUFS */
-#ifdef ESHUTDOWN
- case ESHUTDOWN: /* Can't send after socket shutdown */
-#endif /* ESHUTDOWN */
-#ifdef ECONNREFUSED
- case ECONNREFUSED: /* Connection refused */
-#endif /* ECONNREFUSED */
-#ifdef EHOSTDOWN
- case EHOSTDOWN: /* Host is down */
-#endif /* EHOSTDOWN */
-#ifdef EHOSTUNREACH
- case EHOSTUNREACH: /* No route to host */
-#endif /* EHOSTUNREACH */
-#ifdef EDQUOT
- case EDQUOT: /* Disc quota exceeded */
-#endif /* EDQUOT */
-#ifdef EPROCLIM
- case EPROCLIM: /* Too many processes */
-#endif /* EPROCLIM */
-#ifdef EUSERS
- case EUSERS: /* Too many users */
-#endif /* EUSERS */
-#ifdef EDEADLK
- case EDEADLK: /* Resource deadlock avoided */
-#endif /* EDEADLK */
-#ifdef EISCONN
- case EISCONN: /* Socket already connected */
-#endif /* EISCONN */
-#ifdef EINPROGRESS
- case EINPROGRESS: /* Operation now in progress */
-#endif /* EINPROGRESS */
-#ifdef EALREADY
- case EALREADY: /* Operation already in progress */
-#endif /* EALREADY */
-#ifdef EADDRINUSE
- case EADDRINUSE: /* Address already in use */
-#endif /* EADDRINUSE */
-#ifdef EADDRNOTAVAIL
- case EADDRNOTAVAIL: /* Can't assign requested address */
-#endif /* EADDRNOTAVAIL */
-#ifdef ETXTBSY
- case ETXTBSY: /* (Apollo) file locked */
-#endif /* ETXTBSY */
-#if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR))
- case ENOSR: /* Out of streams resources */
-#endif /* defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR)) */
-#ifdef ENOLCK
- case ENOLCK: /* No locks available */
-#endif /* ENOLCK */
- case E_SM_OPENTIMEOUT: /* PSEUDO: open timed out */
- return true;
- }
-
- /* nope, must be permanent */
- return false;
-}
-/*
-** LOCKFILE -- lock a file using flock or (shudder) fcntl locking
-**
-** Parameters:
-** fd -- the file descriptor of the file.
-** filename -- the file name (for error messages).
-** ext -- the filename extension.
-** type -- type of the lock. Bits can be:
-** LOCK_EX -- exclusive lock.
-** LOCK_NB -- non-blocking.
-** LOCK_UN -- unlock.
-**
-** Returns:
-** true if the lock was acquired.
-** false otherwise.
-*/
-
-bool
-lockfile(fd, filename, ext, type)
- int fd;
- char *filename;
- char *ext;
- int type;
-{
- int i;
- int save_errno;
-# if !HASFLOCK
- int action;
- struct flock lfd;
-
- if (ext == NULL)
- ext = "";
-
- memset(&lfd, '\0', sizeof(lfd));
- if (bitset(LOCK_UN, type))
- lfd.l_type = F_UNLCK;
- else if (bitset(LOCK_EX, type))
- lfd.l_type = F_WRLCK;
- else
- lfd.l_type = F_RDLCK;
-
- if (bitset(LOCK_NB, type))
- action = F_SETLK;
- else
- action = F_SETLKW;
-
- if (tTd(55, 60))
- sm_dprintf("lockfile(%s%s, action=%d, type=%d): ",
- filename, ext, action, lfd.l_type);
-
- while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
- continue;
- if (i >= 0)
- {
- if (tTd(55, 60))
- sm_dprintf("SUCCESS\n");
- return true;
- }
- save_errno = errno;
-
- if (tTd(55, 60))
- sm_dprintf("(%s) ", sm_errstring(save_errno));
-
- /*
- ** On SunOS, if you are testing using -oQ/tmp/mqueue or
- ** -oA/tmp/aliases or anything like that, and /tmp is mounted
- ** as type "tmp" (that is, served from swap space), the
- ** previous fcntl will fail with "Invalid argument" errors.
- ** Since this is fairly common during testing, we will assume
- ** that this indicates that the lock is successfully grabbed.
- */
-
- if (save_errno == EINVAL)
- {
- if (tTd(55, 60))
- sm_dprintf("SUCCESS\n");
- return true;
- }
-
- if (!bitset(LOCK_NB, type) ||
- (save_errno != EACCES && save_errno != EAGAIN))
- {
- int omode = fcntl(fd, F_GETFL, 0);
- uid_t euid = geteuid();
-
- errno = save_errno;
- syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
- filename, ext, fd, type, omode, euid);
- dumpfd(fd, true, true);
- }
-# else /* !HASFLOCK */
- if (ext == NULL)
- ext = "";
-
- if (tTd(55, 60))
- sm_dprintf("lockfile(%s%s, type=%o): ", filename, ext, type);
-
- while ((i = flock(fd, type)) < 0 && errno == EINTR)
- continue;
- if (i >= 0)
- {
- if (tTd(55, 60))
- sm_dprintf("SUCCESS\n");
- return true;
- }
- save_errno = errno;
-
- if (tTd(55, 60))
- sm_dprintf("(%s) ", sm_errstring(save_errno));
-
- if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK)
- {
- int omode = fcntl(fd, F_GETFL, 0);
- uid_t euid = geteuid();
-
- errno = save_errno;
- syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
- filename, ext, fd, type, omode, euid);
- dumpfd(fd, true, true);
- }
-# endif /* !HASFLOCK */
- if (tTd(55, 60))
- sm_dprintf("FAIL\n");
- errno = save_errno;
- return false;
-}
-/*
-** CHOWNSAFE -- tell if chown is "safe" (executable only by root)
-**
-** Unfortunately, given that we can't predict other systems on which
-** a remote mounted (NFS) filesystem will be mounted, the answer is
-** almost always that this is unsafe.
-**
-** Note also that many operating systems have non-compliant
-** implementations of the _POSIX_CHOWN_RESTRICTED variable and the
-** fpathconf() routine. According to IEEE 1003.1-1990, if
-** _POSIX_CHOWN_RESTRICTED is defined and not equal to -1, then
-** no non-root process can give away the file. However, vendors
-** don't take NFS into account, so a comfortable value of
-** _POSIX_CHOWN_RESTRICTED tells us nothing.
-**
-** Also, some systems (e.g., IRIX 6.2) return 1 from fpathconf()
-** even on files where chown is not restricted. Many systems get
-** this wrong on NFS-based filesystems (that is, they say that chown
-** is restricted [safe] on NFS filesystems where it may not be, since
-** other systems can access the same filesystem and do file giveaway;
-** only the NFS server knows for sure!) Hence, it is important to
-** get the value of SAFENFSPATHCONF correct -- it should be defined
-** _only_ after testing (see test/t_pathconf.c) a system on an unsafe
-** NFS-based filesystem to ensure that you can get meaningful results.
-** If in doubt, assume unsafe!
-**
-** You may also need to tweak IS_SAFE_CHOWN -- it should be a
-** condition indicating whether the return from pathconf indicates
-** that chown is safe (typically either > 0 or >= 0 -- there isn't
-** even any agreement about whether a zero return means that a file
-** is or is not safe). It defaults to "> 0".
-**
-** If the parent directory is safe (writable only by owner back
-** to the root) then we can relax slightly and trust fpathconf
-** in more circumstances. This is really a crock -- if this is an
-** NFS mounted filesystem then we really know nothing about the
-** underlying implementation. However, most systems pessimize and
-** return an error (EINVAL or EOPNOTSUPP) on NFS filesystems, which
-** we interpret as unsafe, as we should. Thus, this heuristic gets
-** us into a possible problem only on systems that have a broken
-** pathconf implementation and which are also poorly configured
-** (have :include: files in group- or world-writable directories).
-**
-** Parameters:
-** fd -- the file descriptor to check.
-** safedir -- set if the parent directory is safe.
-**
-** Returns:
-** true -- if the chown(2) operation is "safe" -- that is,
-** only root can chown the file to an arbitrary user.
-** false -- if an arbitrary user can give away a file.
-*/
-
-#ifndef IS_SAFE_CHOWN
-# define IS_SAFE_CHOWN > 0
-#endif /* ! IS_SAFE_CHOWN */
-
-bool
-chownsafe(fd, safedir)
- int fd;
- bool safedir;
-{
-# if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
- (defined(_PC_CHOWN_RESTRICTED) || defined(_GNU_TYPES_H))
- int rval;
-
- /* give the system administrator a chance to override */
- if (bitnset(DBS_ASSUMESAFECHOWN, DontBlameSendmail))
- return true;
-
- /*
- ** Some systems (e.g., SunOS) seem to have the call and the
- ** #define _PC_CHOWN_RESTRICTED, but don't actually implement
- ** the call. This heuristic checks for that.
- */
-
- errno = 0;
- rval = fpathconf(fd, _PC_CHOWN_RESTRICTED);
-# if SAFENFSPATHCONF
- return errno == 0 && rval IS_SAFE_CHOWN;
-# else /* SAFENFSPATHCONF */
- return safedir && errno == 0 && rval IS_SAFE_CHOWN;
-# endif /* SAFENFSPATHCONF */
-# else /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */
- return bitnset(DBS_ASSUMESAFECHOWN, DontBlameSendmail);
-# endif /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */
-}
-/*
-** RESETLIMITS -- reset system controlled resource limits
-**
-** This is to avoid denial-of-service attacks
-**
-** Parameters:
-** none
-**
-** Returns:
-** none
-*/
-
-#if HASSETRLIMIT
-# ifdef RLIMIT_NEEDS_SYS_TIME_H
-# include <sm/time.h>
-# endif /* RLIMIT_NEEDS_SYS_TIME_H */
-# include <sys/resource.h>
-#endif /* HASSETRLIMIT */
-
-void
-resetlimits()
-{
-#if HASSETRLIMIT
- struct rlimit lim;
-
- lim.rlim_cur = lim.rlim_max = RLIM_INFINITY;
- (void) setrlimit(RLIMIT_CPU, &lim);
- (void) setrlimit(RLIMIT_FSIZE, &lim);
-# ifdef RLIMIT_NOFILE
- lim.rlim_cur = lim.rlim_max = FD_SETSIZE;
- (void) setrlimit(RLIMIT_NOFILE, &lim);
-# endif /* RLIMIT_NOFILE */
-#else /* HASSETRLIMIT */
-# if HASULIMIT
- (void) ulimit(2, 0x3fffff);
- (void) ulimit(4, FD_SETSIZE);
-# endif /* HASULIMIT */
-#endif /* HASSETRLIMIT */
- errno = 0;
-}
-/*
-** SETVENDOR -- process vendor code from V configuration line
-**
-** Parameters:
-** vendor -- string representation of vendor.
-**
-** Returns:
-** true -- if ok.
-** false -- if vendor code could not be processed.
-**
-** Side Effects:
-** It is reasonable to set mode flags here to tweak
-** processing in other parts of the code if necessary.
-** For example, if you are a vendor that uses $%y to
-** indicate YP lookups, you could enable that here.
-*/
-
-bool
-setvendor(vendor)
- char *vendor;
-{
- if (sm_strcasecmp(vendor, "Berkeley") == 0)
- {
- VendorCode = VENDOR_BERKELEY;
- return true;
- }
-
- /* add vendor extensions here */
-
-#ifdef SUN_EXTENSIONS
- if (sm_strcasecmp(vendor, "Sun") == 0)
- {
- VendorCode = VENDOR_SUN;
- return true;
- }
-#endif /* SUN_EXTENSIONS */
-#ifdef DEC
- if (sm_strcasecmp(vendor, "Digital") == 0)
- {
- VendorCode = VENDOR_DEC;
- return true;
- }
-#endif /* DEC */
-
-#if defined(VENDOR_NAME) && defined(VENDOR_CODE)
- if (sm_strcasecmp(vendor, VENDOR_NAME) == 0)
- {
- VendorCode = VENDOR_CODE;
- return true;
- }
-#endif /* defined(VENDOR_NAME) && defined(VENDOR_CODE) */
-
- return false;
-}
-/*
-** GETVENDOR -- return vendor name based on vendor code
-**
-** Parameters:
-** vendorcode -- numeric representation of vendor.
-**
-** Returns:
-** string containing vendor name.
-*/
-
-char *
-getvendor(vendorcode)
- int vendorcode;
-{
-#if defined(VENDOR_NAME) && defined(VENDOR_CODE)
- /*
- ** Can't have the same switch case twice so need to
- ** handle VENDOR_CODE outside of switch. It might
- ** match one of the existing VENDOR_* codes.
- */
-
- if (vendorcode == VENDOR_CODE)
- return VENDOR_NAME;
-#endif /* defined(VENDOR_NAME) && defined(VENDOR_CODE) */
-
- switch (vendorcode)
- {
- case VENDOR_BERKELEY:
- return "Berkeley";
-
- case VENDOR_SUN:
- return "Sun";
-
- case VENDOR_HP:
- return "HP";
-
- case VENDOR_IBM:
- return "IBM";
-
- case VENDOR_SENDMAIL:
- return "Sendmail";
-
- default:
- return "Unknown";
- }
-}
-/*
-** VENDOR_PRE_DEFAULTS, VENDOR_POST_DEFAULTS -- set vendor-specific defaults
-**
-** Vendor_pre_defaults is called before reading the configuration
-** file; vendor_post_defaults is called immediately after.
-**
-** Parameters:
-** e -- the global environment to initialize.
-**
-** Returns:
-** none.
-*/
-
-#if SHARE_V1
-int DefShareUid; /* default share uid to run as -- unused??? */
-#endif /* SHARE_V1 */
-
-void
-vendor_pre_defaults(e)
- ENVELOPE *e;
-{
-#if SHARE_V1
- /* OTHERUID is defined in shares.h, do not be alarmed */
- DefShareUid = OTHERUID;
-#endif /* SHARE_V1 */
-#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
- sun_pre_defaults(e);
-#endif /* defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES) */
-#ifdef apollo
- /*
- ** stupid domain/os can't even open
- ** /etc/mail/sendmail.cf without this
- */
-
- sm_setuserenv("ISP", NULL);
- sm_setuserenv("SYSTYPE", NULL);
-#endif /* apollo */
-}
-
-
-void
-vendor_post_defaults(e)
- ENVELOPE *e;
-{
-#ifdef __QNX__
- /* Makes sure the SOCK environment variable remains */
- sm_setuserenv("SOCK", NULL);
-#endif /* __QNX__ */
-#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
- sun_post_defaults(e);
-#endif /* defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES) */
-}
-/*
-** VENDOR_DAEMON_SETUP -- special vendor setup needed for daemon mode
-*/
-
-void
-vendor_daemon_setup(e)
- ENVELOPE *e;
-{
-#if HASSETLOGIN
- (void) setlogin(RunAsUserName);
-#endif /* HASSETLOGIN */
-#if SECUREWARE
- if (getluid() != -1)
- {
- usrerr("Daemon cannot have LUID");
- finis(false, true, EX_USAGE);
- }
-#endif /* SECUREWARE */
-}
-/*
-** VENDOR_SET_UID -- do setup for setting a user id
-**
-** This is called when we are still root.
-**
-** Parameters:
-** uid -- the uid we are about to become.
-**
-** Returns:
-** none.
-*/
-
-void
-vendor_set_uid(uid)
- UID_T uid;
-{
- /*
- ** We need to setup the share groups (lnodes)
- ** and add auditing information (luid's)
- ** before we loose our ``root''ness.
- */
-#if SHARE_V1
- if (setupshares(uid, syserr) != 0)
- syserr("Unable to set up shares");
-#endif /* SHARE_V1 */
-#if SECUREWARE
- (void) setup_secure(uid);
-#endif /* SECUREWARE */
-}
-/*
-** VALIDATE_CONNECTION -- check connection for rationality
-**
-** If the connection is rejected, this routine should log an
-** appropriate message -- but should never issue any SMTP protocol.
-**
-** Parameters:
-** sap -- a pointer to a SOCKADDR naming the peer.
-** hostname -- the name corresponding to sap.
-** e -- the current envelope.
-**
-** Returns:
-** error message from rejection.
-** NULL if not rejected.
-*/
-
-#if TCPWRAPPERS
-# include <tcpd.h>
-
-/* tcpwrappers does no logging, but you still have to declare these -- ugh */
-int allow_severity = LOG_INFO;
-int deny_severity = LOG_NOTICE;
-#endif /* TCPWRAPPERS */
-
-char *
-validate_connection(sap, hostname, e)
- SOCKADDR *sap;
- char *hostname;
- ENVELOPE *e;
-{
-#if TCPWRAPPERS
- char *host;
- char *addr;
- extern int hosts_ctl();
-#endif /* TCPWRAPPERS */
-
- if (tTd(48, 3))
- sm_dprintf("validate_connection(%s, %s)\n",
- hostname, anynet_ntoa(sap));
-
- connection_rate_check(sap, e);
- if (rscheck("check_relay", hostname, anynet_ntoa(sap),
- e, RSF_RMCOMM|RSF_COUNT, 3, NULL, NOQID, NULL) != EX_OK)
- {
- static char reject[BUFSIZ*2];
- extern char MsgBuf[];
-
- if (tTd(48, 4))
- sm_dprintf(" ... validate_connection: BAD (rscheck)\n");
-
- if (strlen(MsgBuf) >= 3)
- (void) sm_strlcpy(reject, MsgBuf, sizeof(reject));
- else
- (void) sm_strlcpy(reject, "Access denied", sizeof(reject));
-
- return reject;
- }
-
-#if TCPWRAPPERS
- if (hostname[0] == '[' && hostname[strlen(hostname) - 1] == ']')
- host = "unknown";
- else
- host = hostname;
- addr = anynet_ntoa(sap);
-
-# if NETINET6
- /* TCP/Wrappers don't want the IPv6: protocol label */
- if (addr != NULL && sm_strncasecmp(addr, "IPv6:", 5) == 0)
- addr += 5;
-# endif /* NETINET6 */
-
- if (!hosts_ctl("sendmail", host, addr, STRING_UNKNOWN))
- {
- if (tTd(48, 4))
- sm_dprintf(" ... validate_connection: BAD (tcpwrappers)\n");
- if (LogLevel > 3)
- sm_syslog(LOG_NOTICE, e->e_id,
- "tcpwrappers (%s, %s) rejection",
- host, addr);
- return "Access denied";
- }
-#endif /* TCPWRAPPERS */
- if (tTd(48, 4))
- sm_dprintf(" ... validate_connection: OK\n");
- return NULL;
-}
-
-/*
-** STRTOL -- convert string to long integer
-**
-** For systems that don't have it in the C library.
-**
-** This is taken verbatim from the 4.4-Lite C library.
-*/
-
-#if NEEDSTRTOL
-
-# if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93";
-# endif /* defined(LIBC_SCCS) && !defined(lint) */
-
-/*
-** Convert a string to a long integer.
-**
-** Ignores `locale' stuff. Assumes that the upper and lower case
-** alphabets and digits are each contiguous.
-*/
-
-long
-strtol(nptr, endptr, base)
- const char *nptr;
- char **endptr;
- register int base;
-{
- register const char *s = nptr;
- register unsigned long acc;
- register int c;
- register unsigned long cutoff;
- register int neg = 0, any, cutlim;
-
- /*
- ** Skip white space and pick up leading +/- sign if any.
- ** If base is 0, allow 0x for hex and 0 for octal, else
- ** assume decimal; if base is already 16, allow 0x.
- */
- do {
- c = *s++;
- } while (isspace(c));
- if (c == '-') {
- neg = 1;
- c = *s++;
- } else if (c == '+')
- c = *s++;
- if ((base == 0 || base == 16) &&
- c == '0' && (*s == 'x' || *s == 'X')) {
- c = s[1];
- s += 2;
- base = 16;
- }
- if (base == 0)
- base = c == '0' ? 8 : 10;
-
- /*
- ** Compute the cutoff value between legal numbers and illegal
- ** numbers. That is the largest legal value, divided by the
- ** base. An input number that is greater than this value, if
- ** followed by a legal input character, is too big. One that
- ** is equal to this value may be valid or not; the limit
- ** between valid and invalid numbers is then based on the last
- ** digit. For instance, if the range for longs is
- ** [-2147483648..2147483647] and the input base is 10,
- ** cutoff will be set to 214748364 and cutlim to either
- ** 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
- ** a value > 214748364, or equal but the next digit is > 7 (or 8),
- ** the number is too big, and we will return a range error.
- **
- ** Set any if any `digits' consumed; make it negative to indicate
- ** overflow.
- */
- cutoff = neg ? -(unsigned long) LONG_MIN : LONG_MAX;
- cutlim = cutoff % (unsigned long) base;
- cutoff /= (unsigned long) base;
- for (acc = 0, any = 0;; c = *s++) {
- if (isdigit(c))
- c -= '0';
- else if (isalpha(c))
- c -= isupper(c) ? 'A' - 10 : 'a' - 10;
- else
- break;
- if (c >= base)
- break;
- if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
- any = -1;
- else {
- any = 1;
- acc *= base;
- acc += c;
- }
- }
- if (any < 0) {
- acc = neg ? LONG_MIN : LONG_MAX;
- errno = ERANGE;
- } else if (neg)
- acc = -acc;
- if (endptr != 0)
- *endptr = (char *)(any ? s - 1 : nptr);
- return acc;
-}
-
-#endif /* NEEDSTRTOL */
-/*
-** STRSTR -- find first substring in string
-**
-** Parameters:
-** big -- the big (full) string.
-** little -- the little (sub) string.
-**
-** Returns:
-** A pointer to the first instance of little in big.
-** big if little is the null string.
-** NULL if little is not contained in big.
-*/
-
-#if NEEDSTRSTR
-
-char *
-strstr(big, little)
- char *big;
- char *little;
-{
- register char *p = big;
- int l;
-
- if (*little == '\0')
- return big;
- l = strlen(little);
-
- while ((p = strchr(p, *little)) != NULL)
- {
- if (strncmp(p, little, l) == 0)
- return p;
- p++;
- }
- return NULL;
-}
-
-#endif /* NEEDSTRSTR */
-/*
-** SM_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
-**
-** Some operating systems have wierd problems with the gethostbyXXX
-** routines. For example, Solaris versions at least through 2.3
-** don't properly deliver a canonical h_name field. This tries to
-** work around these problems.
-**
-** Support IPv6 as well as IPv4.
-*/
-
-#if NETINET6 && NEEDSGETIPNODE
-
-# ifndef AI_DEFAULT
-# define AI_DEFAULT 0 /* dummy */
-# endif /* ! AI_DEFAULT */
-# ifndef AI_ADDRCONFIG
-# define AI_ADDRCONFIG 0 /* dummy */
-# endif /* ! AI_ADDRCONFIG */
-# ifndef AI_V4MAPPED
-# define AI_V4MAPPED 0 /* dummy */
-# endif /* ! AI_V4MAPPED */
-# ifndef AI_ALL
-# define AI_ALL 0 /* dummy */
-# endif /* ! AI_ALL */
-
-static struct hostent *
-getipnodebyname(name, family, flags, err)
- char *name;
- int family;
- int flags;
- int *err;
-{
- bool resv6 = true;
- struct hostent *h;
-
- if (family == AF_INET6)
- {
- /* From RFC2133, section 6.1 */
- resv6 = bitset(RES_USE_INET6, _res.options);
- _res.options |= RES_USE_INET6;
- }
- SM_SET_H_ERRNO(0);
- h = gethostbyname(name);
- if (!resv6)
- _res.options &= ~RES_USE_INET6;
- *err = h_errno;
- return h;
-}
-
-static struct hostent *
-getipnodebyaddr(addr, len, family, err)
- char *addr;
- int len;
- int family;
- int *err;
-{
- struct hostent *h;
-
- SM_SET_H_ERRNO(0);
- h = gethostbyaddr(addr, len, family);
- *err = h_errno;
- return h;
-}
-
-void
-freehostent(h)
- struct hostent *h;
-{
- /*
- ** Stub routine -- if they don't have getipnodeby*(),
- ** they probably don't have the free routine either.
- */
-
- return;
-}
-#endif /* NETINET6 && NEEDSGETIPNODE */
-
-struct hostent *
-sm_gethostbyname(name, family)
- char *name;
- int family;
-{
- int save_errno;
- struct hostent *h = NULL;
-#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4))
-# if SOLARIS == 20300 || SOLARIS == 203
- static struct hostent hp;
- static char buf[1000];
- extern struct hostent *_switch_gethostbyname_r();
-
- if (tTd(61, 10))
- sm_dprintf("_switch_gethostbyname_r(%s)... ", name);
- h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
- save_errno = errno;
-# else /* SOLARIS == 20300 || SOLARIS == 203 */
- extern struct hostent *__switch_gethostbyname();
-
- if (tTd(61, 10))
- sm_dprintf("__switch_gethostbyname(%s)... ", name);
- h = __switch_gethostbyname(name);
- save_errno = errno;
-# endif /* SOLARIS == 20300 || SOLARIS == 203 */
-#else /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */
- int nmaps;
-# if NETINET6
- int flags = AI_DEFAULT|AI_ALL;
- int err;
-# endif /* NETINET6 */
- char *maptype[MAXMAPSTACK];
- short mapreturn[MAXMAPACTIONS];
- char hbuf[MAXNAME];
-
- if (tTd(61, 10))
- sm_dprintf("sm_gethostbyname(%s, %d)... ", name, family);
-
-# if NETINET6
-# if ADDRCONFIG_IS_BROKEN
- flags &= ~AI_ADDRCONFIG;
-# endif /* ADDRCONFIG_IS_BROKEN */
- h = getipnodebyname(name, family, flags, &err);
- SM_SET_H_ERRNO(err);
-# else /* NETINET6 */
- h = gethostbyname(name);
-# endif /* NETINET6 */
-
- save_errno = errno;
- if (h == NULL)
- {
- if (tTd(61, 10))
- sm_dprintf("failure\n");
-
- nmaps = switch_map_find("hosts", maptype, mapreturn);
- while (--nmaps >= 0)
- {
- if (strcmp(maptype[nmaps], "nis") == 0 ||
- strcmp(maptype[nmaps], "files") == 0)
- break;
- }
-
- if (nmaps >= 0)
- {
- /* try short name */
- if (strlen(name) > sizeof(hbuf) - 1)
- {
- errno = save_errno;
- return NULL;
- }
- (void) sm_strlcpy(hbuf, name, sizeof(hbuf));
- (void) shorten_hostname(hbuf);
-
- /* if it hasn't been shortened, there's no point */
- if (strcmp(hbuf, name) != 0)
- {
- if (tTd(61, 10))
- sm_dprintf("sm_gethostbyname(%s, %d)... ",
- hbuf, family);
-
-# if NETINET6
- h = getipnodebyname(hbuf, family, flags, &err);
- SM_SET_H_ERRNO(err);
- save_errno = errno;
-# else /* NETINET6 */
- h = gethostbyname(hbuf);
- save_errno = errno;
-# endif /* NETINET6 */
- }
- }
- }
-#endif /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */
- if (tTd(61, 10))
- {
- if (h == NULL)
- sm_dprintf("failure\n");
- else
- {
- sm_dprintf("%s\n", h->h_name);
- if (tTd(61, 11))
- {
-#if NETINET6
- struct in6_addr ia6;
- char buf6[INET6_ADDRSTRLEN];
-#else /* NETINET6 */
- struct in_addr ia;
-#endif /* NETINET6 */
- size_t i;
-
- if (h->h_aliases != NULL)
- for (i = 0; h->h_aliases[i] != NULL;
- i++)
- sm_dprintf("\talias: %s\n",
- h->h_aliases[i]);
- for (i = 0; h->h_addr_list[i] != NULL; i++)
- {
- char *addr;
-
-#if NETINET6
- memmove(&ia6, h->h_addr_list[i],
- IN6ADDRSZ);
- addr = anynet_ntop(&ia6,
- buf6, sizeof(buf6));
-#else /* NETINET6 */
- memmove(&ia, h->h_addr_list[i],
- INADDRSZ);
- addr = (char *) inet_ntoa(ia);
-#endif /* NETINET6 */
- if (addr != NULL)
- sm_dprintf("\taddr: %s\n", addr);
- }
- }
- }
- }
- errno = save_errno;
- return h;
-}
-
-struct hostent *
-sm_gethostbyaddr(addr, len, type)
- char *addr;
- int len;
- int type;
-{
- struct hostent *hp;
-
-#if NETINET6
- if (type == AF_INET6 &&
- IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *) addr))
- {
- /* Avoid reverse lookup for IPv6 unspecified address */
- SM_SET_H_ERRNO(HOST_NOT_FOUND);
- return NULL;
- }
-#endif /* NETINET6 */
-
-#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204)
-# if SOLARIS == 20300 || SOLARIS == 203
- {
- static struct hostent he;
- static char buf[1000];
- extern struct hostent *_switch_gethostbyaddr_r();
-
- hp = _switch_gethostbyaddr_r(addr, len, type, &he,
- buf, sizeof(buf), &h_errno);
- }
-# else /* SOLARIS == 20300 || SOLARIS == 203 */
- {
- extern struct hostent *__switch_gethostbyaddr();
-
- hp = __switch_gethostbyaddr(addr, len, type);
- }
-# endif /* SOLARIS == 20300 || SOLARIS == 203 */
-#else /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) */
-# if NETINET6
- {
- int err;
-
- hp = getipnodebyaddr(addr, len, type, &err);
- SM_SET_H_ERRNO(err);
- }
-# else /* NETINET6 */
- hp = gethostbyaddr(addr, len, type);
-# endif /* NETINET6 */
-#endif /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) */
- return hp;
-}
-/*
-** SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid
-*/
-
-struct passwd *
-sm_getpwnam(user)
- char *user;
-{
-#ifdef _AIX4
- extern struct passwd *_getpwnam_shadow(const char *, const int);
-
- return _getpwnam_shadow(user, 0);
-#else /* _AIX4 */
- return getpwnam(user);
-#endif /* _AIX4 */
-}
-
-struct passwd *
-sm_getpwuid(uid)
- UID_T uid;
-{
-#if defined(_AIX4) && 0
- extern struct passwd *_getpwuid_shadow(const int, const int);
-
- return _getpwuid_shadow(uid,0);
-#else /* defined(_AIX4) && 0 */
- return getpwuid(uid);
-#endif /* defined(_AIX4) && 0 */
-}
-/*
-** SECUREWARE_SETUP_SECURE -- Convex SecureWare setup
-**
-** Set up the trusted computing environment for C2 level security
-** under SecureWare.
-**
-** Parameters:
-** uid -- uid of the user to initialize in the TCB
-**
-** Returns:
-** none
-**
-** Side Effects:
-** Initialized the user in the trusted computing base
-*/
-
-#if SECUREWARE
-
-# include <sys/security.h>
-# include <prot.h>
-
-void
-secureware_setup_secure(uid)
- UID_T uid;
-{
- int rc;
-
- if (getluid() != -1)
- return;
-
- if ((rc = set_secure_info(uid)) != SSI_GOOD_RETURN)
- {
- switch (rc)
- {
- case SSI_NO_PRPW_ENTRY:
- syserr("No protected passwd entry, uid = %d",
- (int) uid);
- break;
-
- case SSI_LOCKED:
- syserr("Account has been disabled, uid = %d",
- (int) uid);
- break;
-
- case SSI_RETIRED:
- syserr("Account has been retired, uid = %d",
- (int) uid);
- break;
-
- case SSI_BAD_SET_LUID:
- syserr("Could not set LUID, uid = %d", (int) uid);
- break;
-
- case SSI_BAD_SET_PRIVS:
- syserr("Could not set kernel privs, uid = %d",
- (int) uid);
-
- default:
- syserr("Unknown return code (%d) from set_secure_info(%d)",
- rc, (int) uid);
- break;
- }
- finis(false, true, EX_NOPERM);
- }
-}
-#endif /* SECUREWARE */
-/*
-** ADD_HOSTNAMES -- Add a hostname to class 'w' based on IP address
-**
-** Add hostnames to class 'w' based on the IP address read from
-** the network interface.
-**
-** Parameters:
-** sa -- a pointer to a SOCKADDR containing the address
-**
-** Returns:
-** 0 if successful, -1 if host lookup fails.
-*/
-
-static int
-add_hostnames(sa)
- SOCKADDR *sa;
-{
- struct hostent *hp;
- char **ha;
- char hnb[MAXHOSTNAMELEN];
-
- /* lookup name with IP address */
- switch (sa->sa.sa_family)
- {
-#if NETINET
- case AF_INET:
- hp = sm_gethostbyaddr((char *) &sa->sin.sin_addr,
- sizeof(sa->sin.sin_addr),
- sa->sa.sa_family);
- break;
-#endif /* NETINET */
-
-#if NETINET6
- case AF_INET6:
- hp = sm_gethostbyaddr((char *) &sa->sin6.sin6_addr,
- sizeof(sa->sin6.sin6_addr),
- sa->sa.sa_family);
- break;
-#endif /* NETINET6 */
-
- default:
- /* Give warning about unsupported family */
- if (LogLevel > 3)
- sm_syslog(LOG_WARNING, NOQID,
- "Unsupported address family %d: %.100s",
- sa->sa.sa_family, anynet_ntoa(sa));
- return -1;
- }
-
- if (hp == NULL)
- {
- int save_errno = errno;
-
- if (LogLevel > 3 &&
-#if NETINET6
- !(sa->sa.sa_family == AF_INET6 &&
- IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr)) &&
-#endif /* NETINET6 */
- true)
- sm_syslog(LOG_WARNING, NOQID,
- "gethostbyaddr(%.100s) failed: %d",
- anynet_ntoa(sa),
-#if NAMED_BIND
- h_errno
-#else /* NAMED_BIND */
- -1
-#endif /* NAMED_BIND */
- );
- errno = save_errno;
- return -1;
- }
-
- /* save its cname */
- if (!wordinclass((char *) hp->h_name, 'w'))
- {
- setclass('w', (char *) hp->h_name);
- if (tTd(0, 4))
- sm_dprintf("\ta.k.a.: %s\n", hp->h_name);
-
- if (sm_snprintf(hnb, sizeof(hnb), "[%s]", hp->h_name) <
- sizeof(hnb)
- && !wordinclass((char *) hnb, 'w'))
- setclass('w', hnb);
- }
- else
- {
- if (tTd(0, 43))
- sm_dprintf("\ta.k.a.: %s (already in $=w)\n", hp->h_name);
- }
-
- /* save all it aliases name */
- for (ha = hp->h_aliases; ha != NULL && *ha != NULL; ha++)
- {
- if (!wordinclass(*ha, 'w'))
- {
- setclass('w', *ha);
- if (tTd(0, 4))
- sm_dprintf("\ta.k.a.: %s\n", *ha);
- if (sm_snprintf(hnb, sizeof(hnb),
- "[%s]", *ha) < sizeof(hnb) &&
- !wordinclass((char *) hnb, 'w'))
- setclass('w', hnb);
- }
- else
- {
- if (tTd(0, 43))
- sm_dprintf("\ta.k.a.: %s (already in $=w)\n",
- *ha);
- }
- }
-#if NETINET6
- freehostent(hp);
-#endif /* NETINET6 */
- return 0;
-}
-/*
-** LOAD_IF_NAMES -- load interface-specific names into $=w
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Loads $=w with the names of all the interfaces.
-*/
-
-#if !NETINET
-# define SIOCGIFCONF_IS_BROKEN 1 /* XXX */
-#endif /* !NETINET */
-
-#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
-struct rtentry;
-struct mbuf;
-# ifndef SUNOS403
-# include <sm/time.h>
-# endif /* ! SUNOS403 */
-# if (_AIX4 >= 40300) && !defined(_NET_IF_H)
-# undef __P
-# endif /* (_AIX4 >= 40300) && !defined(_NET_IF_H) */
-# include <net/if.h>
-#endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */
-
-void
-load_if_names()
-{
-# if NETINET6 && defined(SIOCGLIFCONF)
-# ifdef __hpux
-
- /*
- ** Unfortunately, HP has changed all of the structures,
- ** making life difficult for implementors.
- */
-
-# define lifconf if_laddrconf
-# define lifc_len iflc_len
-# define lifc_buf iflc_buf
-# define lifreq if_laddrreq
-# define lifr_addr iflr_addr
-# define lifr_name iflr_name
-# define lifr_flags iflr_flags
-# define ss_family sa_family
-# undef SIOCGLIFNUM
-# endif /* __hpux */
-
- int s;
- int i;
- size_t len;
- int numifs;
- char *buf;
- struct lifconf lifc;
-# ifdef SIOCGLIFNUM
- struct lifnum lifn;
-# endif /* SIOCGLIFNUM */
-
- s = socket(InetMode, SOCK_DGRAM, 0);
- if (s == -1)
- return;
-
- /* get the list of known IP address from the kernel */
-# ifdef __hpux
- i = ioctl(s, SIOCGIFNUM, (char *) &numifs);
-# endif /* __hpux */
-# ifdef SIOCGLIFNUM
- lifn.lifn_family = AF_UNSPEC;
- lifn.lifn_flags = 0;
- i = ioctl(s, SIOCGLIFNUM, (char *)&lifn);
- numifs = lifn.lifn_count;
-# endif /* SIOCGLIFNUM */
-
-# if defined(__hpux) || defined(SIOCGLIFNUM)
- if (i < 0)
- {
- /* can't get number of interfaces -- fall back */
- if (tTd(0, 4))
- sm_dprintf("SIOCGLIFNUM failed: %s\n",
- sm_errstring(errno));
- numifs = -1;
- }
- else if (tTd(0, 42))
- sm_dprintf("system has %d interfaces\n", numifs);
- if (numifs < 0)
-# endif /* defined(__hpux) || defined(SIOCGLIFNUM) */
- numifs = MAXINTERFACES;
-
- if (numifs <= 0)
- {
- (void) close(s);
- return;
- }
-
- len = lifc.lifc_len = numifs * sizeof(struct lifreq);
- buf = lifc.lifc_buf = xalloc(lifc.lifc_len);
-# ifndef __hpux
- lifc.lifc_family = AF_UNSPEC;
- lifc.lifc_flags = 0;
-# endif /* ! __hpux */
- if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
- {
- if (tTd(0, 4))
- sm_dprintf("SIOCGLIFCONF failed: %s\n",
- sm_errstring(errno));
- (void) close(s);
- sm_free(buf);
- return;
- }
-
- /* scan the list of IP address */
- if (tTd(0, 40))
- sm_dprintf("scanning for interface specific names, lifc_len=%ld\n",
- (long) len);
-
- for (i = 0; i < len && i >= 0; )
- {
- int flags;
- struct lifreq *ifr = (struct lifreq *)&buf[i];
- SOCKADDR *sa = (SOCKADDR *) &ifr->lifr_addr;
- int af = ifr->lifr_addr.ss_family;
- char *addr;
- char *name;
- struct in6_addr ia6;
- struct in_addr ia;
-# ifdef SIOCGLIFFLAGS
- struct lifreq ifrf;
-# endif /* SIOCGLIFFLAGS */
- char ip_addr[256];
- char buf6[INET6_ADDRSTRLEN];
-
- /*
- ** We must close and recreate the socket each time
- ** since we don't know what type of socket it is now
- ** (each status function may change it).
- */
-
- (void) close(s);
-
- s = socket(af, SOCK_DGRAM, 0);
- if (s == -1)
- {
- sm_free(buf); /* XXX */
- return;
- }
-
- /*
- ** If we don't have a complete ifr structure,
- ** don't try to use it.
- */
-
- if ((len - i) < sizeof(*ifr))
- break;
-
-# ifdef BSD4_4_SOCKADDR
- if (sa->sa.sa_len > sizeof(ifr->lifr_addr))
- i += sizeof(ifr->lifr_name) + sa->sa.sa_len;
- else
-# endif /* BSD4_4_SOCKADDR */
-# ifdef DEC
- /* fix for IPv6 size differences */
- i += sizeof(ifr->ifr_name) +
- max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
-# else /* DEC */
- i += sizeof(*ifr);
-# endif /* DEC */
-
- if (tTd(0, 20))
- sm_dprintf("%s\n", anynet_ntoa(sa));
-
- if (af != AF_INET && af != AF_INET6)
- continue;
-
-# ifdef SIOCGLIFFLAGS
- memset(&ifrf, '\0', sizeof(struct lifreq));
- (void) sm_strlcpy(ifrf.lifr_name, ifr->lifr_name,
- sizeof(ifrf.lifr_name));
- if (ioctl(s, SIOCGLIFFLAGS, (char *) &ifrf) < 0)
- {
- if (tTd(0, 4))
- sm_dprintf("SIOCGLIFFLAGS failed: %s\n",
- sm_errstring(errno));
- continue;
- }
-
- name = ifr->lifr_name;
- flags = ifrf.lifr_flags;
-
- if (tTd(0, 41))
- sm_dprintf("\tflags: %lx\n", (unsigned long) flags);
-
- if (!bitset(IFF_UP, flags))
- continue;
-# endif /* SIOCGLIFFLAGS */
-
- ip_addr[0] = '\0';
-
- /* extract IP address from the list*/
- switch (af)
- {
- case AF_INET6:
-# ifdef __KAME__
- /* convert into proper scoped address */
- if ((IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr) ||
- IN6_IS_ADDR_SITELOCAL(&sa->sin6.sin6_addr)) &&
- sa->sin6.sin6_scope_id == 0)
- {
- struct in6_addr *ia6p;
-
- ia6p = &sa->sin6.sin6_addr;
- sa->sin6.sin6_scope_id = ntohs(ia6p->s6_addr[3] |
- ((unsigned int)ia6p->s6_addr[2] << 8));
- ia6p->s6_addr[2] = ia6p->s6_addr[3] = 0;
- }
-# endif /* __KAME__ */
- ia6 = sa->sin6.sin6_addr;
- if (IN6_IS_ADDR_UNSPECIFIED(&ia6))
- {
- addr = anynet_ntop(&ia6, buf6, sizeof(buf6));
- message("WARNING: interface %s is UP with %s address",
- name, addr == NULL ? "(NULL)" : addr);
- continue;
- }
-
- /* save IP address in text from */
- addr = anynet_ntop(&ia6, buf6, sizeof(buf6));
- if (addr != NULL)
- (void) sm_snprintf(ip_addr, sizeof(ip_addr),
- "[%.*s]",
- (int) sizeof(ip_addr) - 3,
- addr);
- break;
-
- case AF_INET:
- ia = sa->sin.sin_addr;
- if (ia.s_addr == INADDR_ANY ||
- ia.s_addr == INADDR_NONE)
- {
- message("WARNING: interface %s is UP with %s address",
- name, inet_ntoa(ia));
- continue;
- }
-
- /* save IP address in text from */
- (void) sm_snprintf(ip_addr, sizeof(ip_addr), "[%.*s]",
- (int) sizeof(ip_addr) - 3, inet_ntoa(ia));
- break;
- }
-
- if (*ip_addr == '\0')
- continue;
-
- if (!wordinclass(ip_addr, 'w'))
- {
- setclass('w', ip_addr);
- if (tTd(0, 4))
- sm_dprintf("\ta.k.a.: %s\n", ip_addr);
- }
-
-# ifdef SIOCGLIFFLAGS
- /* skip "loopback" interface "lo" */
- if (DontProbeInterfaces == DPI_SKIPLOOPBACK &&
- bitset(IFF_LOOPBACK, flags))
- continue;
-# endif /* SIOCGLIFFLAGS */
- (void) add_hostnames(sa);
- }
- sm_free(buf); /* XXX */
- (void) close(s);
-# else /* NETINET6 && defined(SIOCGLIFCONF) */
-# if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
- int s;
- int i;
- struct ifconf ifc;
- int numifs;
-
- s = socket(AF_INET, SOCK_DGRAM, 0);
- if (s == -1)
- return;
-
- /* get the list of known IP address from the kernel */
-# if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN
- if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0)
- {
- /* can't get number of interfaces -- fall back */
- if (tTd(0, 4))
- sm_dprintf("SIOCGIFNUM failed: %s\n",
- sm_errstring(errno));
- numifs = -1;
- }
- else if (tTd(0, 42))
- sm_dprintf("system has %d interfaces\n", numifs);
- if (numifs < 0)
-# endif /* defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN */
- numifs = MAXINTERFACES;
-
- if (numifs <= 0)
- {
- (void) close(s);
- return;
- }
- ifc.ifc_len = numifs * sizeof(struct ifreq);
- ifc.ifc_buf = xalloc(ifc.ifc_len);
- if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0)
- {
- if (tTd(0, 4))
- sm_dprintf("SIOCGIFCONF failed: %s\n",
- sm_errstring(errno));
- (void) close(s);
- return;
- }
-
- /* scan the list of IP address */
- if (tTd(0, 40))
- sm_dprintf("scanning for interface specific names, ifc_len=%d\n",
- ifc.ifc_len);
-
- for (i = 0; i < ifc.ifc_len && i >= 0; )
- {
- int af;
- struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i];
- SOCKADDR *sa = (SOCKADDR *) &ifr->ifr_addr;
-# if NETINET6
- char *addr;
- struct in6_addr ia6;
-# endif /* NETINET6 */
- struct in_addr ia;
-# ifdef SIOCGIFFLAGS
- struct ifreq ifrf;
-# endif /* SIOCGIFFLAGS */
- char ip_addr[256];
-# if NETINET6
- char buf6[INET6_ADDRSTRLEN];
-# endif /* NETINET6 */
-
- /*
- ** If we don't have a complete ifr structure,
- ** don't try to use it.
- */
-
- if ((ifc.ifc_len - i) < sizeof(*ifr))
- break;
-
-# ifdef BSD4_4_SOCKADDR
- if (sa->sa.sa_len > sizeof(ifr->ifr_addr))
- i += sizeof(ifr->ifr_name) + sa->sa.sa_len;
- else
-# endif /* BSD4_4_SOCKADDR */
- i += sizeof(*ifr);
-
- if (tTd(0, 20))
- sm_dprintf("%s\n", anynet_ntoa(sa));
-
- af = ifr->ifr_addr.sa_family;
- if (af != AF_INET
-# if NETINET6
- && af != AF_INET6
-# endif /* NETINET6 */
- )
- continue;
-
-# ifdef SIOCGIFFLAGS
- memset(&ifrf, '\0', sizeof(struct ifreq));
- (void) sm_strlcpy(ifrf.ifr_name, ifr->ifr_name,
- sizeof(ifrf.ifr_name));
- (void) ioctl(s, SIOCGIFFLAGS, (char *) &ifrf);
- if (tTd(0, 41))
- sm_dprintf("\tflags: %lx\n",
- (unsigned long) ifrf.ifr_flags);
-# define IFRFREF ifrf
-# else /* SIOCGIFFLAGS */
-# define IFRFREF (*ifr)
-# endif /* SIOCGIFFLAGS */
-
- if (!bitset(IFF_UP, IFRFREF.ifr_flags))
- continue;
-
- ip_addr[0] = '\0';
-
- /* extract IP address from the list*/
- switch (af)
- {
- case AF_INET:
- ia = sa->sin.sin_addr;
- if (ia.s_addr == INADDR_ANY ||
- ia.s_addr == INADDR_NONE)
- {
- message("WARNING: interface %s is UP with %s address",
- ifr->ifr_name, inet_ntoa(ia));
- continue;
- }
-
- /* save IP address in text from */
- (void) sm_snprintf(ip_addr, sizeof(ip_addr), "[%.*s]",
- (int) sizeof(ip_addr) - 3,
- inet_ntoa(ia));
- break;
-
-# if NETINET6
- case AF_INET6:
-# ifdef __KAME__
- /* convert into proper scoped address */
- if ((IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr) ||
- IN6_IS_ADDR_SITELOCAL(&sa->sin6.sin6_addr)) &&
- sa->sin6.sin6_scope_id == 0)
- {
- struct in6_addr *ia6p;
-
- ia6p = &sa->sin6.sin6_addr;
- sa->sin6.sin6_scope_id = ntohs(ia6p->s6_addr[3] |
- ((unsigned int)ia6p->s6_addr[2] << 8));
- ia6p->s6_addr[2] = ia6p->s6_addr[3] = 0;
- }
-# endif /* __KAME__ */
- ia6 = sa->sin6.sin6_addr;
- if (IN6_IS_ADDR_UNSPECIFIED(&ia6))
- {
- addr = anynet_ntop(&ia6, buf6, sizeof(buf6));
- message("WARNING: interface %s is UP with %s address",
- ifr->ifr_name,
- addr == NULL ? "(NULL)" : addr);
- continue;
- }
-
- /* save IP address in text from */
- addr = anynet_ntop(&ia6, buf6, sizeof(buf6));
- if (addr != NULL)
- (void) sm_snprintf(ip_addr, sizeof(ip_addr),
- "[%.*s]",
- (int) sizeof(ip_addr) - 3,
- addr);
- break;
-
-# endif /* NETINET6 */
- }
-
- if (ip_addr[0] == '\0')
- continue;
-
- if (!wordinclass(ip_addr, 'w'))
- {
- setclass('w', ip_addr);
- if (tTd(0, 4))
- sm_dprintf("\ta.k.a.: %s\n", ip_addr);
- }
-
- /* skip "loopback" interface "lo" */
- if (DontProbeInterfaces == DPI_SKIPLOOPBACK &&
- bitset(IFF_LOOPBACK, IFRFREF.ifr_flags))
- continue;
-
- (void) add_hostnames(sa);
- }
- sm_free(ifc.ifc_buf); /* XXX */
- (void) close(s);
-# undef IFRFREF
-# endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */
-# endif /* NETINET6 && defined(SIOCGLIFCONF) */
-}
-/*
-** ISLOOPBACK -- is socket address in the loopback net?
-**
-** Parameters:
-** sa -- socket address.
-**
-** Returns:
-** true -- is socket address in the loopback net?
-** false -- otherwise
-**
-*/
-
-bool
-isloopback(sa)
- SOCKADDR sa;
-{
-#if NETINET6
- if (IN6_IS_ADDR_LOOPBACK(&sa.sin6.sin6_addr))
- return true;
-#else /* NETINET6 */
- /* XXX how to correctly extract IN_LOOPBACKNET part? */
- if (((ntohl(sa.sin.sin_addr.s_addr) & IN_CLASSA_NET)
- >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
- return true;
-#endif /* NETINET6 */
- return false;
-}
-/*
-** GET_NUM_PROCS_ONLINE -- return the number of processors currently online
-**
-** Parameters:
-** none.
-**
-** Returns:
-** The number of processors online.
-*/
-
-static int
-get_num_procs_online()
-{
- int nproc = 0;
-
-#ifdef USESYSCTL
-# if defined(CTL_HW) && defined(HW_NCPU)
- size_t sz;
- int mib[2];
-
- mib[0] = CTL_HW;
- mib[1] = HW_NCPU;
- sz = (size_t) sizeof(nproc);
- (void) sysctl(mib, 2, &nproc, &sz, NULL, 0);
-# endif /* defined(CTL_HW) && defined(HW_NCPU) */
-#else /* USESYSCTL */
-# ifdef _SC_NPROCESSORS_ONLN
- nproc = (int) sysconf(_SC_NPROCESSORS_ONLN);
-# else /* _SC_NPROCESSORS_ONLN */
-# ifdef __hpux
-# include <sys/pstat.h>
- struct pst_dynamic psd;
-
- if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) != -1)
- nproc = psd.psd_proc_cnt;
-# endif /* __hpux */
-# endif /* _SC_NPROCESSORS_ONLN */
-#endif /* USESYSCTL */
-
- if (nproc <= 0)
- nproc = 1;
- return nproc;
-}
-/*
-** SM_CLOSEFROM -- close file descriptors
-**
-** Parameters:
-** lowest -- first fd to close
-** highest -- last fd + 1 to close
-**
-** Returns:
-** none
-*/
-
-void
-sm_closefrom(lowest, highest)
- int lowest, highest;
-{
-#if HASCLOSEFROM
- closefrom(lowest);
-#else /* HASCLOSEFROM */
- int i;
-
- for (i = lowest; i < highest; i++)
- (void) close(i);
-#endif /* HASCLOSEFROM */
-}
-#if HASFDWALK
-/*
-** CLOSEFD_WALK -- walk fd's arranging to close them
-** Callback for fdwalk()
-**
-** Parameters:
-** lowest -- first fd to arrange to be closed
-** fd -- fd to arrange to be closed
-**
-** Returns:
-** zero
-*/
-
-static int
-closefd_walk(lowest, fd)
- void *lowest;
- int fd;
-{
- if (fd >= *(int *)lowest)
- (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
- return 0;
-}
-#endif /* HASFDWALK */
-/*
-** SM_CLOSE_ON_EXEC -- arrange for file descriptors to be closed
-**
-** Parameters:
-** lowest -- first fd to arrange to be closed
-** highest -- last fd + 1 to arrange to be closed
-**
-** Returns:
-** none
-*/
-
-void
-sm_close_on_exec(highest, lowest)
- int highest, lowest;
-{
-#if HASFDWALK
- (void) fdwalk(closefd_walk, &lowest);
-#else /* HASFDWALK */
- int i, j;
-
- for (i = lowest; i < highest; i++)
- {
- if ((j = fcntl(i, F_GETFD, 0)) != -1)
- (void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
- }
-#endif /* HASFDWALK */
-}
-/*
-** SEED_RANDOM -- seed the random number generator
-**
-** Parameters:
-** none
-**
-** Returns:
-** none
-*/
-
-void
-seed_random()
-{
-#if HASSRANDOMDEV
- srandomdev();
-#else /* HASSRANDOMDEV */
- long seed;
- struct timeval t;
-
- seed = (long) CurrentPid;
- if (gettimeofday(&t, NULL) >= 0)
- seed += t.tv_sec + t.tv_usec;
-
-# if HASRANDOM
- (void) srandom(seed);
-# else /* HASRANDOM */
- (void) srand((unsigned int) seed);
-# endif /* HASRANDOM */
-#endif /* HASSRANDOMDEV */
-}
-/*
-** SM_SYSLOG -- syslog wrapper to keep messages under SYSLOG_BUFSIZE
-**
-** Parameters:
-** level -- syslog level
-** id -- envelope ID or NULL (NOQUEUE)
-** fmt -- format string
-** arg... -- arguments as implied by fmt.
-**
-** Returns:
-** none
-*/
-
-/* VARARGS3 */
-void
-#ifdef __STDC__
-sm_syslog(int level, const char *id, const char *fmt, ...)
-#else /* __STDC__ */
-sm_syslog(level, id, fmt, va_alist)
- int level;
- const char *id;
- const char *fmt;
- va_dcl
-#endif /* __STDC__ */
-{
- char *buf;
- size_t bufsize;
- char *begin, *end;
- int save_errno;
- int seq = 1;
- int idlen;
- char buf0[MAXLINE];
- char *newstring;
- extern int SyslogPrefixLen;
- SM_VA_LOCAL_DECL
-
- save_errno = errno;
- if (id == NULL)
- id = "NOQUEUE";
- idlen = strlen(id) + SyslogPrefixLen;
-
- buf = buf0;
- bufsize = sizeof(buf0);
-
- for (;;)
- {
- int n;
-
- /* print log message into buf */
- SM_VA_START(ap, fmt);
- n = sm_vsnprintf(buf, bufsize, fmt, ap);
- SM_VA_END(ap);
- SM_ASSERT(n > 0);
- if (n < bufsize)
- break;
-
- /* String too small, redo with correct size */
- bufsize = n + 1;
- if (buf != buf0)
- {
- sm_free(buf);
- buf = NULL;
- }
- buf = sm_malloc_x(bufsize);
- }
-
- /* clean up buf after it has been expanded with args */
- newstring = str2prt(buf);
- if ((strlen(newstring) + idlen + 1) < SYSLOG_BUFSIZE)
- {
-#if LOG
- if (*id == '\0')
- {
- if (tTd(89, 8))
- sm_dprintf("%s\n", newstring);
- else
- syslog(level, "%s", newstring);
- }
- else
- {
- if (tTd(89, 8))
- sm_dprintf("%s: %s\n", id, newstring);
- else
- syslog(level, "%s: %s", id, newstring);
- }
-#else /* LOG */
- /*XXX should do something more sensible */
- if (*id == '\0')
- (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s\n",
- newstring);
- else
- (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
- "%s: %s\n", id, newstring);
-#endif /* LOG */
- if (buf != buf0)
- sm_free(buf);
- errno = save_errno;
- return;
- }
-
-/*
-** additional length for splitting: " ..." + 3, where 3 is magic to
-** have some data for the next entry.
-*/
-
-#define SL_SPLIT 7
-
- begin = newstring;
- idlen += 5; /* strlen("[999]"), see below */
- while (*begin != '\0' &&
- (strlen(begin) + idlen) > SYSLOG_BUFSIZE)
- {
- char save;
-
- if (seq >= 999)
- {
- /* Too many messages */
- break;
- }
- end = begin + SYSLOG_BUFSIZE - idlen - SL_SPLIT;
- while (end > begin)
- {
- /* Break on comma or space */
- if (*end == ',' || *end == ' ')
- {
- end++; /* Include separator */
- break;
- }
- end--;
- }
- /* No separator, break midstring... */
- if (end == begin)
- end = begin + SYSLOG_BUFSIZE - idlen - SL_SPLIT;
- save = *end;
- *end = 0;
-#if LOG
- if (tTd(89, 8))
- sm_dprintf("%s[%d]: %s ...\n", id, seq++, begin);
- else
- syslog(level, "%s[%d]: %s ...", id, seq++, begin);
-#else /* LOG */
- (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
- "%s[%d]: %s ...\n", id, seq++, begin);
-#endif /* LOG */
- *end = save;
- begin = end;
- }
- if (seq >= 999)
- {
-#if LOG
- if (tTd(89, 8))
- sm_dprintf("%s[%d]: log terminated, too many parts\n",
- id, seq);
- else
- syslog(level, "%s[%d]: log terminated, too many parts",
- id, seq);
-#else /* LOG */
- (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
- "%s[%d]: log terminated, too many parts\n", id, seq);
-#endif /* LOG */
- }
- else if (*begin != '\0')
- {
-#if LOG
- if (tTd(89, 8))
- sm_dprintf("%s[%d]: %s\n", id, seq, begin);
- else
- syslog(level, "%s[%d]: %s", id, seq, begin);
-#else /* LOG */
- (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
- "%s[%d]: %s\n", id, seq, begin);
-#endif /* LOG */
- }
- if (buf != buf0)
- sm_free(buf);
- errno = save_errno;
-}
-/*
-** HARD_SYSLOG -- call syslog repeatedly until it works
-**
-** Needed on HP-UX, which apparently doesn't guarantee that
-** syslog succeeds during interrupt handlers.
-*/
-
-#if defined(__hpux) && !defined(HPUX11)
-
-# define MAXSYSLOGTRIES 100
-# undef syslog
-# ifdef V4FS
-# define XCNST const
-# define CAST (const char *)
-# else /* V4FS */
-# define XCNST
-# define CAST
-# endif /* V4FS */
-
-void
-# ifdef __STDC__
-hard_syslog(int pri, XCNST char *msg, ...)
-# else /* __STDC__ */
-hard_syslog(pri, msg, va_alist)
- int pri;
- XCNST char *msg;
- va_dcl
-# endif /* __STDC__ */
-{
- int i;
- char buf[SYSLOG_BUFSIZE];
- SM_VA_LOCAL_DECL
-
- SM_VA_START(ap, msg);
- (void) sm_vsnprintf(buf, sizeof(buf), msg, ap);
- SM_VA_END(ap);
-
- for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; )
- continue;
-}
-
-# undef CAST
-#endif /* defined(__hpux) && !defined(HPUX11) */
-#if NEEDLOCAL_HOSTNAME_LENGTH
-/*
-** LOCAL_HOSTNAME_LENGTH
-**
-** This is required to get sendmail to compile against BIND 4.9.x
-** on Ultrix.
-**
-** Unfortunately, a Compaq Y2K patch kit provides it without
-** bumping __RES in /usr/include/resolv.h so we can't automatically
-** figure out whether it is needed.
-*/
-
-int
-local_hostname_length(hostname)
- char *hostname;
-{
- size_t len_host, len_domain;
-
- if (!*_res.defdname)
- res_init();
- len_host = strlen(hostname);
- len_domain = strlen(_res.defdname);
- if (len_host > len_domain &&
- (sm_strcasecmp(hostname + len_host - len_domain,
- _res.defdname) == 0) &&
- hostname[len_host - len_domain - 1] == '.')
- return len_host - len_domain - 1;
- else
- return 0;
-}
-#endif /* NEEDLOCAL_HOSTNAME_LENGTH */
-
-#if NEEDLINK
-/*
-** LINK -- clone a file
-**
-** Some OS's lacks link() and hard links. Since sendmail is using
-** link() as an efficient way to clone files, this implementation
-** will simply do a file copy.
-**
-** NOTE: This link() replacement is not a generic replacement as it
-** does not handle all of the semantics of the real link(2).
-**
-** Parameters:
-** source -- pathname of existing file.
-** target -- pathname of link (clone) to be created.
-**
-** Returns:
-** 0 -- success.
-** -1 -- failure, see errno for details.
-*/
-
-int
-link(source, target)
- const char *source;
- const char *target;
-{
- int save_errno;
- int sff;
- int src = -1, dst = -1;
- ssize_t readlen;
- ssize_t writelen;
- char buf[BUFSIZ];
- struct stat st;
-
- sff = SFF_REGONLY|SFF_OPENASROOT;
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
-
- /* Open the original file */
- src = safeopen((char *)source, O_RDONLY, 0, sff);
- if (src < 0)
- goto fail;
-
- /* Obtain the size and the mode */
- if (fstat(src, &st) < 0)
- goto fail;
-
- /* Create the duplicate copy */
- sff &= ~SFF_NOLOCK;
- sff |= SFF_CREAT;
- dst = safeopen((char *)target, O_CREAT|O_EXCL|O_WRONLY,
- st.st_mode, sff);
- if (dst < 0)
- goto fail;
-
- /* Copy all of the bytes one buffer at a time */
- while ((readlen = read(src, &buf, sizeof(buf))) > 0)
- {
- ssize_t left = readlen;
- char *p = buf;
-
- while (left > 0 &&
- (writelen = write(dst, p, (size_t) left)) >= 0)
- {
- left -= writelen;
- p += writelen;
- }
- if (writelen < 0)
- break;
- }
-
- /* Any trouble reading? */
- if (readlen < 0 || writelen < 0)
- goto fail;
-
- /* Close the input file */
- if (close(src) < 0)
- {
- src = -1;
- goto fail;
- }
- src = -1;
-
- /* Close the output file */
- if (close(dst) < 0)
- {
- /* don't set dst = -1 here so we unlink the file */
- goto fail;
- }
-
- /* Success */
- return 0;
-
- fail:
- save_errno = errno;
- if (src >= 0)
- (void) close(src);
- if (dst >= 0)
- {
- (void) unlink(target);
- (void) close(dst);
- }
- errno = save_errno;
- return -1;
-}
-#endif /* NEEDLINK */
-
-/*
-** Compile-Time options
-*/
-
-char *CompileOptions[] =
-{
-#if ALLOW_255
- "ALLOW_255",
-#endif /* ALLOW_255 */
-#if NAMED_BIND
-# if DNSMAP
- "DNSMAP",
-# endif /* DNSMAP */
-#endif /* NAMED_BIND */
-#if EGD
- "EGD",
-#endif /* EGD */
-#if HESIOD
- "HESIOD",
-#endif /* HESIOD */
-#if HES_GETMAILHOST
- "HES_GETMAILHOST",
-#endif /* HES_GETMAILHOST */
-#if LDAPMAP
- "LDAPMAP",
-#endif /* LDAPMAP */
-#if LDAP_REFERRALS
- "LDAP_REFERRALS",
-#endif /* LDAP_REFERRALS */
-#if LOG
- "LOG",
-#endif /* LOG */
-#if MAP_NSD
- "MAP_NSD",
-#endif /* MAP_NSD */
-#if MAP_REGEX
- "MAP_REGEX",
-#endif /* MAP_REGEX */
-#if MATCHGECOS
- "MATCHGECOS",
-#endif /* MATCHGECOS */
-#if MILTER
- "MILTER",
-#endif /* MILTER */
-#if MIME7TO8
- "MIME7TO8",
-#endif /* MIME7TO8 */
-#if MIME7TO8_OLD
- "MIME7TO8_OLD",
-#endif /* MIME7TO8_OLD */
-#if MIME8TO7
- "MIME8TO7",
-#endif /* MIME8TO7 */
-#if NAMED_BIND
- "NAMED_BIND",
-#endif /* NAMED_BIND */
-#if NDBM
- "NDBM",
-#endif /* NDBM */
-#if NETINET
- "NETINET",
-#endif /* NETINET */
-#if NETINET6
- "NETINET6",
-#endif /* NETINET6 */
-#if NETINFO
- "NETINFO",
-#endif /* NETINFO */
-#if NETISO
- "NETISO",
-#endif /* NETISO */
-#if NETNS
- "NETNS",
-#endif /* NETNS */
-#if NETUNIX
- "NETUNIX",
-#endif /* NETUNIX */
-#if NETX25
- "NETX25",
-#endif /* NETX25 */
-#if NEWDB
- "NEWDB",
-#endif /* NEWDB */
-#if NIS
- "NIS",
-#endif /* NIS */
-#if NISPLUS
- "NISPLUS",
-#endif /* NISPLUS */
-#if NO_DH
- "NO_DH",
-#endif /* NO_DH */
-#if PH_MAP
- "PH_MAP",
-#endif /* PH_MAP */
-#ifdef PICKY_HELO_CHECK
- "PICKY_HELO_CHECK",
-#endif /* PICKY_HELO_CHECK */
-#if PIPELINING
- "PIPELINING",
-#endif /* PIPELINING */
-#if SASL
-# if SASL >= 20000
- "SASLv2",
-# else /* SASL >= 20000 */
- "SASL",
-# endif /* SASL >= 20000 */
-#endif /* SASL */
-#if SCANF
- "SCANF",
-#endif /* SCANF */
-#if SM_LDAP_ERROR_ON_MISSING_ARGS
- "SM_LDAP_ERROR_ON_MISSING_ARGS",
-#endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
-#if SMTPDEBUG
- "SMTPDEBUG",
-#endif /* SMTPDEBUG */
-#if SOCKETMAP
- "SOCKETMAP",
-#endif /* SOCKETMAP */
-#if STARTTLS
- "STARTTLS",
-#endif /* STARTTLS */
-#if SUID_ROOT_FILES_OK
- "SUID_ROOT_FILES_OK",
-#endif /* SUID_ROOT_FILES_OK */
-#if TCPWRAPPERS
- "TCPWRAPPERS",
-#endif /* TCPWRAPPERS */
-#if TLS_NO_RSA
- "TLS_NO_RSA",
-#endif /* TLS_NO_RSA */
-#if TLS_VRFY_PER_CTX
- "TLS_VRFY_PER_CTX",
-#endif /* TLS_VRFY_PER_CTX */
-#if USERDB
- "USERDB",
-#endif /* USERDB */
-#if USE_LDAP_INIT
- "USE_LDAP_INIT",
-#endif /* USE_LDAP_INIT */
-#if USE_TTYPATH
- "USE_TTYPATH",
-#endif /* USE_TTYPATH */
-#if XDEBUG
- "XDEBUG",
-#endif /* XDEBUG */
-#if XLA
- "XLA",
-#endif /* XLA */
- NULL
-};
-
-
-/*
-** OS compile options.
-*/
-
-char *OsCompileOptions[] =
-{
-#if ADDRCONFIG_IS_BROKEN
- "ADDRCONFIG_IS_BROKEN",
-#endif /* ADDRCONFIG_IS_BROKEN */
-#ifdef AUTO_NETINFO_HOSTS
- "AUTO_NETINFO_HOSTS",
-#endif /* AUTO_NETINFO_HOSTS */
-#ifdef AUTO_NIS_ALIASES
- "AUTO_NIS_ALIASES",
-#endif /* AUTO_NIS_ALIASES */
-#if BROKEN_RES_SEARCH
- "BROKEN_RES_SEARCH",
-#endif /* BROKEN_RES_SEARCH */
-#ifdef BSD4_4_SOCKADDR
- "BSD4_4_SOCKADDR",
-#endif /* BSD4_4_SOCKADDR */
-#if BOGUS_O_EXCL
- "BOGUS_O_EXCL",
-#endif /* BOGUS_O_EXCL */
-#if DEC_OSF_BROKEN_GETPWENT
- "DEC_OSF_BROKEN_GETPWENT",
-#endif /* DEC_OSF_BROKEN_GETPWENT */
-#if FAST_PID_RECYCLE
- "FAST_PID_RECYCLE",
-#endif /* FAST_PID_RECYCLE */
-#if HASCLOSEFROM
- "HASCLOSEFROM",
-#endif /* HASCLOSEFROM */
-#if HASFCHOWN
- "HASFCHOWN",
-#endif /* HASFCHOWN */
-#if HASFCHMOD
- "HASFCHMOD",
-#endif /* HASFCHMOD */
-#if HASFDWALK
- "HASFDWALK",
-#endif /* HASFDWALK */
-#if HASFLOCK
- "HASFLOCK",
-#endif /* HASFLOCK */
-#if HASGETDTABLESIZE
- "HASGETDTABLESIZE",
-#endif /* HASGETDTABLESIZE */
-#if HASGETUSERSHELL
- "HASGETUSERSHELL",
-#endif /* HASGETUSERSHELL */
-#if HASINITGROUPS
- "HASINITGROUPS",
-#endif /* HASINITGROUPS */
-#if HASLDAPGETALIASBYNAME
- "HASLDAPGETALIASBYNAME",
-#endif /* HASLDAPGETALIASBYNAME */
-#if HASLSTAT
- "HASLSTAT",
-#endif /* HASLSTAT */
-#if HASNICE
- "HASNICE",
-#endif /* HASNICE */
-#if HASRANDOM
- "HASRANDOM",
-#endif /* HASRANDOM */
-#if HASRRESVPORT
- "HASRRESVPORT",
-#endif /* HASRRESVPORT */
-#if HASSETEGID
- "HASSETEGID",
-#endif /* HASSETEGID */
-#if HASSETLOGIN
- "HASSETLOGIN",
-#endif /* HASSETLOGIN */
-#if HASSETREGID
- "HASSETREGID",
-#endif /* HASSETREGID */
-#if HASSETRESGID
- "HASSETRESGID",
-#endif /* HASSETRESGID */
-#if HASSETREUID
- "HASSETREUID",
-#endif /* HASSETREUID */
-#if HASSETRLIMIT
- "HASSETRLIMIT",
-#endif /* HASSETRLIMIT */
-#if HASSETSID
- "HASSETSID",
-#endif /* HASSETSID */
-#if HASSETUSERCONTEXT
- "HASSETUSERCONTEXT",
-#endif /* HASSETUSERCONTEXT */
-#if HASSETVBUF
- "HASSETVBUF",
-#endif /* HASSETVBUF */
-#if HAS_ST_GEN
- "HAS_ST_GEN",
-#endif /* HAS_ST_GEN */
-#if HASSRANDOMDEV
- "HASSRANDOMDEV",
-#endif /* HASSRANDOMDEV */
-#if HASURANDOMDEV
- "HASURANDOMDEV",
-#endif /* HASURANDOMDEV */
-#if HASSTRERROR
- "HASSTRERROR",
-#endif /* HASSTRERROR */
-#if HASULIMIT
- "HASULIMIT",
-#endif /* HASULIMIT */
-#if HASUNAME
- "HASUNAME",
-#endif /* HASUNAME */
-#if HASUNSETENV
- "HASUNSETENV",
-#endif /* HASUNSETENV */
-#if HASWAITPID
- "HASWAITPID",
-#endif /* HASWAITPID */
-#if IDENTPROTO
- "IDENTPROTO",
-#endif /* IDENTPROTO */
-#if IP_SRCROUTE
- "IP_SRCROUTE",
-#endif /* IP_SRCROUTE */
-#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
- "LOCK_ON_OPEN",
-#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
-#if MILTER_NO_NAGLE
- "MILTER_NO_NAGLE ",
-#endif /* MILTER_NO_NAGLE */
-#if NEEDFSYNC
- "NEEDFSYNC",
-#endif /* NEEDFSYNC */
-#if NEEDLINK
- "NEEDLINK",
-#endif /* NEEDLINK */
-#if NEEDLOCAL_HOSTNAME_LENGTH
- "NEEDLOCAL_HOSTNAME_LENGTH",
-#endif /* NEEDLOCAL_HOSTNAME_LENGTH */
-#if NEEDSGETIPNODE
- "NEEDSGETIPNODE",
-#endif /* NEEDSGETIPNODE */
-#if NEEDSTRSTR
- "NEEDSTRSTR",
-#endif /* NEEDSTRSTR */
-#if NEEDSTRTOL
- "NEEDSTRTOL",
-#endif /* NEEDSTRTOL */
-#ifdef NO_GETSERVBYNAME
- "NO_GETSERVBYNAME",
-#endif /* NO_GETSERVBYNAME */
-#if NOFTRUNCATE
- "NOFTRUNCATE",
-#endif /* NOFTRUNCATE */
-#if REQUIRES_DIR_FSYNC
- "REQUIRES_DIR_FSYNC",
-#endif /* REQUIRES_DIR_FSYNC */
-#if RLIMIT_NEEDS_SYS_TIME_H
- "RLIMIT_NEEDS_SYS_TIME_H",
-#endif /* RLIMIT_NEEDS_SYS_TIME_H */
-#if SAFENFSPATHCONF
- "SAFENFSPATHCONF",
-#endif /* SAFENFSPATHCONF */
-#if SECUREWARE
- "SECUREWARE",
-#endif /* SECUREWARE */
-#if SHARE_V1
- "SHARE_V1",
-#endif /* SHARE_V1 */
-#if SIOCGIFCONF_IS_BROKEN
- "SIOCGIFCONF_IS_BROKEN",
-#endif /* SIOCGIFCONF_IS_BROKEN */
-#if SIOCGIFNUM_IS_BROKEN
- "SIOCGIFNUM_IS_BROKEN",
-#endif /* SIOCGIFNUM_IS_BROKEN */
-#if SNPRINTF_IS_BROKEN
- "SNPRINTF_IS_BROKEN",
-#endif /* SNPRINTF_IS_BROKEN */
-#if SO_REUSEADDR_IS_BROKEN
- "SO_REUSEADDR_IS_BROKEN",
-#endif /* SO_REUSEADDR_IS_BROKEN */
-#if SYS5SETPGRP
- "SYS5SETPGRP",
-#endif /* SYS5SETPGRP */
-#if SYSTEM5
- "SYSTEM5",
-#endif /* SYSTEM5 */
-#if USE_DOUBLE_FORK
- "USE_DOUBLE_FORK",
-#endif /* USE_DOUBLE_FORK */
-#if USE_ENVIRON
- "USE_ENVIRON",
-#endif /* USE_ENVIRON */
-#if USE_SA_SIGACTION
- "USE_SA_SIGACTION",
-#endif /* USE_SA_SIGACTION */
-#if USE_SIGLONGJMP
- "USE_SIGLONGJMP",
-#endif /* USE_SIGLONGJMP */
-#if USEGETCONFATTR
- "USEGETCONFATTR",
-#endif /* USEGETCONFATTR */
-#if USESETEUID
- "USESETEUID",
-#endif /* USESETEUID */
-#ifdef USESYSCTL
- "USESYSCTL",
-#endif /* USESYSCTL */
-#if USING_NETSCAPE_LDAP
- "USING_NETSCAPE_LDAP",
-#endif /* USING_NETSCAPE_LDAP */
-#ifdef WAITUNION
- "WAITUNION",
-#endif /* WAITUNION */
- NULL
-};
-
-/*
-** FFR compile options.
-*/
-
-char *FFRCompileOptions[] =
-{
-#if _FFR_ADDR_TYPE_MODES
- /* more info in {addr_type}, requires m4 changes! */
- "_FFR_ADDR_TYPE_MODES",
-#endif /* _FFR_ADDR_TYPE_MODES */
-#if _FFR_ALLOW_SASLINFO
- /* DefaultAuthInfo can be specified by user. */
- /* DefaultAuthInfo doesn't really work in 8.13 anymore. */
- "_FFR_ALLOW_SASLINFO",
-#endif /* _FFR_ALLOW_SASLINFO */
-#if _FFR_BESTMX_BETTER_TRUNCATION
- /* Better truncation of list of MX records for dns map. */
- "_FFR_BESTMX_BETTER_TRUNCATION",
-#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
-#if _FFR_CATCH_BROKEN_MTAS
- /* Deal with MTAs that send a reply during the DATA phase. */
- "_FFR_CATCH_BROKEN_MTAS",
-#endif /* _FFR_CATCH_BROKEN_MTAS */
-#if _FFR_CHK_QUEUE
- /* Stricter checks about queue directory permissions. */
- "_FFR_CHK_QUEUE",
-#endif /* _FFR_CHK_QUEUE */
-#if _FFR_CLIENT_SIZE
- /* Don't try to send mail if its size exceeds SIZE= of server. */
- "_FFR_CLIENT_SIZE",
-#endif /* _FFR_CLIENT_SIZE */
-#if _FFR_CRLPATH
- /* CRLPath; needs documentation; Al Smith */
- "_FFR_CRLPATH",
-#endif /* _FFR_CRLPATH */
-#if _FFR_DAEMON_NETUNIX
- /* Allow local (not just TCP) socket connection to server. */
- "_FFR_DAEMON_NETUNIX",
-#endif /* _FFR_DAEMON_NETUNIX */
-#if _FFR_DEPRECATE_MAILER_FLAG_I
- /* What it says :-) */
- "_FFR_DEPRECATE_MAILER_FLAG_I",
-#endif /* _FFR_DEPRECATE_MAILER_FLAG_I */
-#if _FFR_DM_ONE
- /* deliver first TA in background, then queue */
- "_FFR_DM_ONE",
-#endif /* _FFR_DM_ONE */
-#if _FFR_DIGUNIX_SAFECHOWN
- /* Properly set SAFECHOWN (include/sm/conf.h) for Digital UNIX */
-/* Problem noted by Anne Bennett of Concordia University */
- "_FFR_DIGUNIX_SAFECHOWN",
-#endif /* _FFR_DIGUNIX_SAFECHOWN */
-#if _FFR_DNSMAP_ALIASABLE
- /* Allow dns map type to be used for aliases. */
-/* Don Lewis of TDK */
- "_FFR_DNSMAP_ALIASABLE",
-#endif /* _FFR_DNSMAP_ALIASABLE */
-#if _FFR_DONTLOCKFILESFORREAD_OPTION
- /* Enable DontLockFilesForRead option. */
- "_FFR_DONTLOCKFILESFORREAD_OPTION",
-#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
-#if _FFR_DOTTED_USERNAMES
- /* Allow usernames with '.' */
- "_FFR_DOTTED_USERNAMES",
-#endif /* _FFR_DOTTED_USERNAMES */
-#if _FFR_DPO_CS
- /*
- ** Make DaemonPortOptions case sensitive.
- ** For some unknown reasons the code converted every option
- ** to uppercase (first letter only, as that's the only one that
- ** is actually checked). This prevented all new lower case options
- ** from working...
- ** The documentation doesn't say anything about case (in)sensitivity,
- ** which means it should be case sensitive by default,
- ** but it's not a good idea to change this within a patch release,
- ** so let's delay this to 8.15.
- */
-
- "_FFR_DPO_CS",
-#endif /* _FFR_DPO_CS */
-#if _FFR_DPRINTF_MAP
- /* dprintf map for logging */
- "_FFR_DPRINTF_MAP",
-#endif /* _FFR_DPRINTF_MAP */
-#if _FFR_DROP_TRUSTUSER_WARNING
- /*
- ** Don't issue this warning:
- ** "readcf: option TrustedUser may cause problems on systems
- ** which do not support fchown() if UseMSP is not set.
- */
-
- "_FFR_DROP_TRUSTUSER_WARNING",
-#endif /* _FFR_DROP_TRUSTUSER_WARNING */
-#if _FFR_EIGHT_BIT_ADDR_OK
- /* EightBitAddrOK: allow 8-bit e-mail addresses */
- "_FFR_EIGHT_BIT_ADDR_OK",
-#endif /* _FFR_EIGHT_BIT_ADDR_OK */
-#if _FFR_EXTRA_MAP_CHECK
- /* perform extra checks on $( $) in R lines */
- "_FFR_EXTRA_MAP_CHECK",
-#endif /* _FFR_EXTRA_MAP_CHECK */
-#if _FFR_GETHBN_ExFILE
- /*
- ** According to Motonori Nakamura some gethostbyname()
- ** implementations (TurboLinux?) may (temporarily) fail
- ** due to a lack of file discriptors. Enabling this FFR
- ** will check errno for EMFILE and ENFILE and in case of a match
- ** cause a temporary error instead of a permanent error.
- ** The right solution is of course to file a bug against those
- ** systems such that they actually set h_errno = TRY_AGAIN.
- */
-
- "_FFR_GETHBN_ExFILE",
-#endif /* _FFR_GETHBN_ExFILE */
-#if _FFR_FIX_DASHT
- /*
- ** If using -t, force not sending to argv recipients, even
- ** if they are mentioned in the headers.
- */
-
- "_FFR_FIX_DASHT",
-#endif /* _FFR_FIX_DASHT */
-#if _FFR_FORWARD_SYSERR
- /* Cause a "syserr" if forward file isn't "safe". */
- "_FFR_FORWARD_SYSERR",
-#endif /* _FFR_FORWARD_SYSERR */
-#if _FFR_GEN_ORCPT
- /* Generate a ORCPT DSN arg if not already provided */
- "_FFR_GEN_ORCPT",
-#endif /* _FFR_GEN_ORCPT */
-#if _FFR_GROUPREADABLEAUTHINFOFILE
- /* Allow group readable DefaultAuthInfo file. */
- "_FFR_GROUPREADABLEAUTHINFOFILE",
-#endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
-#if _FFR_HANDLE_ISO8859_GECOS
- /*
- ** Allow ISO 8859 characters in GECOS field: replace them
- ** ith ASCII "equivalent".
- */
-
-/* Peter Eriksson of Linkopings universitet */
- "_FFR_HANDLE_ISO8859_GECOS",
-#endif /* _FFR_HANDLE_ISO8859_GECOS */
-#if _FFR_HPUX_NSSWITCH
- /* Use nsswitch on HP-UX */
- "_FFR_HPUX_NSSWITCH",
-#endif /* _FFR_HPUX_NSSWITCH */
-#if _FFR_IGNORE_BOGUS_ADDR
- /* Ignore addresses for which prescan() failed */
- "_FFR_IGNORE_BOGUS_ADDR",
-#endif /* _FFR_IGNORE_BOGUS_ADDR */
-#if _FFR_IGNORE_EXT_ON_HELO
- /* Ignore extensions offered in response to HELO */
- "_FFR_IGNORE_EXT_ON_HELO",
-#endif /* _FFR_IGNORE_EXT_ON_HELO */
-#if _FFR_MAXDATASIZE
- /*
- ** It is possible that a header is larger than MILTER_CHUNK_SIZE,
- ** hence this shouldn't be used as limit for milter communication.
- ** see also libmilter/comm.c
- ** Gurusamy Sarathy of ActiveState
- */
-
- "_FFR_MAXDATASIZE",
-#endif /* _FFR_MAXDATASIZE */
-#if _FFR_MAX_FORWARD_ENTRIES
- /* Try to limit number of .forward entries */
- /* (doesn't work) */
-/* Randall S. Winchester of the University of Maryland */
- "_FFR_MAX_FORWARD_ENTRIES",
-#endif /* _FFR_MAX_FORWARD_ENTRIES */
-#if _FFR_MAX_SLEEP_TIME
- /* Limit sleep(2) time in libsm/clock.c */
- "_FFR_MAX_SLEEP_TIME",
-#endif /* _FFR_MAX_SLEEP_TIME */
-#if _FFR_MEMSTAT
- /* Check free memory */
- "_FFR_MEMSTAT",
-#endif /* _FFR_MEMSTAT */
-#if _FFR_MILTER_CHECK
- "_FFR_MILTER_CHECK",
-#endif /* _FFR_MILTER_CHECK */
-#if _FFR_MILTER_CONVERT_ALL_LF_TO_CRLF
- /*
- ** milter_body() uses the same conversion algorithm as putbody()
- ** to translate the "local" df format (\n) to SMTP format (\r\n).
- ** However, putbody() and mime8to7() use different conversion
- ** algorithms.
- ** If the input date does not follow the SMTP standard
- ** (e.g., if it has "naked \r"s), then the output from putbody()
- ** and mime8to7() will most likely be different.
- ** By turning on this FFR milter_body() will try to "imitate"
- ** mime8to7().
- ** Note: there is no (simple) way to deal with both conversions
- ** in a consistent manner. Moreover, as the "GiGo" principle applies,
- ** it's not really worth to fix it.
- */
-
- "_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF",
-#endif /* _FFR_MILTER_CONVERT_ALL_LF_TO_CRLF */
-#if _FFR_MILTER_CHECK_REJECTIONS_TOO
- /*
- ** Also send RCPTs that are rejected by check_rcpt to a milter
- ** (if requested during option negotiation).
- */
-
- "_FFR_MILTER_CHECK_REJECTIONS_TOO",
-#endif /* _FFR_MILTER_CHECK_REJECTIONS_TOO */
-#if _FFR_MIME7TO8_OLD
- /* Old mime7to8 code, the new is broken for at least one example. */
- "_FFR_MIME7TO8_OLD",
-#endif /* _FFR_MAX_SLEEP_TIME */
-#if _FFR_MORE_MACROS
- /* allow more long macro names ("unprintable" characters). */
- "_FFR_MORE_MACROS",
-#endif /* _FFR_MORE_MACROS */
-#if _FFR_MSG_ACCEPT
- /* allow to override "Message accepted for delivery" */
- "_FFR_MSG_ACCEPT",
-#endif /* _FFR_MSG_ACCEPT */
-#if _FFR_NODELAYDSN_ON_HOLD
- /* Do not issue a DELAY DSN for mailers that use the hold flag. */
-/* Steven Pitzl */
- "_FFR_NODELAYDSN_ON_HOLD",
-#endif /* _FFR_NODELAYDSN_ON_HOLD */
-#if _FFR_NO_PIPE
- /* Disable PIPELINING, delay client if used. */
- "_FFR_NO_PIPE",
-#endif /* _FFR_NO_PIPE */
-#if _FFR_LDAP_NETWORK_TIMEOUT
- /* set LDAP_OPT_NETWORK_TIMEOUT if available (-c) */
- "_FFR_LDAP_NETWORK_TIMEOUT",
-#endif /* _FFR_LDAP_NETWORK_TIMEOUT */
-#if _FFR_LOG_NTRIES
- /* log ntries=, from Nik Clayton of FreeBSD */
- "_FFR_LOG_NTRIES",
-#endif /* _FFR_LOG_NTRIES */
-#if _FFR_QF_PARANOIA
- "_FFR_QF_PARANOIA",
-#endif /* _FFR_QF_PARANOIA */
-#if _FFR_QUEUEDELAY
- /* Exponential queue delay; disabled in 8.13 since it isn't used. */
- "_FFR_QUEUEDELAY",
-#endif /* _FFR_QUEUEDELAY */
-#if _FFR_QUEUE_GROUP_SORTORDER
- /* Allow QueueSortOrder per queue group. */
-/* XXX: Still need to actually use qgrp->qg_sortorder */
- "_FFR_QUEUE_GROUP_SORTORDER",
-#endif /* _FFR_QUEUE_GROUP_SORTORDER */
-#if _FFR_QUEUE_MACRO
- /* Define {queue} macro. */
- "_FFR_QUEUE_MACRO",
-#endif /* _FFR_QUEUE_MACRO */
-#if _FFR_QUEUE_RUN_PARANOIA
- /* Additional checks when doing queue runs; interval of checks */
- "_FFR_QUEUE_RUN_PARANOIA",
-#endif /* _FFR_QUEUE_RUN_PARANOIA */
-#if _FFR_QUEUE_SCHED_DBG
- /* Debug output for the queue scheduler. */
- "_FFR_QUEUE_SCHED_DBG",
-#endif /* _FFR_QUEUE_SCHED_DBG */
-#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.
- */
-
- "_FFR_REDIRECTEMPTY",
-#endif /* _FFR_REDIRECTEMPTY */
-#if _FFR_RESET_MACRO_GLOBALS
- /* Allow macro 'j' to be set dynamically via rulesets. */
- "_FFR_RESET_MACRO_GLOBALS",
-#endif /* _FFR_RESET_MACRO_GLOBALS */
-#if _FFR_RHS
- /* Random shuffle for queue sorting. */
- "_FFR_RHS",
-#endif /* _FFR_RHS */
-#if _FFR_RUNPQG
- /*
- ** allow -qGqueue_group -qp to work, i.e.,
- ** restrict a persistent queue runner to a queue group.
- */
-
- "_FFR_RUNPQG",
-#endif /* _FFR_RUNPQG */
-#if _FFR_SESSID
- /* session id (for logging) */
- "_FFR_SESSID",
-#endif /* _FFR_SESSID */
-#if _FFR_SHM_STATUS
- /* Donated code (unused). */
- "_FFR_SHM_STATUS",
-#endif /* _FFR_SHM_STATUS */
-#if _FFR_LDAP_SINGLEDN
- /*
- ** The LDAP database map code in Sendmail 8.12.10, when
- ** given the -1 switch, would match only a single DN,
- ** but was able to return multiple attributes for that
- ** DN. In Sendmail 8.13 this "bug" was corrected to
- ** only return if exactly one attribute matched.
- **
- ** Unfortunately, our configuration uses the former
- ** behaviour. Attached is a relatively simple patch
- ** to 8.13.4 which adds a -2 switch (for lack of a
- ** better option) which returns the single dn/multiple
- ** attributes.
- **
- ** Jeffrey T. Eaton, Carnegie-Mellon University
- */
-
- "_FFR_LDAP_SINGLEDN",
-#endif /* _FFR_LDAP_SINGLEDN */
-#if _FFR_SKIP_DOMAINS
- /* process every N'th domain instead of every N'th message */
- "_FFR_SKIP_DOMAINS",
-#endif /* _FFR_SKIP_DOMAINS */
-#if _FFR_SLEEP_USE_SELECT
- /* Use select(2) in libsm/clock.c to emulate sleep(2) */
- "_FFR_SLEEP_USE_SELECT ",
-#endif /* _FFR_SLEEP_USE_SELECT */
-#if _FFR_SPT_ALIGN
- /*
- ** It looks like the Compaq Tru64 5.1A now aligns argv and envp to 64
- ** bit alignment, so unless each piece of argv and envp is a multiple
- ** of 8 bytes (including terminating NULL), initsetproctitle() won't
- ** use any of the space beyond argv[0]. Be sure to set SPT_ALIGN_SIZE
- ** if you use this FFR.
- */
-
-/* Chris Adams of HiWAAY Informations Services */
- "_FFR_SPT_ALIGN",
-#endif /* _FFR_SPT_ALIGN */
-#if _FFR_SS_PER_DAEMON
- /* SuperSafe per DaemonPortOptions: 'T' (better letter?) */
- "_FFR_SS_PER_DAEMON",
-#endif /* _FFR_SS_PER_DAEMON */
-#if _FFR_TIMERS
- /* Donated code (unused). */
- "_FFR_TIMERS",
-#endif /* _FFR_TIMERS */
-#if _FFR_TLS_1
- /* More STARTTLS options, e.g., secondary certs. */
- "_FFR_TLS_1",
-#endif /* _FFR_TLS_1 */
-#if _FFR_TRUSTED_QF
- /*
- ** If we don't own the file mark it as unsafe.
- ** However, allow TrustedUser to own it as well
- ** in case TrustedUser manipulates the queue.
- */
-
- "_FFR_TRUSTED_QF",
-#endif /* _FFR_TRUSTED_QF */
-#if _FFR_USE_SEM_LOCKING
- "_FFR_USE_SEM_LOCKING",
-#endif /* _FFR_USE_SEM_LOCKING */
-#if _FFR_USE_SETLOGIN
- /* Use setlogin() */
-/* Peter Philipp */
- "_FFR_USE_SETLOGIN",
-#endif /* _FFR_USE_SETLOGIN */
- NULL
-};
-
diff --git a/contrib/sendmail/src/conf.h b/contrib/sendmail/src/conf.h
deleted file mode 100644
index f1386c4..0000000
--- a/contrib/sendmail/src/conf.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * 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.
- *
- *
- * $Id: conf.h,v 8.574 2006/11/29 00:36:06 ca Exp $
- */
-
-/*
-** CONF.H -- All user-configurable parameters for sendmail
-**
-** Send updates to sendmail@Sendmail.ORG so they will be
-** included in the next release.
-*/
-
-#ifndef CONF_H
-#define CONF_H 1
-
-#ifdef __GNUC__
-struct rusage; /* forward declaration to get gcc to shut up in wait.h */
-#endif /* __GNUC__ */
-
-# include <sys/param.h>
-# include <sys/types.h>
-# include <sys/stat.h>
-# ifndef __QNX__
-/* in QNX this grabs bogus LOCK_* manifests */
-# include <sys/file.h>
-# endif /* ! __QNX__ */
-# include <sys/wait.h>
-# include <limits.h>
-# include <fcntl.h>
-# include <signal.h>
-# include <netdb.h>
-# include <pwd.h>
-# include <grp.h>
-
-/* make sure TOBUFSIZ isn't larger than system limit for size of exec() args */
-#ifdef ARG_MAX
-# if ARG_MAX > 4096
-# define SM_ARG_MAX 4096
-# else /* ARG_MAX > 4096 */
-# define SM_ARG_MAX ARG_MAX
-# endif /* ARG_MAX > 4096 */
-#else /* ARG_MAX */
-# define SM_ARG_MAX 4096
-#endif /* ARG_MAX */
-
-/**********************************************************************
-** Table sizes, etc....
-** There shouldn't be much need to change these....
-** If you do, be careful, none should be set anywhere near INT_MAX
-**********************************************************************/
-
-#define MAXLINE 2048 /* max line length */
-#if SASL
-# define MAXINPLINE 12288 /* max input line length (for AUTH) */
-#else /* SASL */
-# define MAXINPLINE MAXLINE /* max input line length */
-#endif /* SASL */
-#define MAXNAME 256 /* max length of a name */
-#ifndef MAXAUTHINFO
-# define MAXAUTHINFO 100 /* max length of authinfo token */
-#endif /* ! MAXAUTHINFO */
-#define MAXPV 256 /* max # of parms to mailers */
-#define MAXATOM 1000 /* max atoms per address */
-#define MAXRWSETS 200 /* max # of sets of rewriting rules */
-#define MAXPRIORITIES 25 /* max values for Precedence: field */
-#define MAXMXHOSTS 100 /* max # of MX records for one host */
-#define SMTPLINELIM 990 /* max SMTP line length */
-#define MAXUDBKEY 128 /* max size of a database key (udb only) */
-#define MAXKEY 1024 /* max size of a database key */
-#define MEMCHUNKSIZE 1024 /* chunk size for memory allocation */
-#define MAXUSERENVIRON 100 /* max envars saved, must be >= 3 */
-#define MAXMAPSTACK 12 /* max # of stacked or sequenced maps */
-#if MILTER
-# define MAXFILTERS 25 /* max # of milter filters */
-# define MAXFILTERMACROS 50 /* max # of macros per milter cmd */
-#endif /* MILTER */
-#define MAXSMTPARGS 20 /* max # of ESMTP args for MAIL/RCPT */
-#define MAXTOCLASS 8 /* max # of message timeout classes */
-#define MAXRESTOTYPES 3 /* max # of resolver timeout types */
-#define MAXMIMEARGS 20 /* max args in Content-Type: */
-#define MAXMIMENESTING 20 /* max MIME multipart nesting */
-#define QUEUESEGSIZE 1000 /* increment for queue size */
-
-#ifndef MAXNOOPCOMMANDS
-# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */
-#endif /* ! MAXNOOPCOMMANDS */
-
-/*
-** MAXQFNAME == 2 (size of "qf", "df" prefix)
-** + 8 (base 60 encoded date, time & sequence number)
-** + 10 (base 10 encoded 32 bit process id)
-** + 1 (terminating NUL character).
-*/
-
-#define MAXQFNAME 21 /* max qf file name length + 1 */
-#define MACBUFSIZE 4096 /* max expanded macro buffer size */
-#define TOBUFSIZE SM_ARG_MAX /* max buffer to hold address list */
-#define MAXSHORTSTR 203 /* max short string length */
-#define MAXMACNAMELEN 25 /* max macro name length */
-#define MAXMACROID 0377 /* max macro id number */
- /* Must match (BITMAPBITS - 1) */
-#ifndef MAXHDRSLEN
-# define MAXHDRSLEN (32 * 1024) /* max size of message headers */
-#endif /* ! MAXHDRSLEN */
-#define MAXDAEMONS 10 /* max number of ports to listen to */
-#ifndef MAXINTERFACES
-# define MAXINTERFACES 512 /* number of interfaces to probe */
-#endif /* MAXINTERFACES */
-#ifndef MAXSYMLINKS
-# define MAXSYMLINKS 32 /* max number of symlinks in a path */
-#endif /* ! MAXSYMLINKS */
-#define MAXLINKPATHLEN (MAXPATHLEN * MAXSYMLINKS) /* max link-expanded file */
-#define DATA_PROGRESS_TIMEOUT 300 /* how often to check DATA progress */
-#define ENHSCLEN 10 /* max len of enhanced status code */
-#define DEFAULT_MAX_RCPT 100 /* max number of RCPTs per envelope */
-#define MAXQUEUEGROUPS 50 /* max # of queue groups */
- /* must be less than BITMAPBITS for DoQueueRun */
-#define MAXWORKGROUPS 50 /* max # of work groups */
-#define MAXFILESYS BITMAPBITS /* max # of queue file systems
- * must be <= BITMAPBITS */
-#ifndef FILESYS_UPDATE_INTERVAL
-# define FILESYS_UPDATE_INTERVAL 300 /* how often to update FileSys table */
-#endif /* FILESYS_UPDATE_INTERVAL */
-
-#ifndef SM_DEFAULT_TTL
-# define SM_DEFAULT_TTL 3600 /* default TTL for services that don't have one */
-#endif /* SM_DEFAULT_TTL */
-
-#if SASL
-# ifndef AUTH_MECHANISMS
-# if STARTTLS
-# define AUTH_MECHANISMS "EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5"
-# else /* STARTTLS */
-# define AUTH_MECHANISMS "GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5"
-# endif /* STARTTLS */
-# endif /* ! AUTH_MECHANISMS */
-#endif /* SASL */
-
-/*
-** Default database permissions (alias, maps, etc.)
-** Used by sendmail and libsmdb
-*/
-
-#ifndef DBMMODE
-# define DBMMODE 0640
-#endif /* ! DBMMODE */
-
-/*
-** Value which means a uid or gid value should not change
-*/
-
-#ifndef NO_UID
-# define NO_UID -1
-#endif /* ! NO_UID */
-#ifndef NO_GID
-# define NO_GID -1
-#endif /* ! NO_GID */
-
-/**********************************************************************
-** Compilation options.
-** #define these to 1 if they are available;
-** #define them to 0 otherwise.
-** All can be overridden from Makefile.
-**********************************************************************/
-
-#ifndef NETINET
-# define NETINET 1 /* include internet support */
-#endif /* ! NETINET */
-
-#ifndef NETINET6
-# define NETINET6 0 /* do not include IPv6 support */
-#endif /* ! NETINET6 */
-
-#ifndef NETISO
-# define NETISO 0 /* do not include ISO socket support */
-#endif /* ! NETISO */
-
-#ifndef NAMED_BIND
-# define NAMED_BIND 1 /* use Berkeley Internet Domain Server */
-#endif /* ! NAMED_BIND */
-
-#ifndef XDEBUG
-# define XDEBUG 1 /* enable extended debugging */
-#endif /* ! XDEBUG */
-
-#ifndef MATCHGECOS
-# define MATCHGECOS 1 /* match user names from gecos field */
-#endif /* ! MATCHGECOS */
-
-#ifndef DSN
-# define DSN 1 /* include delivery status notification code */
-#endif /* ! DSN */
-
-#if !defined(USERDB) && (defined(NEWDB) || defined(HESIOD))
-# define USERDB 1 /* look in user database */
-#endif /* !defined(USERDB) && (defined(NEWDB) || defined(HESIOD)) */
-
-#ifndef MIME8TO7
-# define MIME8TO7 1 /* 8->7 bit MIME conversions */
-#endif /* ! MIME8TO7 */
-
-#ifndef MIME7TO8
-# define MIME7TO8 1 /* 7->8 bit MIME conversions */
-#endif /* ! MIME7TO8 */
-
-#if NAMED_BIND
-# ifndef DNSMAP
-# define DNSMAP 1 /* DNS map type */
-# endif /* ! DNSMAP */
-#endif /* NAMED_BIND */
-
-#ifndef PIPELINING
-# define PIPELINING 1 /* SMTP PIPELINING */
-#endif /* PIPELINING */
-
-/**********************************************************************
-** End of site-specific configuration.
-**********************************************************************/
-
-#include <sm/conf.h>
-
-#endif /* ! CONF_H */
diff --git a/contrib/sendmail/src/control.c b/contrib/sendmail/src/control.c
deleted file mode 100644
index 0b525f7..0000000
--- a/contrib/sendmail/src/control.c
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * Copyright (c) 1998-2004, 2006 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: control.c,v 8.128 2006/08/15 23:24:56 ca Exp $")
-
-#include <sm/fdset.h>
-
-/* values for cmd_code */
-#define CMDERROR 0 /* bad command */
-#define CMDRESTART 1 /* restart daemon */
-#define CMDSHUTDOWN 2 /* end daemon */
-#define CMDHELP 3 /* help */
-#define CMDSTATUS 4 /* daemon status */
-#define CMDMEMDUMP 5 /* dump memory, to find memory leaks */
-#define CMDMSTAT 6 /* daemon status, more info, tagged data */
-
-struct cmd
-{
- char *cmd_name; /* command name */
- int cmd_code; /* internal code, see below */
-};
-
-static struct cmd CmdTab[] =
-{
- { "help", CMDHELP },
- { "restart", CMDRESTART },
- { "shutdown", CMDSHUTDOWN },
- { "status", CMDSTATUS },
- { "memdump", CMDMEMDUMP },
- { "mstat", CMDMSTAT },
- { NULL, CMDERROR }
-};
-
-
-
-static void controltimeout __P((int));
-int ControlSocket = -1;
-
-/*
-** OPENCONTROLSOCKET -- create/open the daemon control named socket
-**
-** Creates and opens a named socket for external control over
-** the sendmail daemon.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** 0 if successful, -1 otherwise
-*/
-
-int
-opencontrolsocket()
-{
-# if NETUNIX
- int save_errno;
- int rval;
- long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
- struct sockaddr_un controladdr;
-
- if (ControlSocketName == NULL || *ControlSocketName == '\0')
- return 0;
-
- if (strlen(ControlSocketName) >= sizeof(controladdr.sun_path))
- {
- errno = ENAMETOOLONG;
- return -1;
- }
-
- rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName,
- sff, S_IRUSR|S_IWUSR, NULL);
-
- /* if not safe, don't create */
- if (rval != 0)
- {
- errno = rval;
- return -1;
- }
-
- ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0);
- if (ControlSocket < 0)
- return -1;
- if (SM_FD_SETSIZE > 0 && ControlSocket >= SM_FD_SETSIZE)
- {
- clrcontrol();
- errno = EINVAL;
- return -1;
- }
-
- (void) unlink(ControlSocketName);
- memset(&controladdr, '\0', sizeof(controladdr));
- controladdr.sun_family = AF_UNIX;
- (void) sm_strlcpy(controladdr.sun_path, ControlSocketName,
- sizeof(controladdr.sun_path));
-
- if (bind(ControlSocket, (struct sockaddr *) &controladdr,
- sizeof(controladdr)) < 0)
- {
- save_errno = errno;
- clrcontrol();
- errno = save_errno;
- return -1;
- }
-
- if (geteuid() == 0)
- {
- uid_t u = 0;
-
- if (RunAsUid != 0)
- u = RunAsUid;
- else if (TrustedUid != 0)
- u = TrustedUid;
-
- if (u != 0 &&
- chown(ControlSocketName, u, -1) < 0)
- {
- save_errno = errno;
- sm_syslog(LOG_ALERT, NOQID,
- "ownership change on %s to uid %d failed: %s",
- ControlSocketName, (int) u,
- sm_errstring(save_errno));
- message("050 ownership change on %s to uid %d failed: %s",
- ControlSocketName, (int) u,
- sm_errstring(save_errno));
- closecontrolsocket(true);
- errno = save_errno;
- return -1;
- }
- }
-
- if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0)
- {
- save_errno = errno;
- closecontrolsocket(true);
- errno = save_errno;
- return -1;
- }
-
- if (listen(ControlSocket, 8) < 0)
- {
- save_errno = errno;
- closecontrolsocket(true);
- errno = save_errno;
- return -1;
- }
-# endif /* NETUNIX */
- return 0;
-}
-/*
-** CLOSECONTROLSOCKET -- close the daemon control named socket
-**
-** Close a named socket.
-**
-** Parameters:
-** fullclose -- if set, close the socket and remove it;
-** otherwise, just remove it
-**
-** Returns:
-** none.
-*/
-
-void
-closecontrolsocket(fullclose)
- bool fullclose;
-{
-# if NETUNIX
- long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
-
- if (ControlSocket >= 0)
- {
- int rval;
-
- if (fullclose)
- {
- (void) close(ControlSocket);
- ControlSocket = -1;
- }
-
- rval = safefile(ControlSocketName, RunAsUid, RunAsGid,
- RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL);
-
- /* if not safe, don't unlink */
- if (rval != 0)
- return;
-
- if (unlink(ControlSocketName) < 0)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "Could not remove control socket: %s",
- sm_errstring(errno));
- return;
- }
- }
-# endif /* NETUNIX */
- return;
-}
-/*
-** CLRCONTROL -- reset the control connection
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** releases any resources used by the control interface.
-*/
-
-void
-clrcontrol()
-{
-# if NETUNIX
- if (ControlSocket >= 0)
- (void) close(ControlSocket);
- ControlSocket = -1;
-# endif /* NETUNIX */
-}
-/*
-** CONTROL_COMMAND -- read and process command from named socket
-**
-** Read and process the command from the opened socket.
-** Exits when done since it is running in a forked child.
-**
-** Parameters:
-** sock -- the opened socket from getrequests()
-** e -- the current envelope
-**
-** Returns:
-** none.
-*/
-
-static jmp_buf CtxControlTimeout;
-
-/* ARGSUSED0 */
-static void
-controltimeout(timeout)
- int timeout;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(CtxControlTimeout, 1);
-}
-
-void
-control_command(sock, e)
- int sock;
- ENVELOPE *e;
-{
- volatile int exitstat = EX_OK;
- SM_FILE_T *s = NULL;
- SM_EVENT *ev = NULL;
- SM_FILE_T *traffic;
- SM_FILE_T *oldout;
- char *cmd;
- char *p;
- struct cmd *c;
- char cmdbuf[MAXLINE];
- char inp[MAXLINE];
-
- sm_setproctitle(false, e, "control cmd read");
-
- 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 = sm_setevent(TimeOuts.to_control, controltimeout,
- TimeOuts.to_control);
- }
-
- s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &sock,
- SM_IO_RDWR, NULL);
- if (s == NULL)
- {
- int save_errno = errno;
-
- (void) close(sock);
- errno = save_errno;
- exit(EX_IOERR);
- }
- (void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL,
- SM_IO_NBF, SM_IO_BUFSIZ);
-
- if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof(inp)) == NULL)
- {
- (void) sm_io_close(s, SM_TIME_DEFAULT);
- exit(EX_IOERR);
- }
- (void) sm_io_flush(s, SM_TIME_DEFAULT);
-
- /* clean up end of line */
- fixcrlf(inp, true);
-
- sm_setproctitle(false, e, "control: %s", inp);
-
- /* break off command */
- for (p = inp; isascii(*p) && isspace(*p); p++)
- continue;
- cmd = cmdbuf;
- while (*p != '\0' &&
- !(isascii(*p) && isspace(*p)) &&
- 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->cmd_name != NULL; c++)
- {
- if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
- break;
- }
-
- switch (c->cmd_code)
- {
- case CMDHELP: /* get help */
- traffic = TrafficLogFile;
- TrafficLogFile = NULL;
- oldout = OutChannel;
- OutChannel = s;
- help("control", e);
- TrafficLogFile = traffic;
- OutChannel = oldout;
- break;
-
- case CMDRESTART: /* restart the daemon */
- (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
- exitstat = EX_RESTART;
- break;
-
- case CMDSHUTDOWN: /* kill the daemon */
- (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n");
- exitstat = EX_SHUTDOWN;
- break;
-
- case CMDSTATUS: /* daemon status */
- proc_list_probe();
- {
- int qgrp;
- long bsize;
- long free;
-
- /* XXX need to deal with different partitions */
- qgrp = e->e_qgrp;
- if (!ISVALIDQGRP(qgrp))
- qgrp = 0;
- free = freediskspace(Queue[qgrp]->qg_qdir, &bsize);
-
- /*
- ** Prevent overflow and don't lose
- ** precision (if bsize == 512)
- */
-
- if (free > 0)
- free = (long)((double) free *
- ((double) bsize / 1024));
-
- (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
- "%d/%d/%ld/%d\r\n",
- CurChildren, MaxChildren,
- free, getla());
- }
- proc_list_display(s, "");
- break;
-
- case CMDMSTAT: /* daemon status, extended, tagged format */
- proc_list_probe();
- (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
- "C:%d\r\nM:%d\r\nL:%d\r\n",
- CurChildren, MaxChildren,
- getla());
- printnqe(s, "Q:");
- disk_status(s, "D:");
- proc_list_display(s, "P:");
- break;
-
- case CMDMEMDUMP: /* daemon memory dump, to find memory leaks */
-# if SM_HEAP_CHECK
- /* dump the heap, if we are checking for memory leaks */
- if (sm_debug_active(&SmHeapCheck, 2))
- {
- sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1);
- }
- else
- {
- (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
- "Memory dump unavailable.\r\n");
- (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
- "To fix, run sendmail with -dsm_check_heap.4\r\n");
- }
-# else /* SM_HEAP_CHECK */
- (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
- "Memory dump unavailable.\r\n");
- (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
- "To fix, rebuild with -DSM_HEAP_CHECK\r\n");
-# endif /* SM_HEAP_CHECK */
- break;
-
- case CMDERROR: /* unknown command */
- (void) sm_io_fprintf(s, SM_TIME_DEFAULT,
- "Bad command (%s)\r\n", cmdbuf);
- break;
- }
- (void) sm_io_close(s, SM_TIME_DEFAULT);
- if (ev != NULL)
- sm_clrevent(ev);
- exit(exitstat);
-}
diff --git a/contrib/sendmail/src/convtime.c b/contrib/sendmail/src/convtime.c
deleted file mode 100644
index 36edc1a..0000000
--- a/contrib/sendmail/src/convtime.c
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: convtime.c,v 8.39 2001/09/11 04:05:13 gshapiro Exp $")
-
-/*
-** CONVTIME -- convert time
-**
-** Takes a time as an ascii string with a trailing character
-** giving units:
-** s -- seconds
-** m -- minutes
-** h -- hours
-** d -- days (default)
-** w -- weeks
-** For example, "3d12h" is three and a half days.
-**
-** Parameters:
-** p -- pointer to ascii time.
-** units -- default units if none specified.
-**
-** Returns:
-** time in seconds.
-**
-** Side Effects:
-** none.
-*/
-
-time_t
-convtime(p, units)
- char *p;
- int units;
-{
- register time_t t, r;
- register char c;
- bool pos = true;
-
- r = 0;
- if (sm_strcasecmp(p, "now") == 0)
- return NOW;
- if (*p == '-')
- {
- pos = false;
- ++p;
- }
- while (*p != '\0')
- {
- t = 0;
- while ((c = *p++) != '\0' && isascii(c) && isdigit(c))
- t = t * 10 + (c - '0');
- if (c == '\0')
- {
- c = units;
- p--;
- }
- else if (strchr("wdhms", c) == NULL)
- {
- usrerr("Invalid time unit `%c'", c);
- c = units;
- }
- switch (c)
- {
- 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;
- }
- r += t;
- }
-
- return pos ? r : -r;
-}
-/*
-** PINTVL -- produce printable version of a time interval
-**
-** Parameters:
-** intvl -- the interval to be converted
-** brief -- if true, print this in an extremely compact form
-** (basically used for logging).
-**
-** Returns:
-** A pointer to a string version of intvl suitable for
-** printing or framing.
-**
-** Side Effects:
-** none.
-**
-** Warning:
-** The string returned is in a static buffer.
-*/
-
-#define PLURAL(n) ((n) == 1 ? "" : "s")
-
-char *
-pintvl(intvl, brief)
- time_t intvl;
- bool brief;
-{
- static char buf[256];
- register char *p;
- int wk, dy, hr, mi, se;
-
- if (intvl == 0 && !brief)
- return "zero seconds";
- if (intvl == NOW)
- return "too long";
-
- /* decode the interval into weeks, days, hours, minutes, seconds */
- se = intvl % 60;
- intvl /= 60;
- mi = intvl % 60;
- intvl /= 60;
- hr = intvl % 24;
- intvl /= 24;
- if (brief)
- {
- dy = intvl;
- wk = 0;
- }
- else
- {
- dy = intvl % 7;
- intvl /= 7;
- wk = intvl;
- }
-
- /* now turn it into a sexy form */
- p = buf;
- if (brief)
- {
- if (dy > 0)
- {
- (void) sm_snprintf(p, SPACELEFT(buf, p), "%d+", dy);
- p += strlen(p);
- }
- (void) sm_snprintf(p, SPACELEFT(buf, p), "%02d:%02d:%02d",
- hr, mi, se);
- return buf;
- }
-
- /* use the verbose form */
- if (wk > 0)
- {
- (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d week%s", wk,
- PLURAL(wk));
- p += strlen(p);
- }
- if (dy > 0)
- {
- (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d day%s", dy,
- PLURAL(dy));
- p += strlen(p);
- }
- if (hr > 0)
- {
- (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d hour%s", hr,
- PLURAL(hr));
- p += strlen(p);
- }
- if (mi > 0)
- {
- (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d minute%s", mi,
- PLURAL(mi));
- p += strlen(p);
- }
- if (se > 0)
- {
- (void) sm_snprintf(p, SPACELEFT(buf, p), ", %d second%s", se,
- PLURAL(se));
- p += strlen(p);
- }
-
- return (buf + 2);
-}
diff --git a/contrib/sendmail/src/daemon.c b/contrib/sendmail/src/daemon.c
deleted file mode 100644
index 76b5b58..0000000
--- a/contrib/sendmail/src/daemon.c
+++ /dev/null
@@ -1,4486 +0,0 @@
-/*
- * Copyright (c) 1998-2007 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.
- *
- */
-
-#include <sendmail.h>
-#include "map.h"
-
-SM_RCSID("@(#)$Id: daemon.c,v 8.678 2007/03/08 00:33:40 ca Exp $")
-
-#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__)
-# define USE_SOCK_STREAM 1
-#endif /* defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) */
-
-#if defined(USE_SOCK_STREAM)
-# if NETINET || NETINET6
-# include <arpa/inet.h>
-# endif /* NETINET || NETINET6 */
-# if NAMED_BIND
-# ifndef NO_DATA
-# define NO_DATA NO_ADDRESS
-# endif /* ! NO_DATA */
-# endif /* NAMED_BIND */
-#endif /* defined(USE_SOCK_STREAM) */
-
-#if STARTTLS
-# include <openssl/rand.h>
-#endif /* STARTTLS */
-
-#include <sm/time.h>
-
-#if IP_SRCROUTE && NETINET
-# include <netinet/in_systm.h>
-# include <netinet/ip.h>
-# if HAS_IN_H
-# include <netinet/in.h>
-# ifndef IPOPTION
-# define IPOPTION ip_opts
-# define IP_LIST ip_opts
-# define IP_DST ip_dst
-# endif /* ! IPOPTION */
-# else /* HAS_IN_H */
-# include <netinet/ip_var.h>
-# ifndef IPOPTION
-# define IPOPTION ipoption
-# define IP_LIST ipopt_list
-# define IP_DST ipopt_dst
-# endif /* ! IPOPTION */
-# endif /* HAS_IN_H */
-#endif /* IP_SRCROUTE && NETINET */
-
-#include <sm/fdset.h>
-
-#define DAEMON_C 1
-#include <daemon.h>
-
-static void connecttimeout __P((int));
-static int opendaemonsocket __P((DAEMON_T *, bool));
-static unsigned short setupdaemon __P((SOCKADDR *));
-static void getrequests_checkdiskspace __P((ENVELOPE *e));
-static void setsockaddroptions __P((char *, DAEMON_T *));
-static void printdaemonflags __P((DAEMON_T *));
-static int addr_family __P((char *));
-static int addrcmp __P((struct hostent *, char *, SOCKADDR *));
-static void authtimeout __P((int));
-
-/*
-** DAEMON.C -- routines to use when running as a daemon.
-**
-** This entire file is highly dependent on the 4.2 BSD
-** interprocess communication primitives. No attempt has
-** been made to make this file portable to Version 7,
-** Version 6, MPX files, etc. If you should try such a
-** thing yourself, I recommend chucking the entire file
-** and starting from scratch. Basic semantics are:
-**
-** getrequests(e)
-** Opens a port and initiates a connection.
-** Returns in a child. Must set InChannel and
-** OutChannel appropriately.
-** clrdaemon()
-** Close any open files associated with getting
-** the connection; this is used when running the queue,
-** etc., to avoid having extra file descriptors during
-** the queue run and to avoid confusing the network
-** code (if it cares).
-** makeconnection(host, port, mci, e, enough)
-** Make a connection to the named host on the given
-** port. Returns zero on success, else an exit status
-** describing the error.
-** host_map_lookup(map, hbuf, avp, pstat)
-** Convert the entry in hbuf into a canonical form.
-*/
-
-static int NDaemons = 0; /* actual number of daemons */
-
-static time_t NextDiskSpaceCheck = 0;
-
-/*
-** GETREQUESTS -- open mail IPC port and get requests.
-**
-** Parameters:
-** e -- the current envelope.
-**
-** Returns:
-** pointer to flags.
-**
-** Side Effects:
-** Waits until some interesting activity occurs. When
-** it does, a child is created to process it, and the
-** parent waits for completion. Return from this
-** routine is always in the child. The file pointers
-** "InChannel" and "OutChannel" should be set to point
-** to the communication channel.
-** May restart persistent queue runners if they have ended
-** for some reason.
-*/
-
-BITMAP256 *
-getrequests(e)
- ENVELOPE *e;
-{
- int t;
- int idx, curdaemon = -1;
- int i, olddaemon = 0;
-#if XDEBUG
- bool j_has_dot;
-#endif /* XDEBUG */
- char status[MAXLINE];
- SOCKADDR sa;
- SOCKADDR_LEN_T len = sizeof(sa);
-#if _FFR_QUEUE_RUN_PARANOIA
- time_t lastrun;
-#endif /* _FFR_QUEUE_RUN_PARANOIA */
-# if NETUNIX
- extern int ControlSocket;
-# endif /* NETUNIX */
- extern ENVELOPE BlankEnvelope;
-
-
- /* initialize data for function that generates queue ids */
- init_qid_alg();
- for (idx = 0; idx < NDaemons; idx++)
- {
- Daemons[idx].d_port = setupdaemon(&(Daemons[idx].d_addr));
- Daemons[idx].d_firsttime = true;
- Daemons[idx].d_refuse_connections_until = (time_t) 0;
- }
-
- /*
- ** Try to actually open the connection.
- */
-
- if (tTd(15, 1))
- {
- for (idx = 0; idx < NDaemons; idx++)
- {
- sm_dprintf("getrequests: daemon %s: port %d\n",
- Daemons[idx].d_name,
- ntohs(Daemons[idx].d_port));
- }
- }
-
- /* get a socket for the SMTP connection */
- for (idx = 0; idx < NDaemons; idx++)
- Daemons[idx].d_socksize = opendaemonsocket(&Daemons[idx], true);
-
- if (opencontrolsocket() < 0)
- sm_syslog(LOG_WARNING, NOQID,
- "daemon could not open control socket %s: %s",
- ControlSocketName, sm_errstring(errno));
-
- /* If there are any queue runners released reapchild() co-ord's */
- (void) sm_signal(SIGCHLD, reapchild);
-
- /* write the pid to file, command line args to syslog */
- log_sendmail_pid(e);
-
-#if XDEBUG
- {
- char jbuf[MAXHOSTNAMELEN];
-
- expand("\201j", jbuf, sizeof(jbuf), e);
- j_has_dot = strchr(jbuf, '.') != NULL;
- }
-#endif /* XDEBUG */
-
- /* Add parent process as first item */
- proc_list_add(CurrentPid, "Sendmail daemon", PROC_DAEMON, 0, -1, NULL);
-
- if (tTd(15, 1))
- {
- for (idx = 0; idx < NDaemons; idx++)
- sm_dprintf("getrequests: daemon %s: %d\n",
- Daemons[idx].d_name,
- Daemons[idx].d_socket);
- }
-
- for (;;)
- {
- register pid_t pid;
- auto SOCKADDR_LEN_T lotherend;
- bool timedout = false;
- bool control = false;
- int save_errno;
- int pipefd[2];
- time_t now;
-#if STARTTLS
- long seed;
-#endif /* STARTTLS */
-
- /* see if we are rejecting connections */
- (void) sm_blocksignal(SIGALRM);
- CHECK_RESTART;
-
- for (idx = 0; idx < NDaemons; idx++)
- {
- /*
- ** XXX do this call outside the loop?
- ** no: refuse_connections may sleep().
- */
-
- now = curtime();
- if (now < Daemons[idx].d_refuse_connections_until)
- continue;
- if (bitnset(D_DISABLE, Daemons[idx].d_flags))
- continue;
- if (refuseconnections(e, idx, curdaemon == idx))
- {
- if (Daemons[idx].d_socket >= 0)
- {
- /* close socket so peer fails quickly */
- (void) close(Daemons[idx].d_socket);
- Daemons[idx].d_socket = -1;
- }
-
- /* refuse connections for next 15 seconds */
- Daemons[idx].d_refuse_connections_until = now + 15;
- }
- else if (Daemons[idx].d_socket < 0 ||
- Daemons[idx].d_firsttime)
- {
- if (!Daemons[idx].d_firsttime && LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "accepting connections again for daemon %s",
- Daemons[idx].d_name);
-
- /* arrange to (re)open the socket if needed */
- (void) opendaemonsocket(&Daemons[idx], false);
- Daemons[idx].d_firsttime = false;
- }
- }
-
- /* May have been sleeping above, check again */
- CHECK_RESTART;
-
- getrequests_checkdiskspace(e);
-
-#if XDEBUG
- /* check for disaster */
- {
- char jbuf[MAXHOSTNAMELEN];
-
- 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");
- 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");
- abort();
- }
- }
-#endif /* XDEBUG */
-
-#if 0
- /*
- ** Andrew Sun <asun@ieps-sun.ml.com> claims that this will
- ** fix the SVr4 problem. But it seems to have gone away,
- ** so is it worth doing this?
- */
-
- if (DaemonSocket >= 0 &&
- SetNonBlocking(DaemonSocket, false) < 0)
- log an error here;
-#endif /* 0 */
- (void) sm_releasesignal(SIGALRM);
-
- for (;;)
- {
- bool setproc = false;
- int highest = -1;
- fd_set readfds;
- struct timeval timeout;
-
- CHECK_RESTART;
- FD_ZERO(&readfds);
- for (idx = 0; idx < NDaemons; idx++)
- {
- /* wait for a connection */
- if (Daemons[idx].d_socket >= 0)
- {
- if (!setproc &&
- !bitnset(D_ETRNONLY,
- Daemons[idx].d_flags))
- {
- sm_setproctitle(true, e,
- "accepting connections");
- setproc = true;
- }
- if (Daemons[idx].d_socket > highest)
- highest = Daemons[idx].d_socket;
- SM_FD_SET(Daemons[idx].d_socket,
- &readfds);
- }
- }
-
-#if NETUNIX
- if (ControlSocket >= 0)
- {
- if (ControlSocket > highest)
- highest = ControlSocket;
- SM_FD_SET(ControlSocket, &readfds);
- }
-#endif /* NETUNIX */
-
- timeout.tv_sec = 5;
- timeout.tv_usec = 0;
-
- t = select(highest + 1, FDSET_CAST &readfds,
- NULL, NULL, &timeout);
-
- /* Did someone signal while waiting? */
- CHECK_RESTART;
-
- curdaemon = -1;
- if (doqueuerun())
- {
- (void) runqueue(true, false, false, false);
-#if _FFR_QUEUE_RUN_PARANOIA
- lastrun = now;
-#endif /* _FFR_QUEUE_RUN_PARANOIA */
- }
-#if _FFR_QUEUE_RUN_PARANOIA
- else if (CheckQueueRunners > 0 && QueueIntvl > 0 &&
- lastrun + QueueIntvl + CheckQueueRunners < now)
- {
-
- /*
- ** set lastrun unconditionally to avoid
- ** calling checkqueuerunner() all the time.
- ** That's also why we currently ignore the
- ** result of the function call.
- */
-
- (void) checkqueuerunner();
- lastrun = now;
- }
-#endif /* _FFR_QUEUE_RUN_PARANOIA */
-
- if (t <= 0)
- {
- timedout = true;
- break;
- }
-
- control = false;
- errno = 0;
-
- /* look "round-robin" for an active socket */
- if ((idx = olddaemon + 1) >= NDaemons)
- idx = 0;
- for (i = 0; i < NDaemons; i++)
- {
- if (Daemons[idx].d_socket >= 0 &&
- SM_FD_ISSET(Daemons[idx].d_socket,
- &readfds))
- {
- lotherend = Daemons[idx].d_socksize;
- memset(&RealHostAddr, '\0',
- sizeof(RealHostAddr));
- t = accept(Daemons[idx].d_socket,
- (struct sockaddr *)&RealHostAddr,
- &lotherend);
-
- /*
- ** If remote side closes before
- ** accept() finishes, sockaddr
- ** might not be fully filled in.
- */
-
- if (t >= 0 &&
- (lotherend == 0 ||
-# ifdef BSD4_4_SOCKADDR
- RealHostAddr.sa.sa_len == 0 ||
-# endif /* BSD4_4_SOCKADDR */
- RealHostAddr.sa.sa_family != Daemons[idx].d_addr.sa.sa_family))
- {
- (void) close(t);
- t = -1;
- errno = EINVAL;
- }
- olddaemon = curdaemon = idx;
- break;
- }
- if (++idx >= NDaemons)
- idx = 0;
- }
-#if NETUNIX
- if (curdaemon == -1 && ControlSocket >= 0 &&
- SM_FD_ISSET(ControlSocket, &readfds))
- {
- struct sockaddr_un sa_un;
-
- lotherend = sizeof(sa_un);
- memset(&sa_un, '\0', sizeof(sa_un));
- t = accept(ControlSocket,
- (struct sockaddr *)&sa_un,
- &lotherend);
-
- /*
- ** If remote side closes before
- ** accept() finishes, sockaddr
- ** might not be fully filled in.
- */
-
- if (t >= 0 &&
- (lotherend == 0 ||
-# ifdef BSD4_4_SOCKADDR
- sa_un.sun_len == 0 ||
-# endif /* BSD4_4_SOCKADDR */
- sa_un.sun_family != AF_UNIX))
- {
- (void) close(t);
- t = -1;
- errno = EINVAL;
- }
- if (t >= 0)
- control = true;
- }
-#else /* NETUNIX */
- if (curdaemon == -1)
- {
- /* No daemon to service */
- continue;
- }
-#endif /* NETUNIX */
- if (t >= 0 || errno != EINTR)
- break;
- }
- if (timedout)
- {
- timedout = false;
- continue;
- }
- save_errno = errno;
- (void) sm_blocksignal(SIGALRM);
- if (t < 0)
- {
- errno = save_errno;
-
- /* let's ignore these temporary errors */
- if (save_errno == EINTR
-#ifdef EAGAIN
- || save_errno == EAGAIN
-#endif /* EAGAIN */
-#ifdef ECONNABORTED
- || save_errno == ECONNABORTED
-#endif /* ECONNABORTED */
-#ifdef EWOULDBLOCK
- || save_errno == EWOULDBLOCK
-#endif /* EWOULDBLOCK */
- )
- continue;
-
- syserr("getrequests: accept");
-
- if (curdaemon >= 0)
- {
- /* arrange to re-open socket next time around */
- (void) close(Daemons[curdaemon].d_socket);
- Daemons[curdaemon].d_socket = -1;
-#if SO_REUSEADDR_IS_BROKEN
- /*
- ** Give time for bound socket to be released.
- ** This creates a denial-of-service if you can
- ** force accept() to fail on affected systems.
- */
-
- Daemons[curdaemon].d_refuse_connections_until =
- 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:
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_family}"), "unspec");
- break;
-#if _FFR_DAEMON_NETUNIX
-# if NETUNIX
- case AF_UNIX:
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_family}"), "local");
- break;
-# endif /* NETUNIX */
-#endif /* _FFR_DAEMON_NETUNIX */
-#if NETINET
- case AF_INET:
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_family}"), "inet");
- break;
-#endif /* NETINET */
-#if NETINET6
- case AF_INET6:
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_family}"), "inet6");
- break;
-#endif /* NETINET6 */
-#if NETISO
- case AF_ISO:
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_family}"), "iso");
- break;
-#endif /* NETISO */
-#if NETNS
- case AF_NS:
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_family}"), "ns");
- break;
-#endif /* NETNS */
-#if NETX25
- case AF_CCITT:
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_family}"), "x.25");
- break;
-#endif /* NETX25 */
- }
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_name}"),
- Daemons[curdaemon].d_name);
- if (Daemons[curdaemon].d_mflags != NULL)
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_flags}"),
- Daemons[curdaemon].d_mflags);
- else
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_flags}"), "");
- }
-
- /*
- ** If connection rate is exceeded here, connection shall be
- ** refused later by a new call after fork() by the
- ** validate_connection() function. Closing the connection
- ** at this point violates RFC 2821.
- ** Do NOT remove this call, its side effects are needed.
- */
-
- connection_rate_check(&RealHostAddr, NULL);
-
- /*
- ** Create a subprocess to process the mail.
- */
-
- if (tTd(15, 2))
- sm_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.
- */
-#if STARTTLS
- /* XXX get some better "random" data? */
- seed = get_random();
- RAND_seed((void *) &NextDiskSpaceCheck,
- sizeof(NextDiskSpaceCheck));
- RAND_seed((void *) &now, sizeof(now));
- RAND_seed((void *) &seed, sizeof(seed));
-#else /* STARTTLS */
- (void) get_random();
-#endif /* STARTTLS */
-
-#if NAMED_BIND
- /*
- ** Update MX records for FallbackMX.
- ** Let's hope this is fast otherwise we screw up the
- ** response time.
- */
-
- if (FallbackMX != NULL)
- (void) getfallbackmxrr(FallbackMX);
-#endif /* NAMED_BIND */
-
- if (tTd(93, 100))
- {
- /* don't fork, handle connection in this process */
- pid = 0;
- pipefd[0] = pipefd[1] = -1;
- }
- else
- {
- /*
- ** Create a pipe to keep the child from writing to
- ** the socket until after the parent has closed
- ** it. Otherwise the parent may hang if the child
- ** has closed it first.
- */
-
- if (pipe(pipefd) < 0)
- pipefd[0] = pipefd[1] = -1;
-
- (void) sm_blocksignal(SIGCHLD);
- pid = fork();
- if (pid < 0)
- {
- syserr("daemon: cannot fork");
- if (pipefd[0] != -1)
- {
- (void) close(pipefd[0]);
- (void) close(pipefd[1]);
- }
- (void) sm_releasesignal(SIGCHLD);
- (void) sleep(10);
- (void) close(t);
- continue;
- }
- }
-
- if (pid == 0)
- {
- char *p;
- SM_FILE_T *inchannel, *outchannel = NULL;
-
- /*
- ** CHILD -- return to caller.
- ** Collect verified idea of sending host.
- ** Verify calling user id if possible here.
- */
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- CurrentPid = getpid();
- close_sendmail_pid();
-
- (void) sm_releasesignal(SIGALRM);
- (void) sm_releasesignal(SIGCHLD);
- (void) sm_signal(SIGCHLD, SIG_DFL);
- (void) sm_signal(SIGHUP, SIG_DFL);
- (void) sm_signal(SIGTERM, intsig);
-
- /* turn on profiling */
- /* SM_PROF(0); */
-
- /*
- ** Initialize exception stack and default exception
- ** handler for child process.
- */
-
- sm_exc_newthread(fatal_error);
-
- if (!control)
- {
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{daemon_addr}"),
- anynet_ntoa(&Daemons[curdaemon].d_addr));
- (void) sm_snprintf(status, sizeof(status), "%d",
- ntohs(Daemons[curdaemon].d_port));
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{daemon_port}"), status);
- }
-
- for (idx = 0; idx < NDaemons; idx++)
- {
- if (Daemons[idx].d_socket >= 0)
- (void) close(Daemons[idx].d_socket);
- Daemons[idx].d_socket = -1;
- }
- clrcontrol();
-
- /* Avoid SMTP daemon actions if control command */
- if (control)
- {
- /* Add control socket process */
- proc_list_add(CurrentPid,
- "console socket child",
- PROC_CONTROL_CHILD, 0, -1, NULL);
- }
- else
- {
- proc_list_clear();
-
- /* clean up background delivery children */
- (void) sm_signal(SIGCHLD, reapchild);
-
- /* Add parent process as first child item */
- proc_list_add(CurrentPid, "daemon child",
- PROC_DAEMON_CHILD, 0, -1, NULL);
- /* don't schedule queue runs if ETRN */
- QueueIntvl = 0;
-
- /*
- ** Hack: override global variables if
- ** the corresponding DaemonPortOption
- ** is set.
- */
-#if _FFR_SS_PER_DAEMON
- if (Daemons[curdaemon].d_supersafe !=
- DPO_NOTSET)
- SuperSafe = Daemons[curdaemon].
- d_supersafe;
-#endif /* _FFR_SS_PER_DAEMON */
- if (Daemons[curdaemon].d_dm != DM_NOTSET)
- set_delivery_mode(
- Daemons[curdaemon].d_dm, e);
-
- if (Daemons[curdaemon].d_refuseLA !=
- DPO_NOTSET)
- RefuseLA = Daemons[curdaemon].
- d_refuseLA;
- if (Daemons[curdaemon].d_queueLA != DPO_NOTSET)
- QueueLA = Daemons[curdaemon].d_queueLA;
- if (Daemons[curdaemon].d_delayLA != DPO_NOTSET)
- DelayLA = Daemons[curdaemon].d_delayLA;
- if (Daemons[curdaemon].d_maxchildren !=
- DPO_NOTSET)
- MaxChildren = Daemons[curdaemon].
- d_maxchildren;
-
- sm_setproctitle(true, e, "startup with %s",
- anynet_ntoa(&RealHostAddr));
- }
-
- if (pipefd[0] != -1)
- {
- auto char c;
-
- /*
- ** Wait for the parent to close the write end
- ** of the pipe, which we will see as an EOF.
- ** This guarantees that we won't write to the
- ** socket until after the parent has closed
- ** the pipe.
- */
-
- /* close the write end of the pipe */
- (void) close(pipefd[1]);
-
- /* we shouldn't be interrupted, but ... */
- while (read(pipefd[0], &c, 1) < 0 &&
- errno == EINTR)
- continue;
- (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) > MAXNAME) /* XXX - 1 ? */
- p[MAXNAME] = '\0';
- RealHostName = newstr(p);
- if (RealHostName[0] == '[')
- {
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{client_resolve}"),
- h_errno == TRY_AGAIN ? "TEMP" : "FAIL");
- }
- else
- {
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{client_resolve}"), "OK");
- }
- sm_setproctitle(true, e, "startup with %s", p);
- markstats(e, NULL, STATS_CONNECT);
-
- if ((inchannel = sm_io_open(SmFtStdiofd,
- SM_TIME_DEFAULT,
- (void *) &t,
- SM_IO_RDONLY_B,
- NULL)) == NULL ||
- (t = dup(t)) < 0 ||
- (outchannel = sm_io_open(SmFtStdiofd,
- SM_TIME_DEFAULT,
- (void *) &t,
- SM_IO_WRONLY_B,
- NULL)) == NULL)
- {
- syserr("cannot open SMTP server channel, fd=%d",
- t);
- finis(false, true, EX_OK);
- }
- sm_io_automode(inchannel, outchannel);
-
- InChannel = inchannel;
- OutChannel = outchannel;
- DisConnected = false;
-
-#if XLA
- if (!xla_host_ok(RealHostName))
- {
- message("421 4.4.5 Too many SMTP sessions for this host");
- finis(false, true, EX_OK);
- }
-#endif /* XLA */
- /* find out name for interface of connection */
- if (getsockname(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
- NULL), &sa.sa, &len) == 0)
- {
- p = hostnamebyanyaddr(&sa);
- if (tTd(15, 9))
- sm_dprintf("getreq: got name %s\n", p);
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{if_name}"), p);
-
- /*
- ** Do this only if it is not the loopback
- ** interface.
- */
-
- if (!isloopback(sa))
- {
- char *addr;
- char family[5];
-
- addr = anynet_ntoa(&sa);
- (void) sm_snprintf(family,
- sizeof(family),
- "%d", sa.sa.sa_family);
- macdefine(&BlankEnvelope.e_macro,
- A_TEMP,
- macid("{if_addr}"), addr);
- macdefine(&BlankEnvelope.e_macro,
- A_TEMP,
- macid("{if_family}"), family);
- if (tTd(15, 7))
- sm_dprintf("getreq: got addr %s and family %s\n",
- addr, family);
- }
- else
- {
- macdefine(&BlankEnvelope.e_macro,
- A_PERM,
- macid("{if_addr}"), NULL);
- macdefine(&BlankEnvelope.e_macro,
- A_PERM,
- macid("{if_family}"), NULL);
- }
- }
- else
- {
- if (tTd(15, 7))
- sm_dprintf("getreq: getsockname failed\n");
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{if_name}"), NULL);
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{if_addr}"), NULL);
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{if_family}"), NULL);
- }
- break;
- }
-
- /* parent -- keep track of children */
- if (control)
- {
- (void) sm_snprintf(status, sizeof(status),
- "control socket server child");
- proc_list_add(pid, status, PROC_CONTROL, 0, -1, NULL);
- }
- else
- {
- (void) sm_snprintf(status, sizeof(status),
- "SMTP server child for %s",
- anynet_ntoa(&RealHostAddr));
- proc_list_add(pid, status, PROC_DAEMON, 0, -1,
- &RealHostAddr);
- }
- (void) sm_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))
- sm_dprintf("getreq: returning\n");
-
-#if MILTER
- /* set the filters for this daemon */
- if (Daemons[curdaemon].d_inputfilterlist != NULL)
- {
- for (i = 0;
- (i < MAXFILTERS &&
- Daemons[curdaemon].d_inputfilters[i] != NULL);
- i++)
- {
- InputFilters[i] = Daemons[curdaemon].d_inputfilters[i];
- }
- if (i < MAXFILTERS)
- InputFilters[i] = NULL;
- }
-#endif /* MILTER */
- return &Daemons[curdaemon].d_flags;
-}
-
-/*
-** GETREQUESTS_CHECKDISKSPACE -- check available diskspace.
-**
-** Parameters:
-** e -- envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Modifies Daemon flags (D_ETRNONLY) if not enough disk space.
-*/
-
-static void
-getrequests_checkdiskspace(e)
- ENVELOPE *e;
-{
- bool logged = false;
- int idx;
- time_t now;
-
- now = curtime();
- if (now < NextDiskSpaceCheck)
- return;
-
- /* Check if there is available disk space in all queue groups. */
- if (!enoughdiskspace(0, NULL))
- {
- for (idx = 0; idx < NDaemons; ++idx)
- {
- if (bitnset(D_ETRNONLY, Daemons[idx].d_flags))
- continue;
-
- /* log only if not logged before */
- if (!logged)
- {
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "rejecting new messages: min free: %ld",
- MinBlocksFree);
- sm_setproctitle(true, e,
- "rejecting new messages: min free: %ld",
- MinBlocksFree);
- logged = true;
- }
- setbitn(D_ETRNONLY, Daemons[idx].d_flags);
- }
- }
- else
- {
- for (idx = 0; idx < NDaemons; ++idx)
- {
- if (!bitnset(D_ETRNONLY, Daemons[idx].d_flags))
- continue;
-
- /* log only if not logged before */
- if (!logged)
- {
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "accepting new messages (again)");
- logged = true;
- }
-
- /* title will be set later */
- clrbitn(D_ETRNONLY, Daemons[idx].d_flags);
- }
- }
-
- /* only check disk space once a minute */
- NextDiskSpaceCheck = now + 60;
-}
-
-/*
-** OPENDAEMONSOCKET -- open SMTP socket
-**
-** Deals with setting all appropriate options.
-**
-** Parameters:
-** d -- the structure for the daemon to open.
-** firsttime -- set if this is the initial open.
-**
-** Returns:
-** Size in bytes of the daemon socket addr.
-**
-** Side Effects:
-** Leaves DaemonSocket set to the open socket.
-** Exits if the socket cannot be created.
-*/
-
-#define MAXOPENTRIES 10 /* maximum number of tries to open connection */
-
-static int
-opendaemonsocket(d, firsttime)
- DAEMON_T *d;
- bool firsttime;
-{
- int on = 1;
- int fdflags;
- SOCKADDR_LEN_T socksize = 0;
- int ntries = 0;
- int save_errno;
-
- if (tTd(15, 2))
- sm_dprintf("opendaemonsocket(%s)\n", d->d_name);
-
- do
- {
- if (ntries > 0)
- (void) sleep(5);
- if (firsttime || d->d_socket < 0)
- {
-#if _FFR_DAEMON_NETUNIX
-# if NETUNIX
- if (d->d_addr.sa.sa_family == AF_UNIX)
- {
- int rval;
- long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_ROOTOK|SFF_EXECOK|SFF_CREAT;
-
- /* if not safe, don't use it */
- rval = safefile(d->d_addr.sunix.sun_path,
- RunAsUid, RunAsGid,
- RunAsUserName, sff,
- S_IRUSR|S_IWUSR, NULL);
- if (rval != 0)
- {
- save_errno = errno;
- syserr("opendaemonsocket: daemon %s: unsafe domain socket %s",
- d->d_name,
- d->d_addr.sunix.sun_path);
- goto fail;
- }
-
- /* Don't try to overtake an existing socket */
- (void) unlink(d->d_addr.sunix.sun_path);
- }
-# endif /* NETUNIX */
-#endif /* _FFR_DOMAIN_NETUNIX */
- d->d_socket = socket(d->d_addr.sa.sa_family,
- SOCK_STREAM, 0);
- if (d->d_socket < 0)
- {
- save_errno = errno;
- syserr("opendaemonsocket: daemon %s: can't create server SMTP socket",
- d->d_name);
- fail:
- if (bitnset(D_OPTIONAL, d->d_flags) &&
- (!transienterror(save_errno) ||
- ntries >= MAXOPENTRIES - 1))
- {
- syserr("opendaemonsocket: daemon %s: optional socket disabled",
- d->d_name);
- setbitn(D_DISABLE, d->d_flags);
- d->d_socket = -1;
- return -1;
- }
- severe:
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID,
- "daemon %s: problem creating SMTP socket",
- d->d_name);
- d->d_socket = -1;
- continue;
- }
-
- if (SM_FD_SETSIZE > 0 && d->d_socket >= SM_FD_SETSIZE)
- {
- save_errno = EINVAL;
- syserr("opendaemonsocket: daemon %s: server SMTP socket (%d) too large",
- d->d_name, d->d_socket);
- goto fail;
- }
-
- /* turn on network debugging? */
- if (tTd(15, 101))
- (void) setsockopt(d->d_socket, SOL_SOCKET,
- SO_DEBUG, (char *)&on,
- sizeof(on));
-
- (void) setsockopt(d->d_socket, SOL_SOCKET,
- SO_REUSEADDR, (char *)&on, sizeof(on));
- (void) setsockopt(d->d_socket, SOL_SOCKET,
- SO_KEEPALIVE, (char *)&on, sizeof(on));
-
-#ifdef SO_RCVBUF
- if (d->d_tcprcvbufsize > 0)
- {
- if (setsockopt(d->d_socket, SOL_SOCKET,
- 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 /* SO_SNDBUF */
-
- 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",
- sm_errstring(save_errno));
- (void) close(d->d_socket);
- goto severe;
- }
-
- switch (d->d_addr.sa.sa_family)
- {
-#if _FFR_DAEMON_NETUNIX
-# ifdef NETUNIX
- case AF_UNIX:
- socksize = sizeof(d->d_addr.sunix);
- break;
-# endif /* NETUNIX */
-#endif /* _FFR_DAEMON_NETUNIX */
-#if NETINET
- case AF_INET:
- socksize = sizeof(d->d_addr.sin);
- break;
-#endif /* NETINET */
-
-#if NETINET6
- case AF_INET6:
- socksize = sizeof(d->d_addr.sin6);
- break;
-#endif /* NETINET6 */
-
-#if NETISO
- case AF_ISO:
- socksize = sizeof(d->d_addr.siso);
- break;
-#endif /* NETISO */
-
- default:
- socksize = sizeof(d->d_addr);
- break;
- }
-
- if (bind(d->d_socket, &d->d_addr.sa, socksize) < 0)
- {
- /* probably another daemon already */
- save_errno = errno;
- syserr("opendaemonsocket: daemon %s: cannot bind",
- d->d_name);
- (void) close(d->d_socket);
- goto fail;
- }
- }
- if (!firsttime &&
- listen(d->d_socket, d->d_listenqueue) < 0)
- {
- 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(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
-**
-** Returns:
-** port number on which daemon should run
-**
-*/
-
-static unsigned short
-setupdaemon(daemonaddr)
- SOCKADDR *daemonaddr;
-{
- unsigned 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:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** releases any resources used by the passive daemon.
-*/
-
-void
-clrdaemon()
-{
- 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;
- }
-}
-
-/*
-** GETMODIFIERS -- get modifier flags
-**
-** Parameters:
-** v -- the modifiers (input text line).
-** modifiers -- pointer to flag field to represent modifiers.
-**
-** Returns:
-** (xallocat()ed) string representation of modifiers.
-**
-** Side Effects:
-** fills in modifiers.
-*/
-
-char *
-getmodifiers(v, modifiers)
- char *v;
- BITMAP256 modifiers;
-{
- int l;
- char *h, *f, *flags;
-
- /* maximum length of flags: upper case Option -> "OO " */
- l = 3 * strlen(v) + 3;
-
- /* is someone joking? */
- if (l < 0 || l > 256)
- {
- if (LogLevel > 2)
- sm_syslog(LOG_ERR, NOQID,
- "getmodifiers too long, ignored");
- return NULL;
- }
- flags = xalloc(l);
- f = flags;
- clrbitmap(modifiers);
- for (h = v; *h != '\0'; h++)
- {
- if (isascii(*h) && !isspace(*h) && isprint(*h))
- {
- setbitn(*h, modifiers);
- if (flags != f)
- *flags++ = ' ';
- *flags++ = *h;
- if (isupper(*h))
- *flags++ = *h;
- }
- }
- *flags++ = '\0';
- return f;
-}
-
-/*
-** CHKDAEMONMODIFIERS -- check whether all daemons have set a flag.
-**
-** Parameters:
-** flag -- the flag to test.
-**
-** Returns:
-** true iff all daemons have set flag.
-*/
-
-bool
-chkdaemonmodifiers(flag)
- int flag;
-{
- int i;
-
- for (i = 0; i < NDaemons; i++)
- if (!bitnset((char) flag, Daemons[i].d_flags))
- return false;
- return true;
-}
-
-/*
-** SETSOCKADDROPTIONS -- set options for SOCKADDR (daemon or client)
-**
-** Parameters:
-** p -- the options line.
-** d -- the daemon structure to fill in.
-**
-** Returns:
-** none.
-*/
-
-static void
-setsockaddroptions(p, d)
- char *p;
- DAEMON_T *d;
-{
-#if NETISO
- short portno;
-#endif /* NETISO */
- char *port = NULL;
- char *addr = NULL;
-
-#if NETINET
- if (d->d_addr.sa.sa_family == AF_UNSPEC)
- d->d_addr.sa.sa_family = AF_INET;
-#endif /* NETINET */
-#if _FFR_SS_PER_DAEMON
- d->d_supersafe = DPO_NOTSET;
-#endif /* _FFR_SS_PER_DAEMON */
- d->d_dm = DM_NOTSET;
- d->d_refuseLA = DPO_NOTSET;
- d->d_queueLA = DPO_NOTSET;
- d->d_delayLA = DPO_NOTSET;
- d->d_maxchildren = DPO_NOTSET;
-
- while (p != NULL)
- {
- register char *f;
- register char *v;
-
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- break;
- f = p;
- p = strchr(p, ',');
- if (p != NULL)
- *p++ = '\0';
- v = strchr(f, '=');
- if (v == NULL)
- continue;
- while (isascii(*++v) && isspace(*v))
- continue;
-
- switch (*f)
- {
- case 'A': /* address */
-#if !_FFR_DPO_CS
- case 'a':
-#endif /* !_FFR_DPO_CS */
- addr = v;
- break;
-
- case 'c':
- d->d_maxchildren = atoi(v);
- break;
-
- case 'D': /* DeliveryMode */
- switch (*v)
- {
- case SM_QUEUE:
- case SM_DEFER:
- case SM_DELIVER:
- case SM_FORK:
- d->d_dm = *v;
- break;
- default:
- syserr("554 5.3.5 Unknown delivery mode %c",
- *v);
- break;
- }
- break;
-
- case 'd': /* delayLA */
- d->d_delayLA = atoi(v);
- break;
-
- case 'F': /* address family */
-#if !_FFR_DPO_CS
- case 'f':
-#endif /* !_FFR_DPO_CS */
- if (isascii(*v) && isdigit(*v))
- d->d_addr.sa.sa_family = atoi(v);
-#if _FFR_DAEMON_NETUNIX
-# ifdef NETUNIX
- else if (sm_strcasecmp(v, "unix") == 0 ||
- sm_strcasecmp(v, "local") == 0)
- d->d_addr.sa.sa_family = AF_UNIX;
-# endif /* NETUNIX */
-#endif /* _FFR_DAEMON_NETUNIX */
-#if NETINET
- else if (sm_strcasecmp(v, "inet") == 0)
- d->d_addr.sa.sa_family = AF_INET;
-#endif /* NETINET */
-#if NETINET6
- else if (sm_strcasecmp(v, "inet6") == 0)
- d->d_addr.sa.sa_family = AF_INET6;
-#endif /* NETINET6 */
-#if NETISO
- else if (sm_strcasecmp(v, "iso") == 0)
- d->d_addr.sa.sa_family = AF_ISO;
-#endif /* NETISO */
-#if NETNS
- else if (sm_strcasecmp(v, "ns") == 0)
- d->d_addr.sa.sa_family = AF_NS;
-#endif /* NETNS */
-#if NETX25
- else if (sm_strcasecmp(v, "x.25") == 0)
- d->d_addr.sa.sa_family = AF_CCITT;
-#endif /* NETX25 */
- else
- syserr("554 5.3.5 Unknown address family %s in Family=option",
- v);
- break;
-
-#if MILTER
- case 'I':
-# if !_FFR_DPO_CS
- case 'i':
-# endif /* !_FFR_DPO_CS */
- d->d_inputfilterlist = v;
- break;
-#endif /* MILTER */
-
- case 'L': /* listen queue size */
-#if !_FFR_DPO_CS
- case 'l':
-#endif /* !_FFR_DPO_CS */
- d->d_listenqueue = atoi(v);
- break;
-
- case 'M': /* modifiers (flags) */
-#if !_FFR_DPO_CS
- case 'm':
-#endif /* !_FFR_DPO_CS */
- d->d_mflags = getmodifiers(v, d->d_flags);
- break;
-
- case 'N': /* name */
-#if !_FFR_DPO_CS
- case 'n':
-#endif /* !_FFR_DPO_CS */
- d->d_name = v;
- break;
-
- case 'P': /* port */
-#if !_FFR_DPO_CS
- case 'p':
-#endif /* !_FFR_DPO_CS */
- port = v;
- break;
-
- case 'q':
- d->d_queueLA = atoi(v);
- break;
-
- case 'R': /* receive buffer size */
- d->d_tcprcvbufsize = atoi(v);
- break;
-
- case 'r':
- d->d_refuseLA = atoi(v);
- break;
-
- case 'S': /* send buffer size */
-#if !_FFR_DPO_CS
- case 's':
-#endif /* !_FFR_DPO_CS */
- d->d_tcpsndbufsize = atoi(v);
- break;
-
-#if _FFR_SS_PER_DAEMON
- case 'T': /* SuperSafe */
- if (tolower(*v) == 'i')
- d->d_supersafe = SAFE_INTERACTIVE;
- else if (tolower(*v) == 'p')
-# if MILTER
- d->d_supersafe = SAFE_REALLY_POSTMILTER;
-# else /* MILTER */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n");
-# endif /* MILTER */
- else
- d->d_supersafe = atobool(v) ? SAFE_REALLY
- : SAFE_NO;
- break;
-#endif /* _FFR_SS_PER_DAEMON */
-
- default:
- syserr("554 5.3.5 PortOptions parameter \"%s\" unknown",
- f);
- }
- }
-
- /* Check addr and port after finding family */
- if (addr != NULL)
- {
- switch (d->d_addr.sa.sa_family)
- {
-#if _FFR_DAEMON_NETUNIX
-# if NETUNIX
- case AF_UNIX:
- if (strlen(addr) >= sizeof(d->d_addr.sunix.sun_path))
- {
- errno = ENAMETOOLONG;
- syserr("setsockaddroptions: domain socket name too long: %s > %d",
- addr, sizeof(d->d_addr.sunix.sun_path));
- break;
- }
-
- /* file safety check done in opendaemonsocket() */
- (void) memset(&d->d_addr.sunix.sun_path, '\0',
- sizeof(d->d_addr.sunix.sun_path));
- (void) sm_strlcpy((char *)&d->d_addr.sunix.sun_path,
- addr,
- sizeof(d->d_addr.sunix.sun_path));
- break;
-# endif /* NETUNIX */
-#endif /* _FFR_DAEMON_NETUNIX */
-#if NETINET
- case AF_INET:
- if (!isascii(*addr) || !isdigit(*addr) ||
- ((d->d_addr.sin.sin_addr.s_addr = inet_addr(addr))
- == INADDR_NONE))
- {
- register struct hostent *hp;
-
- hp = sm_gethostbyname(addr, AF_INET);
- if (hp == NULL)
- syserr("554 5.3.0 host \"%s\" unknown",
- addr);
- else
- {
- while (*(hp->h_addr_list) != NULL &&
- hp->h_addrtype != AF_INET)
- hp->h_addr_list++;
- if (*(hp->h_addr_list) == NULL)
- syserr("554 5.3.0 host \"%s\" unknown",
- addr);
- else
- memmove(&d->d_addr.sin.sin_addr,
- *(hp->h_addr_list),
- INADDRSZ);
-# if NETINET6
- freehostent(hp);
- hp = NULL;
-# endif /* NETINET6 */
- }
- }
- break;
-#endif /* NETINET */
-
-#if NETINET6
- case AF_INET6:
- if (anynet_pton(AF_INET6, addr,
- &d->d_addr.sin6.sin6_addr) != 1)
- {
- register struct hostent *hp;
-
- hp = sm_gethostbyname(addr, AF_INET6);
- if (hp == NULL)
- syserr("554 5.3.0 host \"%s\" unknown",
- addr);
- else
- {
- while (*(hp->h_addr_list) != NULL &&
- hp->h_addrtype != AF_INET6)
- hp->h_addr_list++;
- if (*(hp->h_addr_list) == NULL)
- syserr("554 5.3.0 host \"%s\" unknown",
- addr);
- else
- memmove(&d->d_addr.sin6.sin6_addr,
- *(hp->h_addr_list),
- IN6ADDRSZ);
- freehostent(hp);
- hp = NULL;
- }
- }
- break;
-#endif /* NETINET6 */
-
- default:
- syserr("554 5.3.5 address= option unsupported for family %d",
- d->d_addr.sa.sa_family);
- break;
- }
- }
-
- if (port != NULL)
- {
- switch (d->d_addr.sa.sa_family)
- {
-#if NETINET
- case AF_INET:
- if (isascii(*port) && isdigit(*port))
- d->d_addr.sin.sin_port = htons((unsigned short)
- atoi((const char *) port));
- else
- {
-# ifdef NO_GETSERVBYNAME
- syserr("554 5.3.5 invalid port number: %s",
- port);
-# else /* NO_GETSERVBYNAME */
- register struct servent *sp;
-
- sp = getservbyname(port, "tcp");
- if (sp == NULL)
- syserr("554 5.3.5 service \"%s\" unknown",
- port);
- else
- d->d_addr.sin.sin_port = sp->s_port;
-# endif /* NO_GETSERVBYNAME */
- }
- break;
-#endif /* NETINET */
-
-#if NETINET6
- case AF_INET6:
- if (isascii(*port) && isdigit(*port))
- d->d_addr.sin6.sin6_port = htons((unsigned short)
- atoi(port));
- else
- {
-# ifdef NO_GETSERVBYNAME
- syserr("554 5.3.5 invalid port number: %s",
- port);
-# else /* NO_GETSERVBYNAME */
- register struct servent *sp;
-
- sp = getservbyname(port, "tcp");
- if (sp == NULL)
- syserr("554 5.3.5 service \"%s\" unknown",
- port);
- else
- d->d_addr.sin6.sin6_port = sp->s_port;
-# endif /* NO_GETSERVBYNAME */
- }
- break;
-#endif /* NETINET6 */
-
-#if NETISO
- case AF_ISO:
- /* assume two byte transport selector */
- if (isascii(*port) && isdigit(*port))
- portno = htons((unsigned short) atoi(port));
- else
- {
-# ifdef NO_GETSERVBYNAME
- syserr("554 5.3.5 invalid port number: %s",
- port);
-# else /* NO_GETSERVBYNAME */
- register struct servent *sp;
-
- sp = getservbyname(port, "tcp");
- if (sp == NULL)
- syserr("554 5.3.5 service \"%s\" unknown",
- port);
- else
- portno = sp->s_port;
-# endif /* NO_GETSERVBYNAME */
- }
- memmove(TSEL(&d->d_addr.siso),
- (char *) &portno, 2);
- break;
-#endif /* NETISO */
-
- default:
- syserr("554 5.3.5 Port= option unsupported for family %d",
- d->d_addr.sa.sa_family);
- break;
- }
- }
-}
-/*
-** SETDAEMONOPTIONS -- set options for running the MTA daemon
-**
-** Parameters:
-** p -- the options line.
-**
-** Returns:
-** true if successful, false otherwise.
-**
-** Side Effects:
-** increments number of daemons.
-*/
-
-#define DEF_LISTENQUEUE 10
-
-struct dflags
-{
- char *d_name;
- int d_flag;
-};
-
-static struct dflags DaemonFlags[] =
-{
- { "AUTHREQ", D_AUTHREQ },
- { "BINDIF", D_BINDIF },
- { "CANONREQ", D_CANONREQ },
- { "IFNHELO", D_IFNHELO },
- { "FQMAIL", D_FQMAIL },
- { "FQRCPT", D_FQRCPT },
- { "SMTPS", D_SMTPS },
- { "UNQUALOK", D_UNQUALOK },
- { "NOAUTH", D_NOAUTH },
- { "NOCANON", D_NOCANON },
- { "NOETRN", D_NOETRN },
- { "NOTLS", D_NOTLS },
- { "ETRNONLY", D_ETRNONLY },
- { "OPTIONAL", D_OPTIONAL },
- { "DISABLE", D_DISABLE },
- { "ISSET", D_ISSET },
- { NULL, 0 }
-};
-
-static void
-printdaemonflags(d)
- DAEMON_T *d;
-{
- register struct dflags *df;
- bool first = true;
-
- for (df = DaemonFlags; df->d_name != NULL; df++)
- {
- if (!bitnset(df->d_flag, d->d_flags))
- continue;
- if (first)
- sm_dprintf("<%s", df->d_name);
- else
- sm_dprintf(",%s", df->d_name);
- first = false;
- }
- if (!first)
- sm_dprintf(">");
-}
-
-bool
-setdaemonoptions(p)
- register char *p;
-{
- if (NDaemons >= MAXDAEMONS)
- return false;
- Daemons[NDaemons].d_socket = -1;
- Daemons[NDaemons].d_listenqueue = DEF_LISTENQUEUE;
- clrbitmap(Daemons[NDaemons].d_flags);
- setsockaddroptions(p, &Daemons[NDaemons]);
-
-#if MILTER
- if (Daemons[NDaemons].d_inputfilterlist != NULL)
- Daemons[NDaemons].d_inputfilterlist = newstr(Daemons[NDaemons].d_inputfilterlist);
-#endif /* MILTER */
-
- if (Daemons[NDaemons].d_name != NULL)
- Daemons[NDaemons].d_name = newstr(Daemons[NDaemons].d_name);
- else
- {
- char num[30];
-
- (void) sm_snprintf(num, sizeof(num), "Daemon%d", NDaemons);
- Daemons[NDaemons].d_name = newstr(num);
- }
-
- if (tTd(37, 1))
- {
- sm_dprintf("Daemon %s flags: ", Daemons[NDaemons].d_name);
- printdaemonflags(&Daemons[NDaemons]);
- sm_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 = DEF_LISTENQUEUE;
- Daemons[NDaemons].d_name = "Daemon0";
- NDaemons = 1;
- }
-}
-/*
-** SETCLIENTOPTIONS -- set options for running the client
-**
-** Parameters:
-** p -- the options line.
-**
-** Returns:
-** none.
-*/
-
-static DAEMON_T ClientSettings[AF_MAX + 1];
-
-void
-setclientoptions(p)
- register char *p;
-{
- int family;
- DAEMON_T d;
-
- memset(&d, '\0', sizeof(d));
- setsockaddroptions(p, &d);
-
- /* grab what we need */
- family = d.d_addr.sa.sa_family;
- STRUCTCOPY(d, ClientSettings[family]);
- setbitn(D_ISSET, ClientSettings[family].d_flags); /* mark as set */
- if (d.d_name != NULL)
- ClientSettings[family].d_name = newstr(d.d_name);
- else
- {
- char num[30];
-
- (void) sm_snprintf(num, sizeof(num), "Client%d", family);
- ClientSettings[family].d_name = newstr(num);
- }
-}
-/*
-** ADDR_FAMILY -- determine address family from address
-**
-** Parameters:
-** 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))
- sm_dprintf("addr_family(%s): INET\n", addr);
- return AF_INET;
- }
-#endif /* NETINET */
-#if NETINET6
- if (anynet_pton(AF_INET6, addr, &clt_addr.sin6.sin6_addr) == 1)
- {
- if (tTd(16, 9))
- sm_dprintf("addr_family(%s): INET6\n", addr);
- return AF_INET6;
- }
-#endif /* NETINET6 */
-#if _FFR_DAEMON_NETUNIX
-# if NETUNIX
- if (*addr == '/')
- {
- if (tTd(16, 9))
- sm_dprintf("addr_family(%s): LOCAL\n", addr);
- return AF_UNIX;
- }
-# endif /* NETUNIX */
-#endif /* _FFR_DAEMON_NETUNIX */
- if (tTd(16, 9))
- sm_dprintf("addr_family(%s): UNSPEC\n", addr);
- return AF_UNSPEC;
-}
-
-/*
-** CHKCLIENTMODIFIERS -- check whether all clients have set a flag.
-**
-** Parameters:
-** flag -- the flag to test.
-**
-** Returns:
-** true iff all configured clients have set the flag.
-*/
-
-bool
-chkclientmodifiers(flag)
- int flag;
-{
- int i;
- bool flagisset;
-
- flagisset = false;
- for (i = 0; i < AF_MAX; i++)
- {
- if (bitnset(D_ISSET, ClientSettings[i].d_flags))
- {
- if (!bitnset((char) flag, ClientSettings[i].d_flags))
- return false;
- flagisset = true;
- }
- }
- return flagisset;
-}
-
-#if MILTER
-/*
-** SETUP_DAEMON_FILTERS -- Parse per-socket filters
-**
-** Parameters:
-** none
-**
-** Returns:
-** none
-*/
-
-void
-setup_daemon_milters()
-{
- int idx;
-
- if (OpMode == MD_SMTP)
- {
- /* no need to configure the daemons */
- return;
- }
-
- for (idx = 0; idx < NDaemons; idx++)
- {
- if (Daemons[idx].d_inputfilterlist != NULL)
- {
- milter_config(Daemons[idx].d_inputfilterlist,
- Daemons[idx].d_inputfilters,
- MAXFILTERS);
- }
- }
-}
-#endif /* MILTER */
-/*
-** MAKECONNECTION -- make a connection to an SMTP socket on a machine.
-**
-** Parameters:
-** host -- the name of the host.
-** port -- the port number to connect to.
-** mci -- a pointer to the mail connection information
-** structure to be filled in.
-** e -- the current envelope.
-** enough -- time at which to stop further connection attempts.
-** (0 means no limit)
-**
-** Returns:
-** An exit code telling whether the connection could be
-** made and if not why not.
-**
-** Side Effects:
-** none.
-*/
-
-static jmp_buf CtxConnectTimeout;
-
-SOCKADDR CurHostAddr; /* address of current host */
-
-int
-makeconnection(host, port, mci, e, enough)
- char *host;
- volatile unsigned int port;
- register MCI *mci;
- ENVELOPE *e;
- time_t enough;
-{
- register volatile int addrno = 0;
- volatile int s;
- register struct hostent *volatile hp = (struct hostent *) NULL;
- SOCKADDR addr;
- SOCKADDR clt_addr;
- int save_errno = 0;
- volatile SOCKADDR_LEN_T addrlen;
- volatile bool firstconnect = true;
- SM_EVENT *volatile ev = NULL;
-#if NETINET6
- volatile bool v6found = false;
-#endif /* NETINET6 */
- volatile int family = InetMode;
- SOCKADDR_LEN_T len;
- volatile SOCKADDR_LEN_T socksize = 0;
- 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}"), e)) != NULL)
- {
- for (; *p != '\0'; p++)
- {
- if (!(isascii(*p) && isspace(*p)))
- setbitn(bitidx(*p), d_flags);
- }
- }
-
-#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}"), e)) != NULL &&
- *p != '\0')
- {
-#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:
- clt_addr.sin.sin_addr.s_addr = inet_addr(p);
- if (clt_addr.sin.sin_addr.s_addr != INADDR_NONE &&
- clt_addr.sin.sin_addr.s_addr != INADDR_LOOPBACK)
- {
- clt_bind = true;
- socksize = sizeof(struct sockaddr_in);
- }
- break;
-#endif /* NETINET */
-
-#if NETINET6
- case AF_INET6:
- if (inet_addr(p) != INADDR_NONE)
- (void) sm_snprintf(p6, sizeof(p6),
- "IPv6:::ffff:%s", p);
- else
- (void) sm_strlcpy(p6, p, sizeof(p6));
- if (anynet_pton(AF_INET6, p6,
- &clt_addr.sin6.sin6_addr) == 1 &&
- !IN6_IS_ADDR_LOOPBACK(&clt_addr.sin6.sin6_addr))
- {
- clt_bind = true;
- 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;
- }
-
- /* D_BINDIF not set or not available, fallback to ClientPortOptions */
- if (!clt_bind)
- {
- STRUCTCOPY(ClientSettings[family].d_addr, clt_addr);
- 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.
- */
-
- SM_SET_H_ERRNO(0);
- errno = 0;
- memset(&CurHostAddr, '\0', sizeof(CurHostAddr));
- memset(&addr, '\0', sizeof(addr));
- SmtpPhase = mci->mci_phase = "initial connection";
- CurHostName = host;
-
- if (host[0] == '[')
- {
- 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 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 &&
- anynet_pton(AF_INET6, &host[1],
- &hid6.sin6_addr) == 1)
- {
- addr.sin6.sin6_family = AF_INET6;
- addr.sin6.sin6_addr = hid6.sin6_addr;
- }
- else
-#endif /* NETINET6 */
- {
- /* try it as a host name (avoid MX lookup) */
- hp = sm_gethostbyname(&host[1], family);
- if (hp == NULL && p[-1] == '.')
- {
-#if NAMED_BIND
- int oldopts = _res.options;
-
- _res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
-#endif /* NAMED_BIND */
- p[-1] = '\0';
- hp = sm_gethostbyname(&host[1],
- family);
- p[-1] = '.';
-#if NAMED_BIND
- _res.options = oldopts;
-#endif /* NAMED_BIND */
- }
- *p = ']';
- goto gothostent;
- }
- *p = ']';
- }
- if (p == NULL)
- {
- extern char MsgBuf[];
-
- 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;
- }
- }
- else
- {
- /* contortion to get around SGI cc complaints */
- {
- p = &host[strlen(host) - 1];
- hp = sm_gethostbyname(host, family);
- if (hp == NULL && *p == '.')
- {
-#if NAMED_BIND
- int oldopts = _res.options;
-
- _res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
-#endif /* NAMED_BIND */
- *p = '\0';
- hp = sm_gethostbyname(host, family);
- *p = '.';
-#if NAMED_BIND
- _res.options = oldopts;
-#endif /* NAMED_BIND */
- }
- }
-gothostent:
- if (hp == NULL)
- {
-#if NAMED_BIND
- /* check for name server timeouts */
-# if NETINET6
- if (WorkAroundBrokenAAAA && family == AF_INET6 &&
- errno == ETIMEDOUT)
- {
- /*
- ** An attempt with family AF_INET may
- ** succeed By skipping the next section
- ** of code, we will try AF_INET before
- ** failing.
- */
-
- if (tTd(16, 10))
- sm_dprintf("makeconnection: WorkAroundBrokenAAAA: Trying AF_INET lookup (AF_INET6 failed)\n");
- }
- else
-# endif /* NETINET6 */
- {
- if (errno == ETIMEDOUT ||
-# if _FFR_GETHBN_ExFILE
-# ifdef EMFILE
- errno == EMFILE ||
-# endif /* EMFILE */
-# ifdef ENFILE
- errno == ENFILE ||
-# endif /* ENFILE */
-# endif /* _FFR_GETHBN_ExFILE */
- h_errno == TRY_AGAIN ||
- (errno == ECONNREFUSED && UseNameServer))
- {
- save_errno = errno;
- mci_setstat(mci, EX_TEMPFAIL,
- "4.4.3", NULL);
- errno = save_errno;
- return EX_TEMPFAIL;
- }
- }
-#endif /* NAMED_BIND */
-#if NETINET6
- /*
- ** Try v6 first, then fall back to v4.
- ** If we found a v6 address, but no v4
- ** 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);
- errno = save_errno;
- return EX_NOHOST;
- }
- 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);
- break;
-#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))
- {
- 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;
- }
- memmove(addr.sa.sa_data, hp->h_addr, hp->h_length);
- break;
- }
- addrno = 1;
- }
-
- /*
- ** Determine the port number.
- */
-
- 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");
- port = htons(25);
- }
- else
- port = sp->s_port;
-#endif /* NO_GETSERVBYNAME */
- }
-
-#if NETINET6
- if (addr.sa.sa_family == AF_INET6 &&
- IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr) &&
- ClientSettings[AF_INET].d_addr.sa.sa_family != 0)
- {
- /*
- ** Ignore mapped IPv4 address since
- ** there is a ClientPortOptions setting
- ** for IPv4.
- */
-
- goto nextaddr;
- }
-#endif /* NETINET6 */
-
- switch (addr.sa.sa_family)
- {
-#if NETINET
- case AF_INET:
- addr.sin.sin_port = port;
- addrlen = sizeof(struct sockaddr_in);
- break;
-#endif /* NETINET */
-
-#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 */
- memmove(TSEL((struct sockaddr_iso *) &addr), (char *) &port, 2);
- addrlen = sizeof(struct sockaddr_iso);
- break;
-#endif /* NETISO */
-
- default:
- syserr("Can't connect to address family %d", addr.sa.sa_family);
- mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
- errno = EINVAL;
-#if NETINET6
- if (hp != NULL)
- freehostent(hp);
-#endif /* NETINET6 */
- return EX_NOHOST;
- }
-
- /*
- ** Try to actually open the connection.
- */
-
-#if XLA
- /* if too many connections, don't bother trying */
- if (!xla_noqueue_ok(host))
- {
-# if NETINET6
- if (hp != NULL)
- freehostent(hp);
-# endif /* NETINET6 */
- return EX_TEMPFAIL;
- }
-#endif /* XLA */
-
- for (;;)
- {
- if (tTd(16, 1))
- sm_dprintf("makeconnection (%s [%s].%d (%d))\n",
- host, anynet_ntoa(&addr), ntohs(port),
- (int) addr.sa.sa_family);
-
- /* save for logging */
- CurHostAddr = addr;
-
-#if HASRRESVPORT
- if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags))
- {
- int rport = IPPORT_RESERVED - 1;
-
- s = rresvport(&rport);
- }
- else
-#endif /* HASRRESVPORT */
- {
- s = socket(addr.sa.sa_family, SOCK_STREAM, 0);
- }
- if (s < 0)
- {
- save_errno = errno;
- syserr("makeconnection: cannot create socket");
-#if XLA
- xla_host_end(host);
-#endif /* XLA */
- mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
-#if NETINET6
- if (hp != NULL)
- freehostent(hp);
-#endif /* NETINET6 */
- errno = save_errno;
- return EX_TEMPFAIL;
- }
-
-#ifdef SO_SNDBUF
- if (ClientSettings[family].d_tcpsndbufsize > 0)
- {
- if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
- (char *) &ClientSettings[family].d_tcpsndbufsize,
- sizeof(ClientSettings[family].d_tcpsndbufsize)) < 0)
- syserr("makeconnection: setsockopt(SO_SNDBUF)");
- }
-#endif /* SO_SNDBUF */
-#ifdef SO_RCVBUF
- if (ClientSettings[family].d_tcprcvbufsize > 0)
- {
- if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
- (char *) &ClientSettings[family].d_tcprcvbufsize,
- sizeof(ClientSettings[family].d_tcprcvbufsize)) < 0)
- syserr("makeconnection: setsockopt(SO_RCVBUF)");
- }
-#endif /* SO_RCVBUF */
-
- if (tTd(16, 1))
- sm_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) /* for debugging */
- (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
- 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));
-#if NETINET6
- if (hp != NULL)
- freehostent(hp);
-#endif /* NETINET6 */
- errno = save_errno;
- return EX_TEMPFAIL;
- }
- }
-
- /*
- ** Linux seems to hang in connect for 90 minutes (!!!).
- ** Time out the connect to avoid this problem.
- */
-
- if (setjmp(CtxConnectTimeout) == 0)
- {
- int i;
-
- if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0)
- ev = sm_setevent(TimeOuts.to_iconnect,
- connecttimeout, 0);
- else if (TimeOuts.to_connect != 0)
- ev = sm_setevent(TimeOuts.to_connect,
- connecttimeout, 0);
- else
- ev = NULL;
-
- 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 */
- }
- if (tTd(16, 1))
- sm_dprintf("Connecting to [%s]...\n", anynet_ntoa(&addr));
- i = connect(s, (struct sockaddr *) &addr, addrlen);
- save_errno = errno;
- if (ev != NULL)
- sm_clrevent(ev);
- if (i >= 0)
- break;
- }
- else
- save_errno = errno;
-
- /* couldn't connect.... figure out why */
- (void) close(s);
-
- /* if running demand-dialed connection, try again */
- if (DialDelay > 0 && firstconnect &&
- bitnset(M_DIALDELAY, mci->mci_mailer->m_flags))
- {
- if (tTd(16, 1))
- sm_dprintf("Connect failed (%s); trying again...\n",
- sm_errstring(save_errno));
- firstconnect = false;
- (void) sleep(DialDelay);
- continue;
- }
-
- if (LogLevel > 13)
- sm_syslog(LOG_INFO, e->e_id,
- "makeconnection (%s [%s]) failed: %s",
- host, anynet_ntoa(&addr),
- sm_errstring(save_errno));
-
-#if NETINET6
-nextaddr:
-#endif /* NETINET6 */
- if (hp != NULL && hp->h_addr_list[addrno] != NULL &&
- (enough == 0 || curtime() < enough))
- {
- if (tTd(16, 1))
- sm_dprintf("Connect failed (%s); trying new address....\n",
- sm_errstring(save_errno));
- 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:
- 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))
- sm_dprintf("Connect failed (%s); retrying with AF_INET....\n",
- sm_errstring(save_errno));
- v6found = true;
- family = AF_INET;
- if (hp != NULL)
- {
- freehostent(hp);
- hp = NULL;
- }
- goto v4retry;
- }
- v6tempfail:
-#endif /* NETINET6 */
- /* couldn't open connection */
-#if NETINET6
- /* Don't clobber an already saved errno from v4retry */
- if (errno > 0)
-#endif /* NETINET6 */
- save_errno = errno;
- if (tTd(16, 1))
- sm_dprintf("Connect failed (%s)\n",
- sm_errstring(save_errno));
-#if XLA
- xla_host_end(host);
-#endif /* XLA */
- mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
-#if NETINET6
- if (hp != NULL)
- freehostent(hp);
-#endif /* NETINET6 */
- errno = save_errno;
- return EX_TEMPFAIL;
- }
-
-#if NETINET6
- if (hp != NULL)
- {
- freehostent(hp);
- hp = NULL;
- }
-#endif /* NETINET6 */
-
- /* connection ok, put it into canonical form */
- mci->mci_out = NULL;
- if ((mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &s,
- SM_IO_WRONLY_B, NULL)) == NULL ||
- (s = dup(s)) < 0 ||
- (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &s,
- SM_IO_RDONLY_B, NULL)) == NULL)
- {
- save_errno = errno;
- syserr("cannot open SMTP client channel, fd=%d", s);
- mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
- if (mci->mci_out != NULL)
- (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
- (void) close(s);
- errno = save_errno;
- return EX_TEMPFAIL;
- }
- sm_io_automode(mci->mci_out, mci->mci_in);
-
- /* set {client_flags} */
- if (ClientSettings[addr.sa.sa_family].d_mflags != NULL)
- {
- macdefine(&mci->mci_macro, A_PERM,
- macid("{client_flags}"),
- ClientSettings[addr.sa.sa_family].d_mflags);
- }
- else
- macdefine(&mci->mci_macro, A_PERM,
- macid("{client_flags}"), "");
-
- /* "add" {client_flags} to bitmap */
- if (bitnset(D_IFNHELO, ClientSettings[addr.sa.sa_family].d_flags))
- {
- /* look for just this one flag */
- setbitn(D_IFNHELO, d_flags);
- }
-
- /* find out name for Interface through which we connect */
- len = sizeof(addr);
- if (getsockname(s, &addr.sa, &len) == 0)
- {
- char *name;
- char family[5];
-
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{if_addr_out}"), anynet_ntoa(&addr));
- (void) sm_snprintf(family, sizeof(family), "%d",
- addr.sa.sa_family);
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{if_family_out}"), family);
-
- name = hostnamebyanyaddr(&addr);
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{if_name_out}"), name);
- 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
- {
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{if_name_out}"), NULL);
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{if_addr_out}"), NULL);
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{if_family_out}"), NULL);
- }
-
- /* Use the configured HeloName as appropriate */
- if (HeloName != NULL && HeloName[0] != '\0')
- mci->mci_heloname = newstr(HeloName);
-
- mci_setstat(mci, EX_OK, NULL, NULL);
- return EX_OK;
-}
-
-static void
-connecttimeout(ignore)
- int ignore;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(CtxConnectTimeout, 1);
-}
-/*
-** 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 %s",
- mux_path);
- 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 %s too long",
- mux_path);
-
- /* XXX why TEMPFAIL but 5.x.y ? */
- mci_setstat(mci, EX_TEMPFAIL, "5.3.5", NULL);
- errno = ENAMETOOLONG;
- return EX_UNAVAILABLE;
- }
- (void) sm_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 %s",
- mux_path);
- 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 = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &sock, SM_IO_WRONLY_B, NULL))
- == NULL
- || (sock = dup(sock)) < 0 ||
- (mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &sock, SM_IO_RDONLY_B, NULL))
- == NULL)
- {
- save_errno = errno;
- syserr("cannot open SMTP client channel, fd=%d", sock);
- mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
- if (mci->mci_out != NULL)
- (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
- (void) close(sock);
- errno = save_errno;
- return EX_TEMPFAIL;
- }
- sm_io_automode(mci->mci_out, mci->mci_in);
-
- mci_setstat(mci, EX_OK, NULL, NULL);
- errno = 0;
- return EX_OK;
-}
-#endif /* NETUNIX */
-/*
-** SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** closes control socket, exits.
-*/
-
-void
-shutdown_daemon()
-{
- int i;
- char *reason;
-
- sm_allsignals(true);
-
- reason = ShutdownRequest;
- ShutdownRequest = NULL;
- PendingSignal = 0;
-
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, CurEnv->e_id, "stopping daemon, reason=%s",
- reason == NULL ? "implicit call" : reason);
-
- FileName = NULL;
- closecontrolsocket(true);
-#if XLA
- xla_all_end();
-#endif /* XLA */
-
- for (i = 0; i < NDaemons; i++)
- {
- if (Daemons[i].d_socket >= 0)
- {
- (void) close(Daemons[i].d_socket);
- Daemons[i].d_socket = -1;
-
-#if _FFR_DAEMON_NETUNIX
-# if NETUNIX
- /* Remove named sockets */
- if (Daemons[i].d_addr.sa.sa_family == AF_UNIX)
- {
- int rval;
- long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_MUSTOWN|SFF_EXECOK|SFF_CREAT;
-
- /* if not safe, don't use it */
- rval = safefile(Daemons[i].d_addr.sunix.sun_path,
- RunAsUid, RunAsGid,
- RunAsUserName, sff,
- S_IRUSR|S_IWUSR, NULL);
- if (rval == 0 &&
- unlink(Daemons[i].d_addr.sunix.sun_path) < 0)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "Could not remove daemon %s socket: %s: %s",
- Daemons[i].d_name,
- Daemons[i].d_addr.sunix.sun_path,
- sm_errstring(errno));
- }
- }
-# endif /* NETUNIX */
-#endif /* _FFR_DAEMON_NETUNIX */
- }
- }
-
- finis(false, true, EX_OK);
-}
-/*
-** RESTART_DAEMON -- Performs a clean restart of the daemon
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** restarts the daemon or exits if restart fails.
-*/
-
-/* Make a non-DFL/IGN signal a noop */
-#define SM_NOOP_SIGNAL(sig, old) \
-do \
-{ \
- (old) = sm_signal((sig), sm_signal_noop); \
- if ((old) == SIG_IGN || (old) == SIG_DFL) \
- (void) sm_signal((sig), (old)); \
-} while (0)
-
-void
-restart_daemon()
-{
- bool drop;
- int save_errno;
- char *reason;
- sigfunc_t ignore, oalrm, ousr1;
- extern int DtableSize;
-
- /* clear the events to turn off SIGALRMs */
- sm_clear_events();
- sm_allsignals(true);
-
- reason = RestartRequest;
- RestartRequest = NULL;
- PendingSignal = 0;
-
- if (SaveArgv[0][0] != '/')
- {
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID,
- "could not restart: need full path");
- finis(false, true, EX_OSFILE);
- /* NOTREACHED */
- }
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, NOQID, "restarting %s due to %s",
- SaveArgv[0],
- reason == NULL ? "implicit call" : reason);
-
- closecontrolsocket(true);
-#if SM_CONF_SHM
- cleanup_shm(DaemonPid == getpid());
-#endif /* SM_CONF_SHM */
-
- /* close locked pid file */
- close_sendmail_pid();
-
- /*
- ** Want to drop to the user who started the process in all cases
- ** *but* when running as "smmsp" for the clientmqueue queue run
- ** daemon. In that case, UseMSP will be true, RunAsUid should not
- ** be root, and RealUid should be either 0 or RunAsUid.
- */
-
- drop = !(UseMSP && RunAsUid != 0 &&
- (RealUid == 0 || RealUid == RunAsUid));
-
- if (drop_privileges(drop) != EX_OK)
- {
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID,
- "could not drop privileges: %s",
- sm_errstring(errno));
- finis(false, true, EX_OSERR);
- /* NOTREACHED */
- }
-
- sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
-
- /*
- ** Need to allow signals before execve() to make them "harmless".
- ** However, the default action can be "terminate", so it isn't
- ** really harmless. Setting signals to IGN will cause them to be
- ** ignored in the new process to, so that isn't a good alternative.
- */
-
- SM_NOOP_SIGNAL(SIGALRM, oalrm);
- SM_NOOP_SIGNAL(SIGCHLD, ignore);
- SM_NOOP_SIGNAL(SIGHUP, ignore);
- SM_NOOP_SIGNAL(SIGINT, ignore);
- SM_NOOP_SIGNAL(SIGPIPE, ignore);
- SM_NOOP_SIGNAL(SIGTERM, ignore);
-#ifdef SIGUSR1
- SM_NOOP_SIGNAL(SIGUSR1, ousr1);
-#endif /* SIGUSR1 */
-
- /* Turn back on signals */
- sm_allsignals(false);
-
- (void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
- save_errno = errno;
-
- /* block signals again and restore needed signals */
- sm_allsignals(true);
-
- /* For finis() events */
- (void) sm_signal(SIGALRM, oalrm);
-
-#ifdef SIGUSR1
- /* For debugging finis() */
- (void) sm_signal(SIGUSR1, ousr1);
-#endif /* SIGUSR1 */
-
- errno = save_errno;
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %s",
- SaveArgv[0], sm_errstring(errno));
- finis(false, true, EX_OSFILE);
- /* NOTREACHED */
-}
-/*
-** MYHOSTNAME -- return the name of this host.
-**
-** Parameters:
-** hostbuf -- a place to return the name of this host.
-** size -- the size of hostbuf.
-**
-** Returns:
-** A list of aliases for this host.
-**
-** Side Effects:
-** Adds numeric codes to $=w.
-*/
-
-struct hostent *
-myhostname(hostbuf, size)
- char hostbuf[];
- int size;
-{
- register struct hostent *hp;
-
- if (gethostname(hostbuf, size) < 0 || hostbuf[0] == '\0')
- (void) sm_strlcpy(hostbuf, "localhost", size);
- hp = sm_gethostbyname(hostbuf, InetMode);
-#if NETINET && NETINET6
- if (hp == NULL && InetMode == AF_INET6)
- {
- /*
- ** It's possible that this IPv6 enabled machine doesn't
- ** actually have any IPv6 interfaces and, therefore, no
- ** IPv6 addresses. Fall back to AF_INET.
- */
-
- hp = sm_gethostbyname(hostbuf, AF_INET);
- }
-#endif /* NETINET && NETINET6 */
- if (hp == NULL)
- return NULL;
- if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL)
- (void) cleanstrcpy(hostbuf, hp->h_name, size);
-
-#if NETINFO
- if (strchr(hostbuf, '.') == NULL)
- {
- char *domainname;
-
- domainname = ni_propval("/locations", NULL, "resolver",
- "domain", '\0');
- if (domainname != NULL &&
- strlen(domainname) + strlen(hostbuf) + 1 < size)
- (void) sm_strlcat2(hostbuf, ".", domainname, size);
- }
-#endif /* NETINFO */
-
- /*
- ** If there is still no dot in the name, try looking for a
- ** dotted alias.
- */
-
- if (strchr(hostbuf, '.') == NULL)
- {
- char **ha;
-
- for (ha = hp->h_aliases; ha != NULL && *ha != NULL; ha++)
- {
- if (strchr(*ha, '.') != NULL)
- {
- (void) cleanstrcpy(hostbuf, *ha, size - 1);
- hostbuf[size - 1] = '\0';
- break;
- }
- }
- }
-
- /*
- ** If _still_ no dot, wait for a while and try again -- it is
- ** possible that some service is starting up. This can result
- ** in excessive delays if the system is badly configured, but
- ** there really isn't a way around that, particularly given that
- ** the config file hasn't been read at this point.
- ** All in all, a bit of a mess.
- */
-
- if (strchr(hostbuf, '.') == NULL &&
- !getcanonname(hostbuf, size, true, NULL))
- {
- sm_syslog(LOG_CRIT, NOQID,
- "My unqualified host name (%s) unknown; sleeping for retry",
- hostbuf);
- message("My unqualified host name (%s) unknown; sleeping for retry",
- hostbuf);
- (void) sleep(60);
- if (!getcanonname(hostbuf, size, true, NULL))
- {
- sm_syslog(LOG_ALERT, NOQID,
- "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;
-}
-/*
-** ADDRCMP -- compare two host addresses
-**
-** Parameters:
-** hp -- hostent structure for the first address
-** ha -- actual first address
-** sa -- second address
-**
-** Returns:
-** 0 -- if ha and sa match
-** else -- they don't match
-*/
-
-static int
-addrcmp(hp, ha, sa)
- struct hostent *hp;
- char *ha;
- SOCKADDR *sa;
-{
-#if NETINET6
- unsigned char *a;
-#endif /* NETINET6 */
-
- switch (sa->sa.sa_family)
- {
-#if NETINET
- case AF_INET:
- if (hp->h_addrtype == AF_INET)
- return memcmp(ha, (char *) &sa->sin.sin_addr, INADDRSZ);
- break;
-#endif /* NETINET */
-
-#if NETINET6
- case AF_INET6:
- a = (unsigned 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 associated with a file descriptor
-**
-** Uses RFC1413 protocol to try to get info from the other end.
-**
-** Parameters:
-** fd -- the descriptor
-** may_be_forged -- an outage that is set to true if the
-** forward lookup of RealHostName does not match
-** RealHostAddr; set to false if they do match.
-**
-** Returns:
-** The user@host information associated with this descriptor.
-*/
-
-static jmp_buf CtxAuthTimeout;
-
-static void
-authtimeout(ignore)
- int ignore;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(CtxAuthTimeout, 1);
-}
-
-char *
-getauthinfo(fd, may_be_forged)
- int fd;
- bool *may_be_forged;
-{
- unsigned short SM_NONVOLATILE port = 0;
- SOCKADDR_LEN_T falen;
- register char *volatile p = NULL;
- SOCKADDR la;
- SOCKADDR_LEN_T lalen;
-#ifndef NO_GETSERVBYNAME
- register struct servent *sp;
-# if NETINET
- static unsigned short port4 = 0;
-# endif /* NETINET */
-# if NETINET6
- static unsigned short port6 = 0;
-# endif /* NETINET6 */
-#endif /* ! NO_GETSERVBYNAME */
- volatile int s;
- int i = 0;
- size_t len;
- SM_EVENT *ev;
- int nleft;
- struct hostent *hp;
- char *ostype = NULL;
- char **ha;
- char ibuf[MAXNAME + 1];
- static char hbuf[MAXNAME + MAXAUTHINFO + 11];
-
- *may_be_forged = false;
- falen = sizeof(RealHostAddr);
- if (isatty(fd) || (i = getpeername(fd, &RealHostAddr.sa, &falen)) < 0 ||
- falen <= 0 || RealHostAddr.sa.sa_family == 0)
- {
- 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) sm_strlcpyn(hbuf, sizeof(hbuf), 2, RealUserName,
- "@localhost");
- if (tTd(9, 1))
- sm_dprintf("getauthinfo: %s\n", hbuf);
- return hbuf;
- }
-
- if (RealHostName == NULL)
- {
- /* translate that to a host name */
- RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
- if (strlen(RealHostName) > MAXNAME)
- RealHostName[MAXNAME] = '\0'; /* XXX - 1 ? */
- }
-
- /* cross check RealHostName with forward DNS lookup */
- if (anynet_ntoa(&RealHostAddr)[0] != '[' &&
- RealHostName[0] != '[')
- {
- int family;
-
- family = RealHostAddr.sa.sa_family;
-#if NETINET6 && NEEDSGETIPNODE
- /*
- ** If RealHostAddr is an IPv6 connection with an
- ** IPv4-mapped address, we need RealHostName's IPv4
- ** address(es) for addrcmp() to compare against
- ** RealHostAddr.
- **
- ** Actually, we only need to do this for systems
- ** which NEEDSGETIPNODE since the real getipnodebyname()
- ** already does V4MAPPED address via the AI_V4MAPPEDCFG
- ** flag. A better fix to this problem is to add this
- ** functionality to our stub getipnodebyname().
- */
-
- if (family == AF_INET6 &&
- IN6_IS_ADDR_V4MAPPED(&RealHostAddr.sin6.sin6_addr))
- family = AF_INET;
-#endif /* NETINET6 && NEEDSGETIPNODE */
-
- /* try to match the reverse against the forward lookup */
- hp = sm_gethostbyname(RealHostName, family);
- if (hp == NULL)
- {
- /* XXX: Could be a temporary error on forward lookup */
- *may_be_forged = true;
- }
- else
- {
- for (ha = hp->h_addr_list; *ha != NULL; ha++)
- {
- if (addrcmp(hp, *ha, &RealHostAddr) == 0)
- break;
- }
- *may_be_forged = *ha == NULL;
-#if NETINET6
- freehostent(hp);
- hp = NULL;
-#endif /* NETINET6 */
- }
- }
-
- if (TimeOuts.to_ident == 0)
- goto noident;
-
- lalen = sizeof(la);
- switch (RealHostAddr.sa.sa_family)
- {
-#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) sm_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 foreign address */
-# ifdef NO_GETSERVBYNAME
- RealHostAddr.sin.sin_port = htons(113);
-# else /* NO_GETSERVBYNAME */
-
- /*
- ** getservbyname() consumes about 5% of the time
- ** when receiving a small message (almost all of the time
- ** spent in this routine).
- ** Hence we store the port in a static variable
- ** to save this time.
- ** The portnumber shouldn't change very often...
- ** This code makes the assumption that the port number
- ** is not 0.
- */
-
- if (port4 == 0)
- {
- sp = getservbyname("auth", "tcp");
- if (sp != NULL)
- port4 = sp->s_port;
- else
- port4 = htons(113);
- }
- RealHostAddr.sin.sin_port = port4;
- break;
-# endif /* NO_GETSERVBYNAME */
-#endif /* NETINET */
-
-#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) sm_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 */
- if (port6 == 0)
- {
- sp = getservbyname("auth", "tcp");
- if (sp != NULL)
- port6 = sp->s_port;
- else
- port6 = htons(113);
- }
- RealHostAddr.sin6.sin6_port = port6;
- break;
-# endif /* NO_GETSERVBYNAME */
-#endif /* NETINET6 */
- default:
- /* no ident info */
- goto noident;
- }
-
- s = -1;
- if (setjmp(CtxAuthTimeout) != 0)
- {
- if (s >= 0)
- (void) close(s);
- goto noident;
- }
-
- /* put a timeout around the whole thing */
- ev = sm_setevent(TimeOuts.to_ident, authtimeout, 0);
-
- /* connect to foreign IDENT server using same address as SMTP socket */
- s = socket(la.sa.sa_family, SOCK_STREAM, 0);
- if (s < 0)
- {
- sm_clrevent(ev);
- goto noident;
- }
- if (bind(s, &la.sa, lalen) < 0 ||
- connect(s, &RealHostAddr.sa, lalen) < 0)
- goto closeident;
-
- if (tTd(9, 10))
- sm_dprintf("getauthinfo: sent %s", ibuf);
-
- /* send query */
- if (write(s, ibuf, strlen(ibuf)) < 0)
- goto closeident;
-
- /* get result */
- p = &ibuf[0];
- nleft = sizeof(ibuf) - 1;
- while ((i = read(s, p, nleft)) > 0)
- {
- char *s;
-
- p += i;
- nleft -= i;
- *p = '\0';
- if ((s = strchr(ibuf, '\n')) != NULL)
- {
- if (p > s + 1)
- {
- p = s + 1;
- *p = '\0';
- }
- break;
- }
- if (nleft <= 0)
- break;
- }
- (void) close(s);
- sm_clrevent(ev);
- if (i < 0 || p == &ibuf[0])
- goto noident;
-
- if (p >= &ibuf[2] && *--p == '\n' && *--p == '\r')
- p--;
- *++p = '\0';
-
- if (tTd(9, 3))
- sm_dprintf("getauthinfo: got %s\n", ibuf);
-
- /* parse result */
- p = strchr(ibuf, ':');
- if (p == NULL)
- {
- /* malformed response */
- goto noident;
- }
- while (isascii(*++p) && isspace(*p))
- continue;
- if (sm_strncasecmp(p, "userid", 6) != 0)
- {
- /* presumably an error string */
- goto noident;
- }
- p += 6;
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p++ != ':')
- {
- /* either useridxx or malformed response */
- goto noident;
- }
-
- /* p now points to the OSTYPE field */
- while (isascii(*p) && isspace(*p))
- p++;
- ostype = p;
- p = strchr(p, ':');
- if (p == NULL)
- {
- /* malformed response */
- goto noident;
- }
- else
- {
- char *charset;
-
- *p = '\0';
- charset = strchr(ostype, ',');
- if (charset != NULL)
- *charset = '\0';
- }
-
- /* 1413 says don't do this -- but it's broken otherwise */
- while (isascii(*++p) && isspace(*p))
- continue;
-
- /* p now points to the authenticated name -- copy carefully */
- if (sm_strncasecmp(ostype, "other", 5) == 0 &&
- (ostype[5] == ' ' || ostype[5] == '\0'))
- {
- (void) sm_strlcpy(hbuf, "IDENT:", sizeof(hbuf));
- cleanstrcpy(&hbuf[6], p, MAXAUTHINFO);
- }
- else
- cleanstrcpy(hbuf, p, MAXAUTHINFO);
- len = strlen(hbuf);
- (void) sm_strlcpyn(&hbuf[len], sizeof(hbuf) - len, 2, "@",
- RealHostName == NULL ? "localhost" : RealHostName);
- goto postident;
-
-closeident:
- (void) close(s);
- sm_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))
- sm_dprintf("getauthinfo: NULL\n");
- return NULL;
- }
- (void) sm_strlcpy(hbuf, RealHostName, sizeof(hbuf));
-
-postident:
-#if IP_SRCROUTE
-# ifndef GET_IPOPT_DST
-# define GET_IPOPT_DST(dst) (dst)
-# endif /* ! GET_IPOPT_DST */
- /*
- ** Extract IP source routing information.
- **
- ** Format of output for a connection from site a through b
- ** through c to d:
- ** loose: @site-c@site-b:site-a
- ** strict: !@site-c@site-b:site-a
- **
- ** o - pointer within ipopt_list structure.
- ** q - pointer within ls/ss rr route data
- ** p - pointer to hbuf
- */
-
- if (RealHostAddr.sa.sa_family == AF_INET)
- {
- SOCKOPT_LEN_T ipoptlen;
- int j;
- unsigned char *q;
- unsigned char *o;
- int l;
- struct IPOPTION ipopt;
-
- ipoptlen = sizeof(ipopt);
- if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS,
- (char *) &ipopt, &ipoptlen) < 0)
- goto noipsr;
- if (ipoptlen == 0)
- goto noipsr;
- o = (unsigned char *) ipopt.IP_LIST;
- while (o != NULL && o < (unsigned char *) &ipopt + ipoptlen)
- {
- switch (*o)
- {
- case IPOPT_EOL:
- o = NULL;
- break;
-
- case IPOPT_NOP:
- o++;
- break;
-
- case IPOPT_SSRR:
- case IPOPT_LSRR:
- /*
- ** Source routing.
- ** o[0] is the option type (loose/strict).
- ** o[1] is the length of this option,
- ** including option type and
- ** length.
- ** o[2] is the pointer into the route
- ** data.
- ** o[3] begins the route data.
- */
-
- p = &hbuf[strlen(hbuf)];
- l = sizeof(hbuf) - (hbuf - p) - 6;
- (void) sm_snprintf(p, SPACELEFT(hbuf, p),
- " [%s@%.*s",
- *o == IPOPT_SSRR ? "!" : "",
- l > 240 ? 120 : l / 2,
- inet_ntoa(GET_IPOPT_DST(ipopt.IP_DST)));
- i = strlen(p);
- p += i;
- l -= strlen(p);
-
- j = o[1] / sizeof(struct in_addr) - 1;
-
- /* q skips length and router pointer to data */
- q = &o[3];
- for ( ; j >= 0; j--)
- {
- struct in_addr addr;
-
- memcpy(&addr, q, sizeof(addr));
- (void) sm_snprintf(p,
- SPACELEFT(hbuf, p),
- "%c%.*s",
- j != 0 ? '@' : ':',
- l > 240 ? 120 :
- j == 0 ? l : l / 2,
- inet_ntoa(addr));
- i = strlen(p);
- p += i;
- l -= i + 1;
- q += sizeof(struct in_addr);
- }
- o += o[1];
- break;
-
- default:
- /* Skip over option */
- o += o[1];
- break;
- }
- }
- (void) sm_snprintf(p, SPACELEFT(hbuf, p), "]");
- goto postipsr;
- }
-
-noipsr:
-#endif /* IP_SRCROUTE */
- if (RealHostName != NULL && RealHostName[0] != '[')
- {
- p = &hbuf[strlen(hbuf)];
- (void) sm_snprintf(p, SPACELEFT(hbuf, p), " [%.100s]",
- anynet_ntoa(&RealHostAddr));
- }
- if (*may_be_forged)
- {
- p = &hbuf[strlen(hbuf)];
- (void) sm_strlcpy(p, " (may be forged)", SPACELEFT(hbuf, p));
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{client_resolve}"), "FORGED");
- }
-
-#if IP_SRCROUTE
-postipsr:
-#endif /* IP_SRCROUTE */
-
- /* 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 (tTd(9, 1))
- sm_dprintf("getauthinfo: %s\n", hbuf);
- return hbuf;
-}
-/*
-** HOST_MAP_LOOKUP -- turn a hostname into canonical form
-**
-** Parameters:
-** map -- a pointer to this map.
-** name -- the (presumably unqualified) hostname.
-** av -- unused -- for compatibility with other mapping
-** functions.
-** statp -- an exit status (out parameter) -- set to
-** EX_TEMPFAIL if the name server is unavailable.
-**
-** Returns:
-** The mapping, if found.
-** NULL if no mapping found.
-**
-** Side Effects:
-** Looks up the host specified in hbuf. If it is not
-** the canonical name for that host, return the canonical
-** name (unless MF_MATCHONLY is set, which will cause the
-** status only to be returned).
-*/
-
-char *
-host_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- register struct hostent *hp;
-#if NETINET
- struct in_addr in_addr;
-#endif /* NETINET */
-#if NETINET6
- struct in6_addr in6_addr;
-#endif /* NETINET6 */
- char *cp, *ans = NULL;
- register STAB *s;
- time_t now;
-#if NAMED_BIND
- time_t SM_NONVOLATILE retrans = 0;
- int SM_NONVOLATILE retry = 0;
-#endif /* NAMED_BIND */
- char hbuf[MAXNAME + 1];
-
- /*
- ** See if we have already looked up this name. If so, just
- ** return it (unless expired).
- */
-
- now = curtime();
- s = stab(name, ST_NAMECANON, ST_ENTER);
- if (bitset(NCF_VALID, s->s_namecanon.nc_flags) &&
- s->s_namecanon.nc_exp >= now)
- {
- if (tTd(9, 1))
- sm_dprintf("host_map_lookup(%s) => CACHE %s\n",
- name,
- s->s_namecanon.nc_cname == NULL
- ? "NULL"
- : s->s_namecanon.nc_cname);
- errno = s->s_namecanon.nc_errno;
- SM_SET_H_ERRNO(s->s_namecanon.nc_herrno);
- *statp = s->s_namecanon.nc_stat;
- if (*statp == EX_TEMPFAIL)
- {
- CurEnv->e_status = "4.4.3";
- message("851 %s: Name server timeout",
- shortenstring(name, 33));
- }
- if (*statp != EX_OK)
- return NULL;
- 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);
- return NULL;
- }
- if (bitset(MF_MATCHONLY, map->map_mflags))
- cp = map_rewrite(map, name, strlen(name), NULL);
- else
- cp = map_rewrite(map,
- s->s_namecanon.nc_cname,
- strlen(s->s_namecanon.nc_cname),
- av);
- return cp;
- }
-
- /*
- ** If we are running without a regular network connection (usually
- ** dial-on-demand) and we are just queueing, we want to avoid DNS
- ** lookups because those could try to connect to a server.
- */
-
- if (CurEnv->e_sendmode == SM_DEFER &&
- bitset(MF_DEFER, map->map_mflags))
- {
- if (tTd(9, 1))
- sm_dprintf("host_map_lookup(%s) => DEFERRED\n", name);
- *statp = EX_TEMPFAIL;
- return NULL;
- }
-
- /*
- ** If first character is a bracket, then it is an address
- ** lookup. Address is copied into a temporary buffer to
- ** strip the brackets and to preserve name if address is
- ** unknown.
- */
-
- if (tTd(9, 1))
- sm_dprintf("host_map_lookup(%s) => ", name);
-#if NAMED_BIND
- if (map->map_timeout > 0)
- {
- retrans = _res.retrans;
- _res.retrans = map->map_timeout;
- }
- if (map->map_retry > 0)
- {
- retry = _res.retry;
- _res.retry = map->map_retry;
- }
-#endif /* NAMED_BIND */
-
- /* set default TTL */
- s->s_namecanon.nc_exp = now + SM_DEFAULT_TTL;
- if (*name != '[')
- {
- int ttl;
-
- (void) sm_strlcpy(hbuf, name, sizeof(hbuf));
- if (getcanonname(hbuf, sizeof(hbuf) - 1, !HasWildcardMX, &ttl))
- {
- ans = hbuf;
- if (ttl > 0)
- s->s_namecanon.nc_exp = now + SM_MIN(ttl,
- SM_DEFAULT_TTL);
- }
- }
- else
- {
- if ((cp = strchr(name, ']')) == NULL)
- {
- if (tTd(9, 1))
- sm_dprintf("FAILED\n");
- 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 &&
- anynet_pton(AF_INET6, &name[1], &in6_addr) == 1)
- hp = sm_gethostbyaddr((char *)&in6_addr,
- IN6ADDRSZ, AF_INET6);
-#endif /* NETINET6 */
- *cp = ']';
-
- if (hp != NULL)
- {
- /* found a match -- copy out */
- ans = denlstring((char *) hp->h_name, true, true);
-#if NETINET6
- if (ans == hp->h_name)
- {
- static char n[MAXNAME + 1];
-
- /* hp->h_name is about to disappear */
- (void) sm_strlcpy(n, ans, sizeof(n));
- ans = n;
- }
- freehostent(hp);
- hp = NULL;
-#endif /* NETINET6 */
- }
- }
-#if NAMED_BIND
- if (map->map_timeout > 0)
- _res.retrans = retrans;
- if (map->map_retry > 0)
- _res.retry = retry;
-#endif /* NAMED_BIND */
-
- s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */
-
- /* Found an answer */
- if (ans != NULL)
- {
- s->s_namecanon.nc_stat = *statp = EX_OK;
- if (s->s_namecanon.nc_cname != NULL)
- sm_free(s->s_namecanon.nc_cname);
- s->s_namecanon.nc_cname = sm_strdup_x(ans);
- if (bitset(MF_MATCHONLY, map->map_mflags))
- cp = map_rewrite(map, name, strlen(name), NULL);
- else
- cp = map_rewrite(map, ans, strlen(ans), av);
- if (tTd(9, 1))
- sm_dprintf("FOUND %s\n", ans);
- return cp;
- }
-
-
- /* No match found */
- s->s_namecanon.nc_errno = errno;
-#if NAMED_BIND
- s->s_namecanon.nc_herrno = h_errno;
- if (tTd(9, 1))
- sm_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 */
- if (tTd(9, 1))
- sm_dprintf("FAIL\n");
- *statp = EX_NOHOST;
-#endif /* NAMED_BIND */
- s->s_namecanon.nc_stat = *statp;
- return NULL;
-}
-/*
-** HOST_MAP_INIT -- initialize host class structures
-**
-** Parameters:
-** map -- a pointer to this map.
-** args -- argument string.
-**
-** Returns:
-** true.
-*/
-
-bool
-host_map_init(map, args)
- MAP *map;
- char *args;
-{
- register char *p = args;
-
- for (;;)
- {
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p != '-')
- break;
- switch (*++p)
- {
- case 'a':
- map->map_app = ++p;
- break;
-
- case 'T':
- map->map_tapp = ++p;
- break;
-
- case 'm':
- map->map_mflags |= MF_MATCHONLY;
- break;
-
- 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;
-
- case 'd':
- {
- char *h;
-
- while (isascii(*++p) && isspace(*p))
- continue;
- h = strchr(p, ' ');
- if (h != NULL)
- *h = '\0';
- map->map_timeout = convtime(p, 's');
- if (h != NULL)
- *h = ' ';
- }
- break;
-
- case 'r':
- while (isascii(*++p) && isspace(*p))
- continue;
- map->map_retry = atoi(p);
- break;
- }
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
- }
- if (map->map_app != NULL)
- map->map_app = newstr(map->map_app);
- if (map->map_tapp != NULL)
- 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
- {
- char *d;
- size_t sz;
-
- /* Save pointer to beginning of string */
- d = dst;
-
- /* Add IPv6: protocol tag */
- sz = sm_strlcpy(dst, "IPv6:", dst_len);
- if (sz >= dst_len)
- return NULL;
- dst += sz;
- dst_len -= sz;
- ap = (char *) inet_ntop(AF_INET6, s6a, dst, dst_len);
-
- /* Restore pointer to beginning of string */
- if (ap != NULL)
- ap = d;
- }
- return ap;
-}
-
-/*
-** ANYNET_PTON -- convert printed form to network address.
-**
-** Wrapper for inet_pton() which handles IPv6: labels.
-**
-** Parameters:
-** family -- address family
-** src -- string
-** dst -- destination address structure
-**
-** Returns:
-** 1 if the address was valid
-** 0 if the address wasn't parseable
-** -1 if error
-*/
-
-int
-anynet_pton(family, src, dst)
- int family;
- const char *src;
- void *dst;
-{
- if (family == AF_INET6 && sm_strncasecmp(src, "IPv6:", 5) == 0)
- src += 5;
- return inet_pton(family, src, dst);
-}
-#endif /* NETINET6 */
-/*
-** ANYNET_NTOA -- convert a network address to printable form.
-**
-** Parameters:
-** sap -- a pointer to a sockaddr structure.
-**
-** Returns:
-** A printable version of that sockaddr.
-*/
-
-#ifdef USE_SOCK_STREAM
-
-# if NETLINK
-# include <net/if_dl.h>
-# endif /* NETLINK */
-
-char *
-anynet_ntoa(sap)
- register SOCKADDR *sap;
-{
- register char *bp;
- register char *ap;
- int l;
- static char buf[100];
-
- /* check for null/zero family */
- if (sap == NULL)
- return "NULLADDR";
- if (sap->sa.sa_family == 0)
- return "0";
-
- switch (sap->sa.sa_family)
- {
-# if NETUNIX
- case AF_UNIX:
- if (sap->sunix.sun_path[0] != '\0')
- (void) sm_snprintf(buf, sizeof(buf), "[UNIX: %.64s]",
- sap->sunix.sun_path);
- else
- (void) sm_strlcpy(buf, "[UNIX: localhost]", sizeof(buf));
- return buf;
-# endif /* NETUNIX */
-
-# if NETINET
- case AF_INET:
- 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
- case AF_LINK:
- (void) sm_snprintf(buf, sizeof(buf), "[LINK: %s]",
- link_ntoa((struct sockaddr_dl *) &sap->sa));
- return buf;
-# endif /* NETLINK */
- default:
- /* this case is needed when nothing is #defined */
- /* in order to keep the switch syntactically correct */
- break;
- }
-
- /* unknown family -- just dump bytes */
- (void) sm_snprintf(buf, sizeof(buf), "Family %d: ", sap->sa.sa_family);
- bp = &buf[strlen(buf)];
- ap = sap->sa.sa_data;
- for (l = sizeof(sap->sa.sa_data); --l >= 0; )
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), "%02x:",
- *ap++ & 0377);
- bp += 3;
- }
- *--bp = '\0';
- return buf;
-}
-/*
-** HOSTNAMEBYANYADDR -- return name of host based on address
-**
-** Parameters:
-** sap -- SOCKADDR pointer
-**
-** Returns:
-** text representation of host name.
-**
-** Side Effects:
-** none.
-*/
-
-char *
-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
- /* shorten name server timeout to avoid higher level timeouts */
- saveretry = _res.retry;
- if (_res.retry * _res.retrans > 20)
- _res.retry = 20 / _res.retrans;
-# endif /* NAMED_BIND */
-
- switch (sap->sa.sa_family)
- {
-# if NETINET
- case AF_INET:
- hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr,
- INADDRSZ, AF_INET);
- break;
-# endif /* NETINET */
-
-# 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 /* NETISO */
-
-# if NETUNIX
- case AF_UNIX:
- hp = NULL;
- break;
-# endif /* NETUNIX */
-
- default:
- hp = sm_gethostbyaddr(sap->sa.sa_data, sizeof(sap->sa.sa_data),
- sap->sa.sa_family);
- break;
- }
-
-# if NAMED_BIND
- _res.retry = saveretry;
-# 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 */
- )
- {
- char *name;
-
- name = denlstring((char *) hp->h_name, true, true);
-# if NETINET6
- if (name == hp->h_name)
- {
- static char n[MAXNAME + 1];
-
- /* Copy the string, hp->h_name is about to disappear */
- (void) sm_strlcpy(n, name, sizeof(n));
- name = n;
- }
- freehostent(hp);
-# endif /* NETINET6 */
- return name;
- }
-# endif /* NETINET || NETINET6 */
-
-# if NETINET6
- if (hp != NULL)
- {
- freehostent(hp);
- hp = NULL;
- }
-# endif /* NETINET6 */
-
-# if NETUNIX
- if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
- return "localhost";
-# endif /* NETUNIX */
- {
- static char buf[203];
-
- (void) sm_snprintf(buf, sizeof(buf), "[%.200s]",
- anynet_ntoa(sap));
- return buf;
- }
-}
-#endif /* USE_SOCK_STREAM */
diff --git a/contrib/sendmail/src/daemon.h b/contrib/sendmail/src/daemon.h
deleted file mode 100644
index d8fa291..0000000
--- a/contrib/sendmail/src/daemon.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2006 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: daemon.h,v 8.3 2006/07/13 22:57:03 ca Exp $
- */
-
-#ifndef DAEMON_H
-#define DAEMON_H 1
-
-#if DAEMON_C
-# define EXTERN
-#else
-# define EXTERN extern
-#endif
-
-/* structure to describe a daemon or a client */
-struct daemon
-{
- int d_socket; /* fd for socket */
- SOCKADDR d_addr; /* socket for incoming */
- unsigned short d_port; /* port number */
- int d_listenqueue; /* size of listen queue */
- int d_tcprcvbufsize; /* size of TCP receive buffer */
- int d_tcpsndbufsize; /* size of TCP send buffer */
- 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 */
-
- int d_dm; /* DeliveryMode */
- int d_refuseLA;
- int d_queueLA;
- int d_delayLA;
- int d_maxchildren;
-
-#if MILTER
- char *d_inputfilterlist;
- struct milter *d_inputfilters[MAXFILTERS];
-#endif /* MILTER */
-#if _FFR_SS_PER_DAEMON
- int d_supersafe;
-#endif /* _FFR_SS_PER_DAEMON */
-};
-
-typedef struct daemon DAEMON_T;
-
-EXTERN DAEMON_T Daemons[MAXDAEMONS];
-
-#define DPO_NOTSET (-1) /* daemon option (int) not set */
-/* see also sendmail.h: SuperSafe values */
-
-extern bool refuseconnections __P((ENVELOPE *, int, bool));
-
-#undef EXTERN
-#endif /* ! DAEMON_H */
diff --git a/contrib/sendmail/src/deliver.c b/contrib/sendmail/src/deliver.c
deleted file mode 100644
index ed60e47..0000000
--- a/contrib/sendmail/src/deliver.c
+++ /dev/null
@@ -1,6258 +0,0 @@
-/*
- * Copyright (c) 1998-2007 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.
- *
- */
-
-#include <sendmail.h>
-#include <sm/time.h>
-
-SM_RCSID("@(#)$Id: deliver.c,v 8.1015 2007/10/17 21:35:30 ca Exp $")
-
-#if HASSETUSERCONTEXT
-# include <login_cap.h>
-#endif /* HASSETUSERCONTEXT */
-
-#if NETINET || NETINET6
-# include <arpa/inet.h>
-#endif /* NETINET || NETINET6 */
-
-#if STARTTLS || SASL
-# include "sfsasl.h"
-#endif /* STARTTLS || SASL */
-
-static int deliver __P((ENVELOPE *, ADDRESS *));
-static void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
-static void mailfiletimeout __P((int));
-static void endwaittimeout __P((int));
-static int parse_hostsignature __P((char *, char **, MAILER *));
-static void sendenvelope __P((ENVELOPE *, int));
-static int coloncmp __P((const char *, const char *));
-
-#if STARTTLS
-static int starttls __P((MAILER *, MCI *, ENVELOPE *));
-static int endtlsclt __P((MCI *));
-#endif /* STARTTLS */
-# if STARTTLS || SASL
-static bool iscltflgset __P((ENVELOPE *, int));
-# endif /* STARTTLS || SASL */
-
-/*
-** SENDALL -- actually send all the messages.
-**
-** Parameters:
-** e -- the envelope to send.
-** mode -- the delivery mode to use. If SM_DEFAULT, use
-** the current e->e_sendmode.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Scans the send lists and sends everything it finds.
-** Delivers any appropriate error messages.
-** If we are running in a non-interactive mode, takes the
-** appropriate action.
-*/
-
-void
-sendall(e, mode)
- ENVELOPE *e;
- int 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;
-
- /*
- ** If this message is to be discarded, don't bother sending
- ** the message at all.
- */
-
- if (bitset(EF_DISCARD, e->e_flags))
- {
- if (tTd(13, 1))
- sm_dprintf("sendall: discarding id %s\n", e->e_id);
- e->e_flags |= EF_CLRQUEUE;
- if (LogLevel > 9)
- logundelrcpts(e, "discarded", 9, true);
- else if (LogLevel > 4)
- sm_syslog(LOG_INFO, e->e_id, "discarded");
- markstats(e, NULL, STATS_REJECT);
- return;
- }
-
- /*
- ** If we have had global, fatal errors, don't bother sending
- ** the message at all if we are in SMTP mode. Local errors
- ** (e.g., a single address failing) will still cause the other
- ** addresses to be sent.
- */
-
- if (bitset(EF_FATALERRS, e->e_flags) &&
- (OpMode == MD_SMTP || OpMode == MD_DAEMON))
- {
- e->e_flags |= EF_CLRQUEUE;
- return;
- }
-
- /* determine actual delivery mode */
- if (mode == SM_DEFAULT)
- {
- mode = e->e_sendmode;
- if (mode != SM_VERIFY && mode != SM_DEFER &&
- shouldqueue(e->e_msgpriority, e->e_ctime))
- mode = SM_QUEUE;
- }
-
- if (tTd(13, 1))
- {
- sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ",
- mode, e->e_id);
- printaddr(sm_debug_file(), &e->e_from, false);
- sm_dprintf("\te_flags = ");
- printenvflags(e);
- sm_dprintf("sendqueue:\n");
- printaddr(sm_debug_file(), e->e_sendqueue, true);
- }
-
- /*
- ** Do any preprocessing necessary for the mode we are running.
- ** Check to make sure the hop count is reasonable.
- ** Delete sends to the sender in mailing lists.
- */
-
- CurEnv = e;
- if (tTd(62, 1))
- checkfds(NULL);
-
- if (e->e_hopcount > MaxHopCount)
- {
- char *recip;
-
- if (e->e_sendqueue != NULL &&
- e->e_sendqueue->q_paddr != NULL)
- recip = e->e_sendqueue->q_paddr;
- else
- recip = "(nobody)";
-
- errno = 0;
- queueup(e, WILL_BE_QUEUED(mode), false);
- e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
- ExitStat = EX_UNAVAILABLE;
- syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s",
- e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
- RealHostName == NULL ? "localhost" : RealHostName,
- recip);
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_DEAD(q->q_state))
- continue;
- q->q_state = QS_BADADDR;
- q->q_status = "5.4.6";
- q->q_rstatus = "554 5.4.6 Too many hops";
- }
- return;
- }
-
- /*
- ** Do sender deletion.
- **
- ** 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) &&
- !QS_IS_QUEUEUP(e->e_from.q_state))
- {
- if (tTd(13, 5))
- {
- sm_dprintf("sendall: QS_SENDER ");
- printaddr(sm_debug_file(), &e->e_from, false);
- }
- e->e_from.q_state = QS_SENDER;
- (void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
- }
-
- /*
- ** Handle alias owners.
- **
- ** We scan up the q_alias chain looking for owners.
- ** We discard owners that are the same as the return path.
- */
-
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- register struct address *a;
-
- for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
- continue;
- if (a != NULL)
- q->q_owner = a->q_owner;
-
- if (q->q_owner != NULL &&
- !QS_IS_DEAD(q->q_state) &&
- strcmp(q->q_owner, e->e_from.q_paddr) == 0)
- q->q_owner = NULL;
- }
-
- if (tTd(13, 25))
- {
- sm_dprintf("\nAfter first owner pass, sendq =\n");
- printaddr(sm_debug_file(), e->e_sendqueue, true);
- }
-
- owner = "";
- otherowners = 1;
- while (owner != NULL && otherowners > 0)
- {
- if (tTd(13, 28))
- sm_dprintf("owner = \"%s\", otherowners = %d\n",
- owner, otherowners);
- owner = NULL;
- otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
-
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (tTd(13, 30))
- {
- sm_dprintf("Checking ");
- printaddr(sm_debug_file(), q, false);
- }
- if (QS_IS_DEAD(q->q_state))
- {
- if (tTd(13, 30))
- sm_dprintf(" ... QS_IS_DEAD\n");
- continue;
- }
- if (tTd(13, 29) && !tTd(13, 30))
- {
- sm_dprintf("Checking ");
- printaddr(sm_debug_file(), q, false);
- }
-
- if (q->q_owner != NULL)
- {
- if (owner == NULL)
- {
- if (tTd(13, 40))
- sm_dprintf(" ... First owner = \"%s\"\n",
- q->q_owner);
- owner = q->q_owner;
- }
- else if (owner != q->q_owner)
- {
- if (strcmp(owner, q->q_owner) == 0)
- {
- if (tTd(13, 40))
- sm_dprintf(" ... Same owner = \"%s\"\n",
- owner);
-
- /* make future comparisons cheap */
- q->q_owner = owner;
- }
- else
- {
- if (tTd(13, 40))
- sm_dprintf(" ... Another owner \"%s\"\n",
- q->q_owner);
- otherowners++;
- }
- owner = q->q_owner;
- }
- else if (tTd(13, 40))
- sm_dprintf(" ... Same owner = \"%s\"\n",
- owner);
- }
- else
- {
- if (tTd(13, 40))
- sm_dprintf(" ... Null owner\n");
- otherowners++;
- }
-
- if (QS_IS_BADADDR(q->q_state))
- {
- if (tTd(13, 30))
- sm_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 &&
- !bitnset(M_NOMX, m->m_flags) &&
- strcmp(m->m_mailer, "[IPC]") == 0 &&
- m->m_argv[0] != NULL &&
- strcmp(m->m_argv[0], "TCP") == 0)
- {
- int len;
- char *p;
-
- if (tTd(13, 30))
- sm_dprintf(" ... FallbackMX\n");
-
- len = strlen(FallbackMX) + 1;
- p = sm_rpool_malloc_x(e->e_rpool, len);
- (void) sm_strlcpy(p, FallbackMX, len);
- q->q_state = QS_OK;
- q->q_host = p;
- }
- else
- {
- if (tTd(13, 30))
- sm_dprintf(" ... QS_IS_QUEUEUP\n");
- continue;
- }
- }
-
- /*
- ** If this mailer is expensive, and if we don't
- ** want to make connections now, just mark these
- ** addresses and return. This is useful if we
- ** want to batch connections to reduce load. This
- ** will cause the messages to be queued up, and a
- ** daemon will come along to send the messages later.
- */
-
- if (NoConnect && !Verbose &&
- bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
- {
- if (tTd(13, 30))
- sm_dprintf(" ... expensive\n");
- q->q_state = QS_QUEUEUP;
- expensive = true;
- }
- else if (bitnset(M_HOLD, q->q_mailer->m_flags) &&
- QueueLimitId == NULL &&
- QueueLimitSender == NULL &&
- QueueLimitRecipient == NULL)
- {
- if (tTd(13, 30))
- sm_dprintf(" ... hold\n");
- q->q_state = QS_QUEUEUP;
- expensive = true;
- }
- else if (QueueMode != QM_QUARANTINE &&
- e->e_quarmsg != NULL)
- {
- if (tTd(13, 30))
- sm_dprintf(" ... quarantine: %s\n",
- e->e_quarmsg);
- q->q_state = QS_QUEUEUP;
- expensive = true;
- }
- else
- {
- if (tTd(13, 30))
- sm_dprintf(" ... deliverable\n");
- somedeliveries = true;
- }
- }
-
- if (owner != NULL && otherowners > 0)
- {
- /*
- ** Split this envelope into two.
- */
-
- ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool,
- sizeof(*ee));
- STRUCTCOPY(*e, *ee);
- ee->e_message = NULL;
- ee->e_id = NULL;
- assign_queueid(ee);
-
- if (tTd(13, 1))
- sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
- e->e_id, ee->e_id, owner,
- otherowners);
-
- ee->e_header = copyheader(e->e_header, ee->e_rpool);
- ee->e_sendqueue = copyqueue(e->e_sendqueue,
- ee->e_rpool);
- ee->e_errorqueue = copyqueue(e->e_errorqueue,
- ee->e_rpool);
- ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
- ee->e_flags |= EF_NORECEIPT;
- setsender(owner, ee, NULL, '\0', true);
- if (tTd(13, 5))
- {
- sm_dprintf("sendall(split): QS_SENDER ");
- printaddr(sm_debug_file(), &ee->e_from, false);
- }
- ee->e_from.q_state = QS_SENDER;
- ee->e_dfp = NULL;
- ee->e_lockfp = NULL;
- ee->e_xfp = NULL;
- ee->e_qgrp = e->e_qgrp;
- ee->e_qdir = e->e_qdir;
- ee->e_errormode = EM_MAIL;
- ee->e_sibling = splitenv;
- ee->e_statmsg = NULL;
- if (e->e_quarmsg != NULL)
- ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
- e->e_quarmsg);
- splitenv = ee;
-
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (q->q_owner == owner)
- {
- q->q_state = QS_CLONED;
- if (tTd(13, 6))
- sm_dprintf("\t... stripping %s from original envelope\n",
- q->q_paddr);
- }
- }
- for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (q->q_owner != owner)
- {
- q->q_state = QS_CLONED;
- if (tTd(13, 6))
- sm_dprintf("\t... dropping %s from cloned envelope\n",
- q->q_paddr);
- }
- else
- {
- /* clear DSN parameters */
- q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
- q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
- if (tTd(13, 6))
- sm_dprintf("\t... moving %s to cloned envelope\n",
- q->q_paddr);
- }
- }
-
- if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
- dup_queue_file(e, ee, DATAFL_LETTER);
-
- /*
- ** 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 = sm_io_dup(e->e_xfp);
-
- /* failed to dup e->e_xfp, start a new transcript */
- if (ee->e_xfp == NULL)
- openxscript(ee);
-
- if (mode != SM_VERIFY && LogLevel > 4)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: clone: owner=%s",
- ee->e_id, owner);
- }
- }
-
- if (owner != NULL)
- {
- setsender(owner, e, NULL, '\0', true);
- if (tTd(13, 5))
- {
- sm_dprintf("sendall(owner): QS_SENDER ");
- printaddr(sm_debug_file(), &e->e_from, false);
- }
- e->e_from.q_state = QS_SENDER;
- e->e_errormode = EM_MAIL;
- e->e_flags |= EF_NORECEIPT;
- e->e_flags &= ~EF_FATALERRS;
- }
-
- /* if nothing to be delivered, just queue up everything */
- if (!somedeliveries && !WILL_BE_QUEUED(mode) &&
- mode != SM_VERIFY)
- {
- time_t now;
-
- if (tTd(13, 29))
- sm_dprintf("No deliveries: auto-queueing\n");
- mode = SM_QUEUE;
- now = curtime();
-
- /* treat this as a delivery in terms of counting tries */
- e->e_dtime = now;
- if (!expensive)
- e->e_ntries++;
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- ee->e_dtime = now;
- if (!expensive)
- ee->e_ntries++;
- }
- }
-
- if ((WILL_BE_QUEUED(mode) || mode == SM_FORK ||
- (mode != SM_VERIFY &&
- (SuperSafe == SAFE_REALLY ||
- SuperSafe == SAFE_REALLY_POSTMILTER))) &&
- (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
- {
- bool msync;
-
- /*
- ** Be sure everything is instantiated in the queue.
- ** Split envelopes first in case the machine crashes.
- ** If the original were done first, we may lose
- ** recipients.
- */
-
-#if !HASFLOCK
- msync = false;
-#else /* !HASFLOCK */
- msync = mode == SM_FORK;
-#endif /* !HASFLOCK */
-
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- queueup(ee, WILL_BE_QUEUED(mode), msync);
- queueup(e, WILL_BE_QUEUED(mode), msync);
- }
-
- if (tTd(62, 10))
- checkfds("after envelope splitting");
-
- /*
- ** If we belong in background, fork now.
- */
-
- if (tTd(13, 20))
- {
- sm_dprintf("sendall: final mode = %c\n", mode);
- if (tTd(13, 21))
- {
- sm_dprintf("\n================ Final Send Queue(s) =====================\n");
- sm_dprintf("\n *** Envelope %s, e_from=%s ***\n",
- e->e_id, e->e_from.q_paddr);
- printaddr(sm_debug_file(), e->e_sendqueue, true);
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- sm_dprintf("\n *** Envelope %s, e_from=%s ***\n",
- ee->e_id, ee->e_from.q_paddr);
- printaddr(sm_debug_file(), ee->e_sendqueue, true);
- }
- sm_dprintf("==========================================================\n\n");
- }
- }
- switch (mode)
- {
- case SM_VERIFY:
- Verbose = 2;
- break;
-
- case SM_QUEUE:
- case SM_DEFER:
-#if HASFLOCK
- queueonly:
-#endif /* HASFLOCK */
- if (e->e_nrcpts > 0)
- e->e_flags |= EF_INQUEUE;
- dropenvelope(e, splitenv != NULL, true);
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- if (ee->e_nrcpts > 0)
- ee->e_flags |= EF_INQUEUE;
- dropenvelope(ee, false, true);
- }
- return;
-
- case SM_FORK:
- if (e->e_xfp != NULL)
- (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
-
-#if !HASFLOCK
- /*
- ** Since fcntl locking has the interesting semantic that
- ** the lock is owned by a process, not by an open file
- ** descriptor, we have to flush this to the queue, and
- ** then restart from scratch in the child.
- */
-
- {
- /* save id for future use */
- char *qid = e->e_id;
-
- /* now drop the envelope in the parent */
- e->e_flags |= EF_INQUEUE;
- dropenvelope(e, splitenv != NULL, false);
-
- /* arrange to reacquire lock after fork */
- e->e_id = qid;
- }
-
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- /* save id for future use */
- char *qid = ee->e_id;
-
- /* drop envelope in parent */
- ee->e_flags |= EF_INQUEUE;
- dropenvelope(ee, false, false);
-
- /* and save qid for reacquisition */
- ee->e_id = qid;
- }
-
-#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(false);
-
- pid = fork();
- if (pid < 0)
- {
- syserr("deliver: fork 1");
-#if HASFLOCK
- goto queueonly;
-#else /* HASFLOCK */
- e->e_id = NULL;
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- ee->e_id = NULL;
- return;
-#endif /* HASFLOCK */
- }
- else if (pid > 0)
- {
-#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) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
- e->e_dfp = NULL;
- e->e_flags &= ~EF_HAS_DF;
-
- /* can't call unlockqueue to avoid unlink of xfp */
- if (e->e_lockfp != NULL)
- (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
- else
- syserr("%s: sendall: null lockfp", e->e_id);
- e->e_lockfp = NULL;
-#endif /* HASFLOCK */
-
- /* make sure the parent doesn't own the envelope */
- e->e_id = NULL;
-
-#if USE_DOUBLE_FORK
- /* catch intermediate zombie */
- (void) waitfor(pid);
-#endif /* USE_DOUBLE_FORK */
- return;
- }
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
-
- /*
- ** Initialize exception stack and default exception
- ** handler for child process.
- */
-
- sm_exc_newthread(fatal_error);
-
- /*
- ** Since we have accepted responsbility for the message,
- ** change the SIGTERM handler. intsig() (the old handler)
- ** would remove the envelope if this was a command line
- ** message submission.
- */
-
- (void) sm_signal(SIGTERM, SIG_DFL);
-
-#if USE_DOUBLE_FORK
- /* double fork to avoid zombies */
- pid = fork();
- if (pid > 0)
- exit(EX_OK);
- save_errno = errno;
-#endif /* USE_DOUBLE_FORK */
-
- CurrentPid = getpid();
-
- /* be sure we are immune from the terminal */
- disconnect(2, e);
- clearstats();
-
- /* prevent parent from waiting if there was an error */
- if (pid < 0)
- {
- errno = save_errno;
- syserr("deliver: fork 2");
-#if HASFLOCK
- e->e_flags |= EF_INQUEUE;
-#else /* HASFLOCK */
- e->e_id = NULL;
-#endif /* HASFLOCK */
- finis(true, true, ExitStat);
- }
-
- /* be sure to give error messages in child */
- QuickAbort = false;
-
- /*
- ** Close any cached connections.
- **
- ** We don't send the QUIT protocol because the parent
- ** still knows about the connection.
- **
- ** This should only happen when delivering an error
- ** message.
- */
-
- mci_flush(false, NULL);
-
-#if HASFLOCK
- break;
-#else /* HASFLOCK */
-
- /*
- ** Now reacquire and run the various queue files.
- */
-
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- ENVELOPE *sibling = ee->e_sibling;
-
- (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id,
- false, false, ee);
- ee->e_sibling = sibling;
- }
- (void) dowork(e->e_qgrp, e->e_qdir, e->e_id,
- false, false, e);
- finis(true, true, ExitStat);
-#endif /* HASFLOCK */
- }
-
- sendenvelope(e, mode);
- dropenvelope(e, true, true);
- for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- {
- CurEnv = ee;
- if (mode != SM_VERIFY)
- openxscript(ee);
- sendenvelope(ee, mode);
- dropenvelope(ee, true, true);
- }
- CurEnv = e;
-
- Verbose = oldverbose;
- if (mode == SM_FORK)
- finis(true, true, ExitStat);
-}
-
-static void
-sendenvelope(e, mode)
- register ENVELOPE *e;
- int mode;
-{
- register ADDRESS *q;
- bool didany;
-
- if (tTd(13, 10))
- sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n",
- e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
- e->e_flags);
- if (LogLevel > 80)
- sm_syslog(LOG_DEBUG, e->e_id,
- "sendenvelope, flags=0x%lx",
- e->e_flags);
-
- /*
- ** If we have had global, fatal errors, don't bother sending
- ** the message at all if we are in SMTP mode. Local errors
- ** (e.g., a single address failing) will still cause the other
- ** addresses to be sent.
- */
-
- if (bitset(EF_FATALERRS, e->e_flags) &&
- (OpMode == MD_SMTP || OpMode == MD_DAEMON))
- {
- e->e_flags |= EF_CLRQUEUE;
- return;
- }
-
- /*
- ** Don't attempt deliveries if we want to bounce now
- ** or if deliver-by time is exceeded.
- */
-
- if (!bitset(EF_RESPONSE, e->e_flags) &&
- (TimeOuts.to_q_return[e->e_timeoutclass] == NOW ||
- (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
- curtime() > e->e_ctime + e->e_deliver_by)))
- return;
-
- /*
- ** Run through the list and send everything.
- **
- ** Set EF_GLOBALERRS so that error messages during delivery
- ** result in returned mail.
- */
-
- e->e_nsent = 0;
- e->e_flags |= EF_GLOBALERRS;
-
- macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid);
- macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype);
- didany = false;
-
- if (!bitset(EF_SPLIT, e->e_flags))
- {
- ENVELOPE *oldsib;
- ENVELOPE *ee;
-
- /*
- ** Save old sibling and set it to NULL to avoid
- ** queueing up the same envelopes again.
- ** This requires that envelopes in that list have
- ** been take care of before (or at some other place).
- */
-
- oldsib = e->e_sibling;
- e->e_sibling = NULL;
- if (!split_by_recipient(e) &&
- bitset(EF_FATALERRS, e->e_flags))
- {
- if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
- e->e_flags |= EF_CLRQUEUE;
- return;
- }
- for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
- queueup(ee, false, true);
-
- /* clean up */
- for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
- {
- /* now unlock the job */
- closexscript(ee);
- unlockqueue(ee);
-
- /* this envelope is marked unused */
- if (ee->e_dfp != NULL)
- {
- (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
- ee->e_dfp = NULL;
- }
- ee->e_id = NULL;
- ee->e_flags &= ~EF_HAS_DF;
- }
- e->e_sibling = oldsib;
- }
-
- /* now run through the queue */
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
-#if XDEBUG
- char wbuf[MAXNAME + 20];
-
- (void) sm_snprintf(wbuf, sizeof(wbuf), "sendall(%.*s)",
- MAXNAME, q->q_paddr);
- checkfd012(wbuf);
-#endif /* XDEBUG */
- if (mode == SM_VERIFY)
- {
- e->e_to = q->q_paddr;
- 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",
- q->q_mailer->m_name,
- q->q_host,
- q->q_user);
- else
- message("deliverable: mailer %s, user %s",
- q->q_mailer->m_name,
- q->q_user);
- }
- }
- else if (QS_IS_OK(q->q_state))
- {
- /*
- ** Checkpoint the send list every few addresses
- */
-
- if (CheckpointInterval > 0 &&
- e->e_nsent >= CheckpointInterval)
- {
- queueup(e, false, false);
- e->e_nsent = 0;
- }
- (void) deliver(e, q);
- didany = true;
- }
- }
- if (didany)
- {
- e->e_dtime = curtime();
- e->e_ntries++;
- }
-
-#if XDEBUG
- checkfd012("end of sendenvelope");
-#endif /* XDEBUG */
-}
-
-#if REQUIRES_DIR_FSYNC
-/*
-** SYNC_DIR -- fsync a directory based on a filename
-**
-** Parameters:
-** filename -- path of file
-** panic -- panic?
-**
-** Returns:
-** none
-*/
-
-void
-sync_dir(filename, panic)
- char *filename;
- bool panic;
-{
- int dirfd;
- char *dirp;
- char dir[MAXPATHLEN];
-
- if (!RequiresDirfsync)
- return;
-
- /* filesystems which require the directory be synced */
- dirp = strrchr(filename, '/');
- if (dirp != NULL)
- {
- if (sm_strlcpy(dir, filename, sizeof(dir)) >= sizeof(dir))
- return;
- dir[dirp - filename] = '\0';
- dirp = dir;
- }
- else
- dirp = ".";
- dirfd = open(dirp, O_RDONLY, 0700);
- if (tTd(40,32))
- sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)",
- dirp, dirfd);
- if (dirfd >= 0)
- {
- if (fsync(dirfd) < 0)
- {
- if (panic)
- syserr("!sync_dir: cannot fsync directory %s",
- dirp);
- else if (LogLevel > 1)
- sm_syslog(LOG_ERR, NOQID,
- "sync_dir: cannot fsync directory %s: %s",
- dirp, sm_errstring(errno));
- }
- (void) close(dirfd);
- }
-}
-#endif /* REQUIRES_DIR_FSYNC */
-/*
-** DUP_QUEUE_FILE -- duplicate a queue file into a split queue
-**
-** Parameters:
-** e -- the existing envelope
-** ee -- the new envelope
-** type -- the queue file type (e.g., DATAFL_LETTER)
-**
-** Returns:
-** none
-*/
-
-static void
-dup_queue_file(e, ee, type)
- ENVELOPE *e, *ee;
- int type;
-{
- char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN];
-
- ee->e_dfp = NULL;
- ee->e_xfp = NULL;
-
- /*
- ** Make sure both are in the same directory.
- */
-
- (void) sm_strlcpy(f1buf, queuename(e, type), sizeof(f1buf));
- (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof(f2buf));
-
- /* Force the df to disk if it's not there yet */
- if (type == DATAFL_LETTER && e->e_dfp != NULL &&
- sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
- errno != EINVAL)
- {
- syserr("!dup_queue_file: can't commit %s", f1buf);
- /* NOTREACHED */
- }
-
- if (link(f1buf, f2buf) < 0)
- {
- int save_errno = errno;
-
- syserr("sendall: link(%s, %s)", f1buf, f2buf);
- if (save_errno == EEXIST)
- {
- if (unlink(f2buf) < 0)
- {
- syserr("!sendall: unlink(%s): permanent",
- f2buf);
- /* NOTREACHED */
- }
- if (link(f1buf, f2buf) < 0)
- {
- syserr("!sendall: link(%s, %s): permanent",
- f1buf, f2buf);
- /* NOTREACHED */
- }
- }
- }
- SYNC_DIR(f2buf, true);
-}
-/*
-** DOFORK -- do a fork, retrying a couple of times on failure.
-**
-** This MUST be a macro, since after a vfork we are running
-** two processes on the same stack!!!
-**
-** Parameters:
-** none.
-**
-** Returns:
-** From a macro??? You've got to be kidding!
-**
-** Side Effects:
-** Modifies the ==> LOCAL <== variable 'pid', leaving:
-** pid of child in parent, zero in child.
-** -1 on unrecoverable error.
-**
-** Notes:
-** I'm awfully sorry this looks so awful. That's
-** vfork for you.....
-*/
-
-#define NFORKTRIES 5
-
-#ifndef FORK
-# define FORK fork
-#endif /* ! FORK */
-
-#define DOFORK(fORKfN) \
-{\
- register int i;\
-\
- for (i = NFORKTRIES; --i >= 0; )\
- {\
- pid = fORKfN();\
- if (pid >= 0)\
- break;\
- if (i > 0)\
- (void) sleep((unsigned) NFORKTRIES - i);\
- }\
-}
-/*
-** DOFORK -- simple fork interface to DOFORK.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** pid of child in parent.
-** zero in child.
-** -1 on error.
-**
-** Side Effects:
-** returns twice, once in parent and once in child.
-*/
-
-pid_t
-dofork()
-{
- register pid_t pid = -1;
-
- DOFORK(fork);
- return pid;
-}
-
-/*
-** COLONCMP -- compare host-signatures up to first ':' or EOS
-**
-** This takes two strings which happen to be host-signatures and
-** compares them. If the lowest preference portions of the MX-RR's
-** match (up to ':' or EOS, whichever is first), then we have
-** match. This is used for coattail-piggybacking messages during
-** message delivery.
-** If the signatures are the same up to the first ':' the remainder of
-** the signatures are then compared with a normal strcmp(). This saves
-** re-examining the first part of the signatures.
-**
-** Parameters:
-** a - first host-signature
-** b - second host-signature
-**
-** Returns:
-** HS_MATCH_NO -- no "match".
-** HS_MATCH_FIRST -- "match" for the first MX preference
-** (up to the first colon (':')).
-** HS_MATCH_FULL -- match for the entire MX record.
-**
-** Side Effects:
-** none.
-*/
-
-#define HS_MATCH_NO 0
-#define HS_MATCH_FIRST 1
-#define HS_MATCH_FULL 2
-
-static int
-coloncmp(a, b)
- register const char *a;
- register const char *b;
-{
- int ret = HS_MATCH_NO;
- int braclev = 0;
-
- while (*a == *b++)
- {
- /* Need to account for IPv6 bracketed addresses */
- if (*a == '[')
- braclev++;
- else if (*a == ']' && braclev > 0)
- braclev--;
- else if (*a == ':' && braclev <= 0)
- {
- ret = HS_MATCH_FIRST;
- a++;
- break;
- }
- else if (*a == '\0')
- return HS_MATCH_FULL; /* a full match */
- a++;
- }
- if (ret == HS_MATCH_NO &&
- braclev <= 0 &&
- ((*a == '\0' && *(b - 1) == ':') ||
- (*a == ':' && *(b - 1) == '\0')))
- return HS_MATCH_FIRST;
- if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0)
- return HS_MATCH_FULL;
-
- return ret;
-}
-
-/*
-** SHOULD_TRY_FBSH -- Should try FallbackSmartHost?
-**
-** Parameters:
-** e -- envelope
-** tried_fallbacksmarthost -- has been tried already? (in/out)
-** hostbuf -- buffer for hostname (expand FallbackSmartHost) (out)
-** hbsz -- size of hostbuf
-** status -- current delivery status
-**
-** Returns:
-** true iff FallbackSmartHost should be tried.
-*/
-
-static bool should_try_fbsh __P((ENVELOPE *, bool *, char *, size_t, int));
-
-static bool
-should_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status)
- ENVELOPE *e;
- bool *tried_fallbacksmarthost;
- char *hostbuf;
- size_t hbsz;
- int status;
-{
- /*
- ** If the host was not found or a temporary failure occurred
- ** and a FallbackSmartHost is defined (and we have not yet
- ** tried it), then make one last try with it as the host.
- */
-
- if ((status == EX_NOHOST || status == EX_TEMPFAIL) &&
- FallbackSmartHost != NULL && !*tried_fallbacksmarthost)
- {
- *tried_fallbacksmarthost = true;
- expand(FallbackSmartHost, hostbuf, hbsz, e);
- if (!wordinclass(hostbuf, 'w'))
- {
- if (tTd(11, 1))
- sm_dprintf("one last try with FallbackSmartHost %s\n",
- hostbuf);
- return true;
- }
- }
- return false;
-}
-/*
-** DELIVER -- Deliver a message to a list of addresses.
-**
-** This routine delivers to everyone on the same host as the
-** user on the head of the list. It is clever about mailers
-** that don't handle multiple users. It is NOT guaranteed
-** that it will deliver to all these addresses however -- so
-** deliver should be called once for each address on the
-** list.
-** Deliver tries to be as opportunistic as possible about piggybacking
-** messages. Some definitions to make understanding easier follow below.
-** Piggybacking occurs when an existing connection to a mail host can
-** be used to send the same message to more than one recipient at the
-** same time. So "no piggybacking" means one message for one recipient
-** per connection. "Intentional piggybacking" happens when the
-** recipients' host address (not the mail host address) is used to
-** attempt piggybacking. Recipients with the same host address
-** have the same mail host. "Coincidental piggybacking" relies on
-** piggybacking based on all the mail host addresses in the MX-RR. This
-** is "coincidental" in the fact it could not be predicted until the
-** MX Resource Records for the hosts were obtained and examined. For
-** example (preference order and equivalence is important, not values):
-** domain1 IN MX 10 mxhost-A
-** IN MX 20 mxhost-B
-** domain2 IN MX 4 mxhost-A
-** IN MX 8 mxhost-B
-** Domain1 and domain2 can piggyback the same message to mxhost-A or
-** mxhost-B (if mxhost-A cannot be reached).
-** "Coattail piggybacking" relaxes the strictness of "coincidental
-** piggybacking" in the hope that most significant (lowest value)
-** MX preference host(s) can create more piggybacking. For example
-** (again, preference order and equivalence is important, not values):
-** domain3 IN MX 100 mxhost-C
-** IN MX 100 mxhost-D
-** IN MX 200 mxhost-E
-** domain4 IN MX 50 mxhost-C
-** IN MX 50 mxhost-D
-** IN MX 80 mxhost-F
-** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C
-** is available. Same with mxhost-D because in both RR's the preference
-** value is the same as mxhost-C, respectively.
-** So deliver attempts coattail piggybacking when possible. If the
-** first MX preference level hosts cannot be used then the piggybacking
-** reverts to coincidental piggybacking. Using the above example you
-** cannot deliver to mxhost-F for domain3 regardless of preference value.
-** ("Coattail" from "riding on the coattails of your predecessor" meaning
-** gaining benefit from a predecessor effort with no or little addition
-** effort. The predecessor here being the preceding MX RR).
-**
-** Parameters:
-** e -- the envelope to deliver.
-** firstto -- head of the address list to deliver to.
-**
-** Returns:
-** zero -- successfully delivered.
-** else -- some failure, see ExitStat for more info.
-**
-** Side Effects:
-** The standard input is passed off to someone.
-*/
-
-static int
-deliver(e, firstto)
- register ENVELOPE *e;
- ADDRESS *firstto;
-{
- char *host; /* host being sent to */
- char *user; /* user being sent to */
- char **pvp;
- register char **mvp;
- register char *p;
- register MAILER *m; /* mailer for this recipient */
- ADDRESS *volatile ctladdr;
-#if HASSETUSERCONTEXT
- ADDRESS *volatile contextaddr = NULL;
-#endif /* HASSETUSERCONTEXT */
- register MCI *volatile mci;
- register ADDRESS *SM_NONVOLATILE to = firstto;
- volatile bool clever = false; /* running user smtp to this mailer */
- ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
- int rcode; /* response code */
- SM_NONVOLATILE int lmtp_rcode = EX_OK;
- SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */
- SM_NONVOLATILE int hostnum = 0; /* current MX host index */
- char *firstsig; /* signature of firstto */
- volatile pid_t pid = -1;
- char *volatile curhost;
- SM_NONVOLATILE unsigned short port = 0;
- SM_NONVOLATILE time_t enough = 0;
-#if NETUNIX
- char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */
-#endif /* NETUNIX */
- time_t xstart;
- bool suidwarn;
- bool anyok; /* at least one address was OK */
- SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */
- bool ovr;
- bool quarantine;
- int strsize;
- int rcptcount;
- int ret;
- static int tobufsize = 0;
- static char *tobuf = NULL;
- char *rpath; /* translated return path */
- int mpvect[2];
- int rpvect[2];
- char *mxhosts[MAXMXHOSTS + 1];
- char *pv[MAXPV + 1];
- char buf[MAXNAME + 1];
- char cbuf[MAXPATHLEN];
-
- errno = 0;
- SM_REQUIRE(firstto != NULL); /* same as to */
- if (!QS_IS_OK(to->q_state))
- return 0;
-
- suidwarn = geteuid() == 0;
-
- SM_REQUIRE(e != NULL);
- m = to->q_mailer;
- host = to->q_host;
- CurEnv = e; /* just in case */
- e->e_statmsg = NULL;
- SmtpError[0] = '\0';
- xstart = curtime();
-
- if (tTd(10, 1))
- sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
- e->e_id, m->m_name, host, to->q_user);
- if (tTd(10, 100))
- printopenfds(false);
-
- /*
- ** Clear {client_*} macros if this is a bounce message to
- ** prevent rejection by check_compat ruleset.
- */
-
- if (bitset(EF_RESPONSE, e->e_flags))
- {
- macdefine(&e->e_macro, A_PERM, macid("{client_name}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{client_port}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), "");
- }
-
- SM_TRY
- {
- ADDRESS *skip_back = NULL;
-
- /*
- ** Do initial argv setup.
- ** Insert the mailer name. Notice that $x expansion is
- ** NOT done on the mailer name. Then, if the mailer has
- ** a picky -f flag, we insert it as appropriate. This
- ** code does not check for 'pv' overflow; this places a
- ** manifest lower limit of 4 for MAXPV.
- ** The from address rewrite is expected to make
- ** the address relative to the other end.
- */
-
- /* rewrite from address, using rewriting rules */
- rcode = EX_OK;
- SM_ASSERT(e->e_from.q_mailer != NULL);
- if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
- p = e->e_sender;
- else
- p = e->e_from.q_paddr;
- rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
- if (strlen(rpath) > MAXSHORTSTR)
- {
- rpath = shortenstring(rpath, MAXSHORTSTR);
-
- /* avoid bogus errno */
- errno = 0;
- syserr("remotename: huge return path %s", rpath);
- }
- rpath = sm_rpool_strdup_x(e->e_rpool, rpath);
- macdefine(&e->e_macro, A_PERM, 'g', rpath);
- macdefine(&e->e_macro, A_PERM, 'h', host);
- Errors = 0;
- pvp = pv;
- *pvp++ = m->m_argv[0];
-
- /* ignore long term host status information if mailer flag W is set */
- if (bitnset(M_NOHOSTSTAT, m->m_flags))
- IgnoreHostStatus = true;
-
- /* insert -f or -r flag as appropriate */
- if (FromFlag &&
- (bitnset(M_FOPT, m->m_flags) ||
- bitnset(M_ROPT, m->m_flags)))
- {
- if (bitnset(M_FOPT, m->m_flags))
- *pvp++ = "-f";
- else
- *pvp++ = "-r";
- *pvp++ = rpath;
- }
-
- /*
- ** Append the other fixed parts of the argv. These run
- ** up to the first entry containing "$u". There can only
- ** be one of these, and there are only a few more slots
- ** in the pv after it.
- */
-
- for (mvp = m->m_argv; (p = *++mvp) != NULL; )
- {
- /* can't use strchr here because of sign extension problems */
- while (*p != '\0')
- {
- if ((*p++ & 0377) == MACROEXPAND)
- {
- if (*p == 'u')
- break;
- }
- }
-
- if (*p != '\0')
- break;
-
- /* this entry is safe -- go ahead and process it */
- expand(*mvp, buf, sizeof(buf), e);
- *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
- if (pvp >= &pv[MAXPV - 3])
- {
- syserr("554 5.3.5 Too many parameters to %s before $u",
- pv[0]);
- rcode = -1;
- goto cleanup;
- }
- }
-
- /*
- ** If we have no substitution for the user name in the argument
- ** list, we know that we must supply the names otherwise -- and
- ** SMTP is the answer!!
- */
-
- if (*mvp == NULL)
- {
- /* running LMTP or SMTP */
- clever = true;
- *pvp = NULL;
- }
- else if (bitnset(M_LMTP, m->m_flags))
- {
- /* not running LMTP */
- sm_syslog(LOG_ERR, NULL,
- "Warning: mailer %s: LMTP flag (F=z) turned off",
- m->m_name);
- clrbitn(M_LMTP, m->m_flags);
- }
-
- /*
- ** At this point *mvp points to the argument with $u. We
- ** run through our address list and append all the addresses
- ** we can. If we run out of space, do not fret! We can
- ** always send another copy later.
- */
-
- e->e_to = NULL;
- strsize = 2;
- rcptcount = 0;
- ctladdr = NULL;
- if (firstto->q_signature == NULL)
- firstto->q_signature = hostsignature(firstto->q_mailer,
- firstto->q_host);
- firstsig = firstto->q_signature;
-
- for (; to != NULL; to = to->q_next)
- {
- /* avoid sending multiple recipients to dumb mailers */
- if (tochain != NULL && !bitnset(M_MUSER, m->m_flags))
- break;
-
- /* if already sent or not for this host, don't send */
- if (!QS_IS_OK(to->q_state)) /* already sent; look at next */
- continue;
-
- /*
- ** Must be same mailer to keep grouping rcpts.
- ** If mailers don't match: continue; sendqueue is not
- ** sorted by mailers, so don't break;
- */
-
- if (to->q_mailer != firstto->q_mailer)
- continue;
-
- if (to->q_signature == NULL) /* for safety */
- to->q_signature = hostsignature(to->q_mailer,
- to->q_host);
-
- /*
- ** This is for coincidental and tailcoat piggybacking messages
- ** to the same mail host. While the signatures are identical
- ** (that's the MX-RR's are identical) we can do coincidental
- ** piggybacking. We try hard for coattail piggybacking
- ** with the same mail host when the next recipient has the
- ** same host at lowest preference. It may be that this
- ** won't work out, so 'skip_back' is maintained if a backup
- ** to coincidental piggybacking or full signature must happen.
- */
-
- ret = firstto == to ? HS_MATCH_FULL :
- coloncmp(to->q_signature, firstsig);
- if (ret == HS_MATCH_FULL)
- skip_back = to;
- else if (ret == HS_MATCH_NO)
- break;
-
- if (!clever)
- {
- /* avoid overflowing tobuf */
- strsize += strlen(to->q_paddr) + 1;
- if (strsize > TOBUFSIZE)
- break;
- }
-
- if (++rcptcount > to->q_mailer->m_maxrcpt)
- break;
-
- if (tTd(10, 1))
- {
- sm_dprintf("\nsend to ");
- printaddr(sm_debug_file(), to, false);
- }
-
- /* compute effective uid/gid when sending */
- if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
-# if HASSETUSERCONTEXT
- contextaddr = ctladdr = getctladdr(to);
-# else /* HASSETUSERCONTEXT */
- ctladdr = getctladdr(to);
-# endif /* HASSETUSERCONTEXT */
-
- if (tTd(10, 2))
- {
- sm_dprintf("ctladdr=");
- printaddr(sm_debug_file(), ctladdr, false);
- }
-
- user = to->q_user;
- e->e_to = to->q_paddr;
-
- /*
- ** Check to see that these people are allowed to
- ** talk to each other.
- ** Check also for overflow of e_msgsize.
- */
-
- if (m->m_maxsize != 0 &&
- (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0))
- {
- e->e_flags |= EF_NO_BODY_RETN;
- if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
- to->q_status = "5.2.3";
- else
- to->q_status = "5.3.4";
-
- /* set to->q_rstatus = NULL; or to the following? */
- usrerrenh(to->q_status,
- "552 Message is too large; %ld bytes max",
- m->m_maxsize);
- markfailure(e, to, NULL, EX_UNAVAILABLE, false);
- giveresponse(EX_UNAVAILABLE, to->q_status, m,
- NULL, ctladdr, xstart, e, to);
- continue;
- }
- SM_SET_H_ERRNO(0);
- ovr = true;
-
- /* do config file checking of compatibility */
- quarantine = (e->e_quarmsg != NULL);
- rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr,
- e, RSF_RMCOMM|RSF_COUNT, 3, NULL,
- e->e_id, NULL);
- 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, ovr);
- giveresponse(rcode, to->q_status, m,
- NULL, ctladdr, xstart, e, to);
- continue;
- }
- if (!quarantine && e->e_quarmsg != NULL)
- {
- /*
- ** check_compat or checkcompat() has tried
- ** to quarantine but that isn't supported.
- ** Revert the attempt.
- */
-
- e->e_quarmsg = NULL;
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), "");
- }
- if (bitset(EF_DISCARD, e->e_flags))
- {
- if (tTd(10, 5))
- {
- sm_dprintf("deliver: discarding recipient ");
- printaddr(sm_debug_file(), 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. This is safe because the
- ** true "global discard" has been handled before
- ** we get here.
- */
-
- e->e_flags &= ~EF_DISCARD;
- continue;
- }
-
- /*
- ** Strip quote bits from names if the mailer is dumb
- ** about them.
- */
-
- if (bitnset(M_STRIPQ, m->m_flags))
- {
- stripquotes(user);
- stripquotes(host);
- }
-
- /*
- ** Strip all leading backslashes if requested and the
- ** next character is alphanumerical (the latter can
- ** probably relaxed a bit, see RFC2821).
- */
-
- if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\')
- stripbackslash(user);
-
- /* hack attack -- delivermail compatibility */
- if (m == ProgMailer && *user == '|')
- user++;
-
- /*
- ** If an error message has already been given, don't
- ** bother to send to this address.
- **
- ** >>>>>>>>>> This clause assumes that the local mailer
- ** >> NOTE >> cannot do any further aliasing; that
- ** >>>>>>>>>> function is subsumed by sendmail.
- */
-
- if (!QS_IS_OK(to->q_state))
- continue;
-
- /*
- ** See if this user name is "special".
- ** If the user name has a slash in it, assume that this
- ** is a file -- send it off without further ado. Note
- ** that this type of addresses is not processed along
- ** with the others, so we fudge on the To person.
- */
-
- if (strcmp(m->m_mailer, "[FILE]") == 0)
- {
- macdefine(&e->e_macro, A_PERM, 'u', user);
- p = to->q_home;
- if (p == NULL && ctladdr != NULL)
- p = ctladdr->q_home;
- macdefine(&e->e_macro, A_PERM, 'z', p);
- expand(m->m_argv[1], buf, sizeof(buf), e);
- if (strlen(buf) > 0)
- rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
- else
- {
- syserr("empty filename specification for mailer %s",
- m->m_name);
- rcode = EX_CONFIG;
- }
- giveresponse(rcode, to->q_status, m, NULL,
- ctladdr, xstart, e, to);
- markfailure(e, to, NULL, rcode, true);
- e->e_nsent++;
- if (rcode == EX_OK)
- {
- to->q_state = QS_SENT;
- if (bitnset(M_LOCALMAILER, m->m_flags) &&
- bitset(QPINGONSUCCESS, to->q_flags))
- {
- to->q_flags |= QDELIVERED;
- to->q_status = "2.1.5";
- (void) sm_io_fprintf(e->e_xfp,
- SM_TIME_DEFAULT,
- "%s... Successfully delivered\n",
- to->q_paddr);
- }
- }
- to->q_statdate = curtime();
- markstats(e, to, STATS_NORMAL);
- continue;
- }
-
- /*
- ** Address is verified -- add this user to mailer
- ** argv, and add it to the print list of recipients.
- */
-
- /* link together the chain of recipients */
- to->q_tchain = tochain;
- tochain = to;
- e->e_to = "[CHAIN]";
-
- macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */
- p = to->q_home;
- if (p == NULL && ctladdr != NULL)
- p = ctladdr->q_home;
- macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */
-
- /* set the ${dsn_notify} macro if applicable */
- if (bitset(QHASNOTIFY, to->q_flags))
- {
- char notify[MAXLINE];
-
- notify[0] = '\0';
- if (bitset(QPINGONSUCCESS, to->q_flags))
- (void) sm_strlcat(notify, "SUCCESS,",
- sizeof(notify));
- if (bitset(QPINGONFAILURE, to->q_flags))
- (void) sm_strlcat(notify, "FAILURE,",
- sizeof(notify));
- if (bitset(QPINGONDELAY, to->q_flags))
- (void) sm_strlcat(notify, "DELAY,",
- sizeof(notify));
-
- /* Set to NEVER or drop trailing comma */
- if (notify[0] == '\0')
- (void) sm_strlcat(notify, "NEVER",
- sizeof(notify));
- else
- notify[strlen(notify) - 1] = '\0';
-
- macdefine(&e->e_macro, A_TEMP,
- macid("{dsn_notify}"), notify);
- }
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_notify}"), NULL);
-
- /*
- ** Expand out this user into argument list.
- */
-
- if (!clever)
- {
- expand(*mvp, buf, sizeof(buf), e);
- *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
- if (pvp >= &pv[MAXPV - 2])
- {
- /* allow some space for trailing parms */
- break;
- }
- }
- }
-
- /* see if any addresses still exist */
- if (tochain == NULL)
- {
- rcode = 0;
- goto cleanup;
- }
-
- /* print out messages as full list */
- strsize = 1;
- for (to = tochain; to != NULL; to = to->q_tchain)
- strsize += strlen(to->q_paddr) + 1;
- if (strsize < TOBUFSIZE)
- strsize = TOBUFSIZE;
- if (strsize > tobufsize)
- {
- SM_FREE_CLR(tobuf);
- tobuf = sm_pmalloc_x(strsize);
- tobufsize = strsize;
- }
- p = tobuf;
- *p = '\0';
- for (to = tochain; to != NULL; to = to->q_tchain)
- {
- (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2,
- ",", to->q_paddr);
- p += strlen(p);
- }
- e->e_to = tobuf + 1;
-
- /*
- ** Fill out any parameters after the $u parameter.
- */
-
- if (!clever)
- {
- while (*++mvp != NULL)
- {
- expand(*mvp, buf, sizeof(buf), e);
- *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf);
- if (pvp >= &pv[MAXPV])
- syserr("554 5.3.0 deliver: pv overflow after $u for %s",
- pv[0]);
- }
- }
- *pvp++ = NULL;
-
- /*
- ** Call the mailer.
- ** The argument vector gets built, pipes
- ** are created as necessary, and we fork & exec as
- ** appropriate.
- ** If we are running SMTP, we just need to clean up.
- */
-
- /* XXX this seems a bit wierd */
- if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
- bitset(QGOODUID, e->e_from.q_flags))
- ctladdr = &e->e_from;
-
-#if NAMED_BIND
- if (ConfigLevel < 2)
- _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */
-#endif /* NAMED_BIND */
-
- if (tTd(11, 1))
- {
- sm_dprintf("openmailer:");
- printav(sm_debug_file(), pv);
- }
- errno = 0;
- SM_SET_H_ERRNO(0);
- CurHostName = NULL;
-
- /*
- ** Deal with the special case of mail handled through an IPC
- ** connection.
- ** In this case we don't actually fork. We must be
- ** running SMTP for this to work. We will return a
- ** zero pid to indicate that we are running IPC.
- ** We also handle a debug version that just talks to stdin/out.
- */
-
- curhost = NULL;
- SmtpPhase = NULL;
- mci = NULL;
-
-#if XDEBUG
- {
- char wbuf[MAXLINE];
-
- /* make absolutely certain 0, 1, and 2 are in use */
- (void) sm_snprintf(wbuf, sizeof(wbuf), "%s... openmailer(%s)",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name);
- checkfd012(wbuf);
- }
-#endif /* XDEBUG */
-
- /* check for 8-bit available */
- if (bitset(EF_HAS8BIT, e->e_flags) &&
- bitnset(M_7BITS, m->m_flags) &&
- (bitset(EF_DONT_MIME, e->e_flags) ||
- !(bitset(MM_MIME8BIT, MimeMode) ||
- (bitset(EF_IS_MIME, e->e_flags) &&
- bitset(MM_CVTMIME, MimeMode)))))
- {
- 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;
- }
-
- if (tTd(62, 8))
- checkfds("before delivery");
-
- /* check for Local Person Communication -- not for mortals!!! */
- if (strcmp(m->m_mailer, "[LPC]") == 0)
- {
- if (clever)
- {
- /* flush any expired connections */
- (void) mci_scan(NULL);
-
- /* try to get a cached connection or just a slot */
- mci = mci_get(m->m_name, m);
- if (mci->mci_host == NULL)
- mci->mci_host = m->m_name;
- CurHostName = mci->mci_host;
- if (mci->mci_state != MCIS_CLOSED)
- {
- message("Using cached SMTP/LPC connection for %s...",
- m->m_name);
- mci->mci_deliveries++;
- goto do_transfer;
- }
- }
- else
- {
- mci = mci_new(e->e_rpool);
- }
- mci->mci_in = smioin;
- mci->mci_out = smioout;
- mci->mci_mailer = m;
- mci->mci_host = m->m_name;
- if (clever)
- {
- mci->mci_state = MCIS_OPENING;
- mci_cache(mci);
- }
- else
- mci->mci_state = MCIS_OPEN;
- }
- else if (strcmp(m->m_mailer, "[IPC]") == 0)
- {
- register int i;
-
- if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
- {
- syserr("null destination for %s mailer", m->m_mailer);
- rcode = EX_CONFIG;
- goto give_up;
- }
-
-# 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')
- {
- syserr("null host signature for %s", pv[1]);
- rcode = EX_CONFIG;
- goto give_up;
- }
-
- if (!clever)
- {
- syserr("554 5.3.5 non-clever IPC");
- rcode = EX_CONFIG;
- goto give_up;
- }
- if (pv[2] != NULL
-# if NETUNIX
- && mux_path == NULL
-# endif /* NETUNIX */
- )
- {
- port = htons((unsigned 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);
- if (TimeOuts.to_aconnect > 0)
- enough = curtime() + TimeOuts.to_aconnect;
-tryhost:
- while (hostnum < nummxhosts)
- {
- char sep = ':';
- char *endp;
- static char hostbuf[MAXNAME + 1];
- bool tried_fallbacksmarthost = false;
-
-# 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 (hostnum == 1 && skip_back != NULL)
- {
- /*
- ** Coattail piggybacking is no longer an
- ** option with the mail host next to be tried
- ** no longer the lowest MX preference
- ** (hostnum == 1 meaning we're on the second
- ** preference). We do not try to coattail
- ** piggyback more than the first MX preference.
- ** Revert 'tochain' to last location for
- ** coincidental piggybacking. This works this
- ** easily because the q_tchain kept getting
- ** added to the top of the linked list.
- */
-
- tochain = skip_back;
- }
-
- if (*mxhosts[hostnum] == '\0')
- {
- syserr("deliver: null host name in signature");
- hostnum++;
- if (endp != NULL)
- *endp = sep;
- continue;
- }
- (void) sm_strlcpy(hostbuf, mxhosts[hostnum],
- sizeof(hostbuf));
- hostnum++;
- if (endp != NULL)
- *endp = sep;
-
- one_last_try:
- /* see if we already know that this host is fried */
- CurHostName = hostbuf;
- mci = mci_get(hostbuf, m);
- if (mci->mci_state != MCIS_CLOSED)
- {
- char *type;
-
- if (tTd(11, 1))
- {
- sm_dprintf("openmailer: ");
- mci_dump(sm_debug_file(), mci, false);
- }
- CurHostName = mci->mci_host;
- if (bitnset(M_LMTP, m->m_flags))
- type = "L";
- else if (bitset(MCIF_ESMTP, mci->mci_flags))
- type = "ES";
- else
- type = "S";
- message("Using cached %sMTP connection to %s via %s...",
- type, hostbuf, m->m_name);
- mci->mci_deliveries++;
- break;
- }
- mci->mci_mailer = m;
- if (mci->mci_exitstat != EX_OK)
- {
- if (mci->mci_exitstat == EX_TEMPFAIL)
- goodmxfound = true;
-
- /* Try FallbackSmartHost? */
- if (should_try_fbsh(e, &tried_fallbacksmarthost,
- hostbuf, sizeof(hostbuf),
- mci->mci_exitstat))
- goto one_last_try;
-
- continue;
- }
-
- if (mci_lock_host(mci) != EX_OK)
- {
- mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
- goodmxfound = true;
- continue;
- }
-
- /* try the connection */
- 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...",
- mux_path, m->m_name);
- i = makeconnection_ds((char *) mux_path, mci);
- }
- else
-# 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,
- enough);
- }
- mci->mci_errno = errno;
- mci->mci_lastuse = curtime();
- mci->mci_deliveries = 0;
- mci->mci_exitstat = i;
-# if NAMED_BIND
- mci->mci_herrno = h_errno;
-# endif /* NAMED_BIND */
-
- /*
- ** Have we tried long enough to get a connection?
- ** If yes, skip to the fallback MX hosts
- ** (if existent).
- */
-
- if (enough > 0 && mci->mci_lastuse >= enough)
- {
- int h;
-# if NAMED_BIND
- extern int NumFallbackMXHosts;
-# else /* NAMED_BIND */
- const int NumFallbackMXHosts = 0;
-# endif /* NAMED_BIND */
-
- if (hostnum < nummxhosts && LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "Timeout.to_aconnect occurred before exhausting all addresses");
-
- /* turn off timeout if fallback available */
- if (NumFallbackMXHosts > 0)
- enough = 0;
-
- /* skip to a fallback MX host */
- h = nummxhosts - NumFallbackMXHosts;
- if (hostnum < h)
- hostnum = h;
- }
- if (i == EX_OK)
- {
- goodmxfound = true;
- markstats(e, firstto, STATS_CONNECT);
- mci->mci_state = MCIS_OPENING;
- mci_cache(mci);
- if (TrafficLogFile != NULL)
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT,
- "%05d === CONNECT %s\n",
- (int) CurrentPid,
- hostbuf);
- break;
- }
- else
- {
- /* Try FallbackSmartHost? */
- if (should_try_fbsh(e, &tried_fallbacksmarthost,
- hostbuf, sizeof(hostbuf), i))
- goto one_last_try;
-
- if (tTd(11, 1))
- sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n",
- i, errno);
- if (i == EX_TEMPFAIL)
- goodmxfound = true;
- mci_unlock_host(mci);
- }
-
- /* enter status of this host */
- setstat(i);
-
- /* should print some message here for -v mode */
- }
- if (mci == NULL)
- {
- syserr("deliver: no host name");
- rcode = EX_SOFTWARE;
- goto give_up;
- }
- mci->mci_pid = 0;
- }
- else
- {
- /* flush any expired connections */
- (void) mci_scan(NULL);
- mci = NULL;
-
- if (bitnset(M_LMTP, m->m_flags))
- {
- /* try to get a cached connection */
- mci = mci_get(m->m_name, m);
- if (mci->mci_host == NULL)
- mci->mci_host = m->m_name;
- CurHostName = mci->mci_host;
- if (mci->mci_state != MCIS_CLOSED)
- {
- message("Using cached LMTP connection for %s...",
- m->m_name);
- mci->mci_deliveries++;
- goto do_transfer;
- }
- }
-
- /* announce the connection to verbose listeners */
- if (host == NULL || host[0] == '\0')
- message("Connecting to %s...", m->m_name);
- else
- message("Connecting to %s via %s...", host, m->m_name);
- if (TrafficLogFile != NULL)
- {
- char **av;
-
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "%05d === EXEC", (int) CurrentPid);
- for (av = pv; *av != NULL; av++)
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT, " %s",
- *av);
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "\n");
- }
-
-#if XDEBUG
- checkfd012("before creating mail pipe");
-#endif /* XDEBUG */
-
- /* create a pipe to shove the mail through */
- if (pipe(mpvect) < 0)
- {
- syserr("%s... openmailer(%s): pipe (to mailer)",
- shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
- if (tTd(11, 1))
- sm_dprintf("openmailer: NULL\n");
- rcode = EX_OSERR;
- goto give_up;
- }
-
-#if XDEBUG
- /* make sure we didn't get one of the standard I/O files */
- if (mpvect[0] < 3 || mpvect[1] < 3)
- {
- syserr("%s... openmailer(%s): bogus mpvect %d %d",
- shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
- mpvect[0], mpvect[1]);
- printopenfds(true);
- if (tTd(11, 1))
- sm_dprintf("openmailer: NULL\n");
- rcode = EX_OSERR;
- goto give_up;
- }
-
- /* make sure system call isn't dead meat */
- checkfdopen(mpvect[0], "mpvect[0]");
- checkfdopen(mpvect[1], "mpvect[1]");
- if (mpvect[0] == mpvect[1] ||
- (e->e_lockfp != NULL &&
- (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
- NULL) ||
- mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD,
- NULL))))
- {
- if (e->e_lockfp == NULL)
- syserr("%s... openmailer(%s): overlapping mpvect %d %d",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, mpvect[0], mpvect[1]);
- else
- syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, mpvect[0], mpvect[1],
- sm_io_getinfo(e->e_lockfp,
- SM_IO_WHAT_FD, NULL));
- }
-#endif /* XDEBUG */
-
- /* create a return pipe */
- 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))
- sm_dprintf("openmailer: NULL\n");
- rcode = EX_OSERR;
- goto give_up;
- }
-#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 endmailer will get it.
- */
-
- if (e->e_xfp != NULL) /* for debugging */
- (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
- (void) sm_signal(SIGCHLD, SIG_DFL);
-
-
- DOFORK(FORK);
- /* pid is set by DOFORK */
-
- if (pid < 0)
- {
- /* failure */
- syserr("%s... openmailer(%s): cannot fork",
- shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
- (void) close(mpvect[0]);
- (void) close(mpvect[1]);
- (void) close(rpvect[0]);
- (void) close(rpvect[1]);
- if (tTd(11, 1))
- sm_dprintf("openmailer: NULL\n");
- rcode = EX_OSERR;
- goto give_up;
- }
- else if (pid == 0)
- {
- int save_errno;
- int sff;
- int new_euid = NO_UID;
- int new_ruid = NO_UID;
- int new_gid = NO_GID;
- char *user = NULL;
- struct stat stb;
- extern int DtableSize;
-
- CurrentPid = getpid();
-
- /* clear the events to turn off SIGALRMs */
- sm_clear_events();
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
-
- if (e->e_lockfp != NULL)
- (void) close(sm_io_getinfo(e->e_lockfp,
- SM_IO_WHAT_FD,
- NULL));
-
- /* child -- set up input & exec mailer */
- (void) sm_signal(SIGALRM, sm_signal_noop);
- (void) sm_signal(SIGCHLD, SIG_DFL);
- (void) sm_signal(SIGHUP, SIG_IGN);
- (void) sm_signal(SIGINT, SIG_IGN);
- (void) sm_signal(SIGTERM, SIG_DFL);
-# ifdef SIGUSR1
- (void) sm_signal(SIGUSR1, sm_signal_noop);
-# endif /* SIGUSR1 */
-
- if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
- stb.st_mode = 0;
-
-# if HASSETUSERCONTEXT
- /*
- ** Set user resources.
- */
-
- if (contextaddr != NULL)
- {
- int sucflags;
- struct passwd *pwd;
-
- if (contextaddr->q_ruser != NULL)
- pwd = sm_getpwnam(contextaddr->q_ruser);
- else
- pwd = sm_getpwnam(contextaddr->q_user);
- sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
-#ifdef LOGIN_SETMAC
- sucflags |= LOGIN_SETMAC;
-#endif /* LOGIN_SETMAC */
- if (pwd != NULL &&
- setusercontext(NULL, pwd, pwd->pw_uid,
- sucflags) == -1 &&
- suidwarn)
- {
- syserr("openmailer: setusercontext() failed");
- exit(EX_TEMPFAIL);
- }
- }
-# endif /* HASSETUSERCONTEXT */
-
-#if HASNICE
- /* tweak niceness */
- if (m->m_nice != 0)
- (void) nice(m->m_nice);
-#endif /* HASNICE */
-
- /* reset group id */
- if (bitnset(M_SPECIFIC_UID, m->m_flags))
- {
- if (m->m_gid == NO_GID)
- new_gid = RunAsGid;
- else
- new_gid = m->m_gid;
- }
- else if (bitset(S_ISGID, stb.st_mode))
- new_gid = stb.st_gid;
- else if (ctladdr != NULL && ctladdr->q_gid != 0)
- {
- if (!DontInitGroups)
- {
- user = ctladdr->q_ruser;
- if (user == NULL)
- user = ctladdr->q_user;
-
- if (initgroups(user,
- ctladdr->q_gid) == -1
- && suidwarn)
- {
- syserr("openmailer: initgroups(%s, %d) failed",
- user, ctladdr->q_gid);
- exit(EX_TEMPFAIL);
- }
- }
- else
- {
- GIDSET_T gidset[1];
-
- gidset[0] = ctladdr->q_gid;
- if (setgroups(1, gidset) == -1
- && suidwarn)
- {
- syserr("openmailer: setgroups() failed");
- exit(EX_TEMPFAIL);
- }
- }
- new_gid = ctladdr->q_gid;
- }
- else
- {
- if (!DontInitGroups)
- {
- user = DefUser;
- if (initgroups(DefUser, DefGid) == -1 &&
- suidwarn)
- {
- syserr("openmailer: initgroups(%s, %d) failed",
- DefUser, DefGid);
- exit(EX_TEMPFAIL);
- }
- }
- else
- {
- GIDSET_T gidset[1];
-
- gidset[0] = DefGid;
- if (setgroups(1, gidset) == -1
- && suidwarn)
- {
- syserr("openmailer: setgroups() failed");
- exit(EX_TEMPFAIL);
- }
- }
- if (m->m_gid == NO_GID)
- new_gid = DefGid;
- else
- new_gid = m->m_gid;
- }
- if (new_gid != NO_GID)
- {
- if (RunAsUid != 0 &&
- bitnset(M_SPECIFIC_UID, m->m_flags) &&
- new_gid != getgid() &&
- new_gid != getegid())
- {
- /* Only root can change the gid */
- syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d",
- (int) RunAsUid, (int) new_gid,
- (int) getgid(), (int) getegid());
- 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, cbuf, sizeof(cbuf), e);
- if (tTd(11, 20))
- sm_dprintf("openmailer: chroot %s\n",
- cbuf);
- if (chroot(cbuf) < 0)
- {
- syserr("openmailer: Cannot chroot(%s)",
- cbuf);
- exit(EX_TEMPFAIL);
- }
- if (chdir("/") < 0)
- {
- syserr("openmailer: cannot chdir(/)");
- exit(EX_TEMPFAIL);
- }
- }
-
- /* reset user id */
- endpwent();
- sm_mbdb_terminate();
- if (bitnset(M_SPECIFIC_UID, m->m_flags))
- {
- if (m->m_uid == NO_UID)
- new_euid = RunAsUid;
- else
- new_euid = m->m_uid;
-
- /*
- ** Undo the effects of the uid change in main
- ** for signal handling. The real uid may
- ** be used by mailer in adding a "From "
- ** line.
- */
-
- if (RealUid != 0 && RealUid != getuid())
- {
-# if MAILER_SETUID_METHOD == USE_SETEUID
-# if HASSETREUID
- if (setreuid(RealUid, geteuid()) < 0)
- {
- syserr("openmailer: setreuid(%d, %d) failed",
- (int) RealUid, (int) geteuid());
- exit(EX_OSERR);
- }
-# endif /* HASSETREUID */
-# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
-# if MAILER_SETUID_METHOD == USE_SETREUID
- new_ruid = RealUid;
-# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
- }
- }
- else if (bitset(S_ISUID, stb.st_mode))
- new_ruid = stb.st_uid;
- else if (ctladdr != NULL && ctladdr->q_uid != 0)
- new_ruid = ctladdr->q_uid;
- else if (m->m_uid != NO_UID)
- new_ruid = m->m_uid;
- else
- new_ruid = DefUid;
-
-# if _FFR_USE_SETLOGIN
- /* run disconnected from terminal and set login name */
- if (setsid() >= 0 &&
- ctladdr != NULL && ctladdr->q_uid != 0 &&
- new_euid == ctladdr->q_uid)
- {
- struct passwd *pwd;
-
- pwd = sm_getpwuid(ctladdr->q_uid);
- if (pwd != NULL && suidwarn)
- (void) setlogin(pwd->pw_name);
- endpwent();
- }
-# endif /* _FFR_USE_SETLOGIN */
-
- if (new_euid != NO_UID)
- {
- if (RunAsUid != 0 && new_euid != RunAsUid)
- {
- /* Only root can change the uid */
- syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d",
- (int) new_euid, (int) RunAsUid);
- exit(EX_TEMPFAIL);
- }
-
- vendor_set_uid(new_euid);
-# if MAILER_SETUID_METHOD == USE_SETEUID
- if (seteuid(new_euid) < 0 && suidwarn)
- {
- syserr("openmailer: seteuid(%ld) failed",
- (long) new_euid);
- 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);
- 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);
- 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))
- sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n",
- (int) getuid(), (int) geteuid(),
- (int) getgid(), (int) getegid());
-
- /* move into some "safe" directory */
- if (m->m_execdir != NULL)
- {
- char *q;
-
- for (p = m->m_execdir; p != NULL; p = q)
- {
- q = strchr(p, ':');
- if (q != NULL)
- *q = '\0';
- expand(p, cbuf, sizeof(cbuf), e);
- if (q != NULL)
- *q++ = ':';
- if (tTd(11, 20))
- sm_dprintf("openmailer: trydir %s\n",
- cbuf);
- if (cbuf[0] != '\0' &&
- chdir(cbuf) >= 0)
- break;
- }
- }
-
- /* Check safety of program to be run */
- sff = SFF_ROOTOK|SFF_EXECOK;
- if (!bitnset(DBS_RUNWRITABLEPROGRAM,
- DontBlameSendmail))
- sff |= SFF_NOGWFILES|SFF_NOWWFILES;
- if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH,
- DontBlameSendmail))
- sff |= SFF_NOPATHCHECK;
- else
- sff |= SFF_SAFEDIRPATH;
- ret = safefile(m->m_mailer, getuid(), getgid(),
- user, sff, 0, NULL);
- if (ret != 0)
- sm_syslog(LOG_INFO, e->e_id,
- "Warning: program %s unsafe: %s",
- m->m_mailer, sm_errstring(ret));
-
- /* arrange to filter std & diag output of command */
- (void) close(rpvect[0]);
- if (dup2(rpvect[1], STDOUT_FILENO) < 0)
- {
- 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]);
-
- if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
- {
- syserr("%s... openmailer(%s): cannot dup stdout for stderr",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name);
- _exit(EX_OSERR);
- }
-
- /* arrange to get standard input */
- (void) close(mpvect[1]);
- if (dup2(mpvect[0], STDIN_FILENO) < 0)
- {
- syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
- shortenstring(e->e_to, MAXSHORTSTR),
- m->m_name, mpvect[0]);
- _exit(EX_OSERR);
- }
- (void) close(mpvect[0]);
-
- /* arrange for all the files to be closed */
- sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
-
-# if !_FFR_USE_SETLOGIN
- /* run disconnected from terminal */
- (void) setsid();
-# endif /* !_FFR_USE_SETLOGIN */
-
- /* try to execute the mailer */
- (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(save_errno))
- _exit(EX_OSERR);
- _exit(EX_UNAVAILABLE);
- }
-
- /*
- ** Set up return value.
- */
-
- if (mci == NULL)
- {
- if (clever)
- {
- /*
- ** Allocate from general heap, not
- ** envelope rpool, because this mci
- ** is going to be cached.
- */
-
- mci = mci_new(NULL);
- }
- else
- {
- /*
- ** Prevent a storage leak by allocating
- ** this from the envelope rpool.
- */
-
- mci = mci_new(e->e_rpool);
- }
- }
- mci->mci_mailer = m;
- if (clever)
- {
- mci->mci_state = MCIS_OPENING;
- mci_cache(mci);
- }
- else
- {
- mci->mci_state = MCIS_OPEN;
- }
- mci->mci_pid = pid;
- (void) close(mpvect[0]);
- mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &(mpvect[1]), SM_IO_WRONLY_B,
- NULL);
- if (mci->mci_out == NULL)
- {
- syserr("deliver: cannot create mailer output channel, fd=%d",
- mpvect[1]);
- (void) close(mpvect[1]);
- (void) close(rpvect[0]);
- (void) close(rpvect[1]);
- rcode = EX_OSERR;
- goto give_up;
- }
-
- (void) close(rpvect[1]);
- mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &(rpvect[0]), SM_IO_RDONLY_B,
- NULL);
- if (mci->mci_in == NULL)
- {
- syserr("deliver: cannot create mailer input channel, fd=%d",
- mpvect[1]);
- (void) close(rpvect[0]);
- (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
- mci->mci_out = NULL;
- rcode = EX_OSERR;
- goto give_up;
- }
- }
-
- /*
- ** If we are in SMTP opening state, send initial protocol.
- */
-
- if (bitnset(M_7BITS, m->m_flags) &&
- (!clever || mci->mci_state == MCIS_OPENING))
- mci->mci_flags |= MCIF_7BIT;
- if (clever && mci->mci_state != MCIS_CLOSED)
- {
-# if STARTTLS || SASL
- int dotpos;
- char *srvname;
- extern SOCKADDR CurHostAddr;
-# endif /* STARTTLS || SASL */
-
-# if SASL
-# define DONE_AUTH(f) bitset(MCIF_AUTHACT, f)
-# endif /* SASL */
-# if STARTTLS
-# define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f)
-# endif /* STARTTLS */
-# define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f)
-# define SET_HELO(f) f |= MCIF_ONLY_EHLO
-# define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO
-
-# if STARTTLS || SASL
- /* don't use CurHostName, it is changed in many places */
- if (mci->mci_host != NULL)
- {
- srvname = mci->mci_host;
- dotpos = strlen(srvname) - 1;
- if (dotpos >= 0)
- {
- if (srvname[dotpos] == '.')
- srvname[dotpos] = '\0';
- else
- dotpos = -1;
- }
- }
- else if (mci->mci_mailer != NULL)
- {
- srvname = mci->mci_mailer->m_name;
- dotpos = -1;
- }
- else
- {
- srvname = "local";
- dotpos = -1;
- }
-
- /* don't set {server_name} to NULL or "": see getauth() */
- macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"),
- srvname);
-
- /* CurHostAddr is set by makeconnection() and mci_get() */
- if (CurHostAddr.sa.sa_family != 0)
- {
- macdefine(&mci->mci_macro, A_TEMP,
- macid("{server_addr}"),
- anynet_ntoa(&CurHostAddr));
- }
- else if (mci->mci_mailer != NULL)
- {
- /* mailer name is unique, use it as address */
- macdefine(&mci->mci_macro, A_PERM,
- macid("{server_addr}"),
- mci->mci_mailer->m_name);
- }
- else
- {
- /* don't set it to NULL or "": see getauth() */
- macdefine(&mci->mci_macro, A_PERM,
- macid("{server_addr}"), "0");
- }
-
- /* undo change of srvname (mci->mci_host) */
- if (dotpos >= 0)
- srvname[dotpos] = '.';
-
-reconnect: /* after switching to an encrypted connection */
-# endif /* STARTTLS || SASL */
-
- /* set the current connection information */
- e->e_mci = mci;
-# if SASL
- mci->mci_saslcap = NULL;
-# endif /* SASL */
- smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags));
- CLR_HELO(mci->mci_flags);
-
- if (IS_DLVR_RETURN(e))
- {
- /*
- ** Check whether other side can deliver e-mail
- ** fast enough
- */
-
- if (!bitset(MCIF_DLVR_BY, mci->mci_flags))
- {
- e->e_status = "5.4.7";
- usrerrenh(e->e_status,
- "554 Server does not support Deliver By");
- rcode = EX_UNAVAILABLE;
- goto give_up;
- }
- if (e->e_deliver_by > 0 &&
- e->e_deliver_by - (curtime() - e->e_ctime) <
- mci->mci_min_by)
- {
- e->e_status = "5.4.7";
- usrerrenh(e->e_status,
- "554 Message can't be delivered in time; %ld < %ld",
- e->e_deliver_by - (curtime() - e->e_ctime),
- mci->mci_min_by);
- rcode = EX_UNAVAILABLE;
- goto give_up;
- }
- }
-
-# if STARTTLS
- /* first TLS then AUTH to provide a security layer */
- if (mci->mci_state != MCIS_CLOSED &&
- !DONE_STARTTLS(mci->mci_flags))
- {
- int olderrors;
- bool usetls;
- bool saveQuickAbort = QuickAbort;
- bool saveSuprErrs = SuprErrs;
- char *host = NULL;
-
- rcode = EX_OK;
- usetls = bitset(MCIF_TLS, mci->mci_flags);
- if (usetls)
- usetls = !iscltflgset(e, D_NOTLS);
-
- host = macvalue(macid("{server_name}"), e);
- if (usetls)
- {
- olderrors = Errors;
- QuickAbort = false;
- SuprErrs = true;
- if (rscheck("try_tls", host, NULL, e,
- RSF_RMCOMM, 7, host, NOQID, NULL)
- != EX_OK
- || Errors > olderrors)
- {
- usetls = false;
- }
- SuprErrs = saveSuprErrs;
- QuickAbort = saveQuickAbort;
- }
-
- if (usetls)
- {
- if ((rcode = starttls(m, mci, e)) == EX_OK)
- {
- /* start again without STARTTLS */
- mci->mci_flags |= MCIF_TLSACT;
- }
- else
- {
- char *s;
-
- /*
- ** TLS negotation failed, what to do?
- ** fall back to unencrypted connection
- ** or abort? How to decide?
- ** set a macro and call a ruleset.
- */
-
- mci->mci_flags &= ~MCIF_TLS;
- switch (rcode)
- {
- case EX_TEMPFAIL:
- s = "TEMP";
- break;
- case EX_USAGE:
- s = "USAGE";
- break;
- case EX_PROTOCOL:
- s = "PROTOCOL";
- break;
- case EX_SOFTWARE:
- s = "SOFTWARE";
- break;
- case EX_UNAVAILABLE:
- s = "NONE";
- break;
-
- /* everything else is a failure */
- default:
- s = "FAILURE";
- rcode = EX_TEMPFAIL;
- }
- macdefine(&e->e_macro, A_PERM,
- macid("{verify}"), s);
- }
- }
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{verify}"), "NONE");
- 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}"), e),
- NULL, e, RSF_RMCOMM|RSF_COUNT, 5,
- host, NOQID, NULL) != EX_OK ||
- Errors > olderrors ||
- rcode == EX_SOFTWARE)
- {
- char enhsc[ENHSCLEN];
- extern char MsgBuf[];
-
- if (ISSMTPCODE(MsgBuf) &&
- extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
- {
- p = sm_rpool_strdup_x(e->e_rpool,
- MsgBuf);
- }
- else
- {
- p = "403 4.7.0 server not authenticated.";
- (void) sm_strlcpy(enhsc, "4.7.0",
- sizeof(enhsc));
- }
- SuprErrs = saveSuprErrs;
- QuickAbort = saveQuickAbort;
-
- if (rcode == EX_SOFTWARE)
- {
- /* drop the connection */
- mci->mci_state = MCIS_QUITING;
- if (mci->mci_in != NULL)
- {
- (void) sm_io_close(mci->mci_in,
- SM_TIME_DEFAULT);
- mci->mci_in = NULL;
- }
- mci->mci_flags &= ~MCIF_TLSACT;
- (void) endmailer(mci, e, pv);
- }
- else
- {
- /* abort transfer */
- smtpquit(m, mci, e);
- }
-
- /* avoid bogus error msg */
- mci->mci_errno = 0;
-
- /* temp or permanent failure? */
- rcode = (*p == '4') ? EX_TEMPFAIL
- : EX_UNAVAILABLE;
- mci_setstat(mci, rcode, enhsc, p);
-
- /*
- ** hack to get the error message into
- ** the envelope (done in giveresponse())
- */
-
- (void) sm_strlcpy(SmtpError, p,
- sizeof(SmtpError));
- }
- else if (mci->mci_state == MCIS_CLOSED)
- {
- /* connection close caused by 421 */
- mci->mci_errno = 0;
- rcode = EX_TEMPFAIL;
- mci_setstat(mci, rcode, NULL, "421");
- }
- else
- rcode = 0;
-
- QuickAbort = saveQuickAbort;
- SuprErrs = saveSuprErrs;
- if (DONE_STARTTLS(mci->mci_flags) &&
- mci->mci_state != MCIS_CLOSED)
- {
- SET_HELO(mci->mci_flags);
- mci->mci_flags &= ~MCIF_EXTENS;
- goto reconnect;
- }
- }
-# endif /* STARTTLS */
-# if SASL
- /* if other server supports authentication let's authenticate */
- if (mci->mci_state != MCIS_CLOSED &&
- mci->mci_saslcap != NULL &&
- !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH))
- {
- /* Should we require some minimum authentication? */
- if ((ret = smtpauth(m, mci, e)) == EX_OK)
- {
- int result;
- sasl_ssf_t *ssf = NULL;
-
- /* Get security strength (features) */
- result = sasl_getprop(mci->mci_conn, SASL_SSF,
-# if SASL >= 20000
- (const void **) &ssf);
-# else /* SASL >= 20000 */
- (void **) &ssf);
-# endif /* SASL >= 20000 */
-
- /* XXX authid? */
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, NOQID,
- "AUTH=client, relay=%.100s, mech=%.16s, bits=%d",
- mci->mci_host,
- macvalue(macid("{auth_type}"), e),
- result == SASL_OK ? *ssf : 0);
-
- /*
- ** Only switch to encrypted connection
- ** if a security layer has been negotiated
- */
-
- if (result == SASL_OK && *ssf > 0)
- {
- int tmo;
-
- /*
- ** Convert I/O layer to use SASL.
- ** If the call fails, the connection
- ** is aborted.
- */
-
- tmo = DATA_PROGRESS_TIMEOUT * 1000;
- if (sfdcsasl(&mci->mci_in,
- &mci->mci_out,
- mci->mci_conn, tmo) == 0)
- {
- mci->mci_flags &= ~MCIF_EXTENS;
- mci->mci_flags |= MCIF_AUTHACT|
- MCIF_ONLY_EHLO;
- goto reconnect;
- }
- syserr("AUTH TLS switch failed in client");
- }
- /* else? XXX */
- mci->mci_flags |= MCIF_AUTHACT;
-
- }
- else if (ret == EX_TEMPFAIL)
- {
- if (LogLevel > 8)
- sm_syslog(LOG_ERR, NOQID,
- "AUTH=client, relay=%.100s, temporary failure, connection abort",
- mci->mci_host);
- smtpquit(m, mci, e);
-
- /* avoid bogus error msg */
- mci->mci_errno = 0;
- rcode = EX_TEMPFAIL;
- mci_setstat(mci, rcode, "4.3.0", p);
-
- /*
- ** hack to get the error message into
- ** the envelope (done in giveresponse())
- */
-
- (void) sm_strlcpy(SmtpError,
- "Temporary AUTH failure",
- sizeof(SmtpError));
- }
- }
-# endif /* SASL */
- }
-
-
-do_transfer:
- /* clear out per-message flags from connection structure */
- mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
-
- if (bitset(EF_HAS8BIT, e->e_flags) &&
- !bitset(EF_DONT_MIME, e->e_flags) &&
- bitnset(M_7BITS, m->m_flags))
- mci->mci_flags |= MCIF_CVT8TO7;
-
-#if MIME7TO8
- if (bitnset(M_MAKE8BIT, m->m_flags) &&
- !bitset(MCIF_7BIT, mci->mci_flags) &&
- (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
- (sm_strcasecmp(p, "quoted-printable") == 0 ||
- sm_strcasecmp(p, "base64") == 0) &&
- (p = hvalue("Content-Type", e->e_header)) != NULL)
- {
- /* may want to convert 7 -> 8 */
- /* XXX should really parse it here -- and use a class XXX */
- if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
- (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
- mci->mci_flags |= MCIF_CVT7TO8;
- }
-#endif /* MIME7TO8 */
-
- if (tTd(11, 1))
- {
- sm_dprintf("openmailer: ");
- mci_dump(sm_debug_file(), mci, false);
- }
-
-#if _FFR_CLIENT_SIZE
- /*
- ** See if we know the maximum size and
- ** abort if the message is too big.
- **
- ** NOTE: _FFR_CLIENT_SIZE is untested.
- */
-
- if (bitset(MCIF_SIZE, mci->mci_flags) &&
- mci->mci_maxsize > 0 &&
- e->e_msgsize > mci->mci_maxsize)
- {
- e->e_flags |= EF_NO_BODY_RETN;
- if (bitnset(M_LOCALMAILER, m->m_flags))
- e->e_status = "5.2.3";
- else
- e->e_status = "5.3.4";
-
- usrerrenh(e->e_status,
- "552 Message is too large; %ld bytes max",
- mci->mci_maxsize);
- rcode = EX_DATAERR;
-
- /* Need an e_message for error */
- (void) sm_snprintf(SmtpError, sizeof(SmtpError),
- "Message is too large; %ld bytes max",
- mci->mci_maxsize);
- goto give_up;
- }
-#endif /* _FFR_CLIENT_SIZE */
-
- if (mci->mci_state != MCIS_OPEN)
- {
- /* couldn't open the mailer */
- rcode = mci->mci_exitstat;
- errno = mci->mci_errno;
- SM_SET_H_ERRNO(mci->mci_herrno);
- if (rcode == EX_OK)
- {
- /* shouldn't happen */
- syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
- (unsigned long) mci, rcode, errno,
- mci->mci_state, firstsig);
- mci_dump_all(smioout, true);
- rcode = EX_SOFTWARE;
- }
- else if (nummxhosts > hostnum)
- {
- /* try next MX site */
- goto tryhost;
- }
- }
- else if (!clever)
- {
- bool ok;
-
- /*
- ** Format and send message.
- */
-
- rcode = EX_OK;
- errno = 0;
- ok = putfromline(mci, e);
- if (ok)
- ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
- if (ok)
- ok = (*e->e_putbody)(mci, e, NULL);
- if (ok && bitset(MCIF_INLONGLINE, mci->mci_flags))
- ok = putline("", mci);
-
- /*
- ** Ignore an I/O error that was caused by EPIPE.
- ** Some broken mailers don't read the entire body
- ** but just exit() thus causing an I/O error.
- */
-
- if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE))
- ok = true;
-
- /* (always) get the exit status */
- rcode = endmailer(mci, e, pv);
- if (!ok)
- rcode = EX_TEMPFAIL;
- if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
- {
- /*
- ** Need an e_message for mailq display.
- ** We set SmtpError as
- */
-
- (void) sm_snprintf(SmtpError, sizeof(SmtpError),
- "%s mailer (%s) exited with EX_TEMPFAIL",
- m->m_name, m->m_mailer);
- }
- }
- else
- {
- /*
- ** Send the MAIL FROM: protocol
- */
-
- /* XXX this isn't pipelined... */
- rcode = smtpmailfrom(m, mci, e);
- if (rcode == EX_OK)
- {
- register int i;
-# if PIPELINING
- ADDRESS *volatile pchain;
-# endif /* PIPELINING */
-
- /* send the recipient list */
- tobuf[0] = '\0';
- mci->mci_retryrcpt = false;
- mci->mci_tolist = tobuf;
-# if PIPELINING
- pchain = NULL;
- mci->mci_nextaddr = NULL;
-# endif /* PIPELINING */
-
- for (to = tochain; to != NULL; to = to->q_tchain)
- {
- if (!QS_IS_UNMARKED(to->q_state))
- continue;
-
- /* mark recipient state as "ok so far" */
- to->q_state = QS_OK;
- e->e_to = to->q_paddr;
-# if STARTTLS
- i = rscheck("tls_rcpt", to->q_user, NULL, e,
- RSF_RMCOMM|RSF_COUNT, 3,
- mci->mci_host, e->e_id, NULL);
- if (i != EX_OK)
- {
- markfailure(e, to, mci, i, false);
- giveresponse(i, to->q_status, m, mci,
- ctladdr, xstart, e, to);
- if (i == EX_TEMPFAIL)
- {
- mci->mci_retryrcpt = true;
- to->q_state = QS_RETRY;
- }
- continue;
- }
-# endif /* STARTTLS */
-
- i = smtprcpt(to, m, mci, e, ctladdr, xstart);
-# if PIPELINING
- if (i == EX_OK &&
- bitset(MCIF_PIPELINED, mci->mci_flags))
- {
- /*
- ** Add new element to list of
- ** recipients for pipelining.
- */
-
- to->q_pchain = NULL;
- if (mci->mci_nextaddr == NULL)
- mci->mci_nextaddr = to;
- if (pchain == NULL)
- pchain = to;
- else
- {
- pchain->q_pchain = to;
- pchain = pchain->q_pchain;
- }
- }
-# endif /* PIPELINING */
- if (i != EX_OK)
- {
- markfailure(e, to, mci, i, false);
- giveresponse(i, to->q_status, m, mci,
- ctladdr, xstart, e, to);
- if (i == EX_TEMPFAIL)
- to->q_state = QS_RETRY;
- }
- }
-
- /* No recipients in list and no missing responses? */
- if (tobuf[0] == '\0'
-# if PIPELINING
- && bitset(MCIF_PIPELINED, mci->mci_flags)
- && mci->mci_nextaddr == NULL
-# endif /* PIPELINING */
- )
- {
- rcode = EX_OK;
- e->e_to = NULL;
- if (bitset(MCIF_CACHED, mci->mci_flags))
- smtprset(m, mci, e);
- }
- else
- {
- e->e_to = tobuf + 1;
- rcode = smtpdata(m, mci, e, ctladdr, xstart);
- }
- }
- if (rcode == EX_TEMPFAIL && nummxhosts > hostnum)
- {
- /* try next MX site */
- goto tryhost;
- }
- }
-#if NAMED_BIND
- if (ConfigLevel < 2)
- _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */
-#endif /* NAMED_BIND */
-
- if (tTd(62, 1))
- checkfds("after delivery");
-
- /*
- ** Do final status disposal.
- ** We check for something in tobuf for the SMTP case.
- ** If we got a temporary failure, arrange to queue the
- ** addressees.
- */
-
- give_up:
- if (bitnset(M_LMTP, m->m_flags))
- {
- lmtp_rcode = rcode;
- tobuf[0] = '\0';
- anyok = false;
- strsize = 0;
- }
- else
- anyok = rcode == EX_OK;
-
- for (to = tochain; to != NULL; to = to->q_tchain)
- {
- /* see if address already marked */
- if (!QS_IS_OK(to->q_state))
- continue;
-
- /* if running LMTP, get the status for each address */
- if (bitnset(M_LMTP, m->m_flags))
- {
- if (lmtp_rcode == EX_OK)
- rcode = smtpgetstat(m, mci, e);
- if (rcode == EX_OK)
- {
- strsize += sm_strlcat2(tobuf + strsize, ",",
- to->q_paddr,
- tobufsize - strsize);
- SM_ASSERT(strsize < tobufsize);
- anyok = true;
- }
- else
- {
- e->e_to = to->q_paddr;
- markfailure(e, to, mci, rcode, true);
- giveresponse(rcode, to->q_status, m, mci,
- ctladdr, xstart, e, to);
- e->e_to = tobuf + 1;
- continue;
- }
- }
- else
- {
- /* mark bad addresses */
- if (rcode != EX_OK)
- {
- if (goodmxfound && rcode == EX_NOHOST)
- rcode = EX_TEMPFAIL;
- markfailure(e, to, mci, rcode, true);
- continue;
- }
- }
-
- /* successful delivery */
- to->q_state = QS_SENT;
- to->q_statdate = curtime();
- e->e_nsent++;
-
- /*
- ** Checkpoint the send list every few addresses
- */
-
- if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval)
- {
- queueup(e, false, false);
- e->e_nsent = 0;
- }
-
- if (bitnset(M_LOCALMAILER, m->m_flags) &&
- bitset(QPINGONSUCCESS, to->q_flags))
- {
- to->q_flags |= QDELIVERED;
- to->q_status = "2.1.5";
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "%s... Successfully delivered\n",
- to->q_paddr);
- }
- else if (bitset(QPINGONSUCCESS, to->q_flags) &&
- bitset(QPRIMARY, to->q_flags) &&
- !bitset(MCIF_DSN, mci->mci_flags))
- {
- to->q_flags |= QRELAYED;
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "%s... relayed; expect no further notifications\n",
- to->q_paddr);
- }
- else if (IS_DLVR_NOTIFY(e) &&
- !bitset(MCIF_DLVR_BY, mci->mci_flags) &&
- bitset(QPRIMARY, to->q_flags) &&
- (!bitset(QHASNOTIFY, to->q_flags) ||
- bitset(QPINGONSUCCESS, to->q_flags) ||
- bitset(QPINGONFAILURE, to->q_flags) ||
- bitset(QPINGONDELAY, to->q_flags)))
- {
- /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */
- to->q_flags |= QBYNRELAY;
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "%s... Deliver-by notify: relayed\n",
- to->q_paddr);
- }
- else if (IS_DLVR_TRACE(e) &&
- (!bitset(QHASNOTIFY, to->q_flags) ||
- bitset(QPINGONSUCCESS, to->q_flags) ||
- bitset(QPINGONFAILURE, to->q_flags) ||
- bitset(QPINGONDELAY, to->q_flags)) &&
- bitset(QPRIMARY, to->q_flags))
- {
- /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */
- to->q_flags |= QBYTRACE;
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "%s... Deliver-By trace: relayed\n",
- to->q_paddr);
- }
- }
-
- if (bitnset(M_LMTP, m->m_flags))
- {
- /*
- ** Global information applies to the last recipient only;
- ** clear it out to avoid bogus errors.
- */
-
- rcode = EX_OK;
- e->e_statmsg = NULL;
-
- /* reset the mci state for the next transaction */
- if (mci != NULL &&
- (mci->mci_state == MCIS_MAIL ||
- mci->mci_state == MCIS_RCPT ||
- mci->mci_state == MCIS_DATA))
- {
- mci->mci_state = MCIS_OPEN;
- SmtpPhase = mci->mci_phase = "idle";
- sm_setproctitle(true, e, "%s: %s", CurHostName,
- mci->mci_phase);
- }
- }
-
- if (tobuf[0] != '\0')
- {
- giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain);
-#if 0
- /*
- ** This code is disabled for now because I am not
- ** sure that copying status from the first recipient
- ** to all non-status'ed recipients is a good idea.
- */
-
- if (tochain->q_message != NULL &&
- !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK)
- {
- for (to = tochain->q_tchain; to != NULL;
- to = to->q_tchain)
- {
- /* see if address already marked */
- if (QS_IS_QUEUEUP(to->q_state) &&
- to->q_message == NULL)
- to->q_message = sm_rpool_strdup_x(e->e_rpool,
- tochain->q_message);
- }
- }
-#endif /* 0 */
- }
- if (anyok)
- markstats(e, tochain, STATS_NORMAL);
- mci_store_persistent(mci);
-
- /* Some recipients were tempfailed, try them on the next host */
- if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum)
- {
- /* try next MX site */
- goto tryhost;
- }
-
- /* now close the connection */
- if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
- !bitset(MCIF_CACHED, mci->mci_flags))
- smtpquit(m, mci, e);
-
-cleanup: ;
- }
- SM_FINALLY
- {
- /*
- ** Restore state and return.
- */
-#if XDEBUG
- char wbuf[MAXLINE];
-
- /* make absolutely certain 0, 1, and 2 are in use */
- (void) sm_snprintf(wbuf, sizeof(wbuf),
- "%s... end of deliver(%s)",
- e->e_to == NULL ? "NO-TO-LIST"
- : shortenstring(e->e_to,
- MAXSHORTSTR),
- m->m_name);
- checkfd012(wbuf);
-#endif /* XDEBUG */
-
- errno = 0;
-
- /*
- ** It was originally necessary to set macro 'g' to NULL
- ** because it previously pointed to an auto buffer.
- ** We don't do this any more, so this may be unnecessary.
- */
-
- macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL);
- e->e_to = NULL;
- }
- SM_END_TRY
- return rcode;
-}
-
-/*
-** MARKFAILURE -- mark a failure on a specific address.
-**
-** Parameters:
-** e -- the envelope we are sending.
-** q -- the address to mark.
-** mci -- mailer connection information.
-** rcode -- the code signifying the particular failure.
-** ovr -- override an existing code?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** marks the address (and possibly the envelope) with the
-** failure so that an error will be returned or
-** the message will be queued, as appropriate.
-*/
-
-void
-markfailure(e, q, mci, rcode, ovr)
- register ENVELOPE *e;
- register ADDRESS *q;
- register MCI *mci;
- int rcode;
- bool ovr;
-{
- int save_errno = errno;
- char *status = NULL;
- char *rstatus = NULL;
-
- switch (rcode)
- {
- case EX_OK:
- break;
-
- case EX_TEMPFAIL:
- case EX_IOERR:
- case EX_OSERR:
- q->q_state = QS_QUEUEUP;
- break;
-
- default:
- q->q_state = QS_BADADDR;
- break;
- }
-
- /* find most specific error code possible */
- if (mci != NULL && mci->mci_status != NULL)
- {
- status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status);
- if (mci->mci_rstatus != NULL)
- rstatus = sm_rpool_strdup_x(e->e_rpool,
- mci->mci_rstatus);
- else
- rstatus = NULL;
- }
- else if (e->e_status != NULL)
- {
- status = e->e_status;
- rstatus = NULL;
- }
- else
- {
- switch (rcode)
- {
- case EX_USAGE:
- status = "5.5.4";
- break;
-
- case EX_DATAERR:
- status = "5.5.2";
- break;
-
- case EX_NOUSER:
- status = "5.1.1";
- break;
-
- case EX_NOHOST:
- status = "5.1.2";
- break;
-
- case EX_NOINPUT:
- case EX_CANTCREAT:
- case EX_NOPERM:
- status = "5.3.0";
- break;
-
- case EX_UNAVAILABLE:
- case EX_SOFTWARE:
- case EX_OSFILE:
- case EX_PROTOCOL:
- case EX_CONFIG:
- status = "5.5.0";
- break;
-
- case EX_OSERR:
- case EX_IOERR:
- status = "4.5.0";
- break;
-
- case EX_TEMPFAIL:
- status = "4.2.0";
- break;
- }
- }
-
- /* 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 &&
- sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0)
- {
- char buf[16];
-
- (void) sm_snprintf(buf, sizeof(buf), "%d", rcode);
- q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf);
- }
-
- q->q_statdate = curtime();
- if (CurHostName != NULL && CurHostName[0] != '\0' &&
- mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags))
- q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName);
-
- /* restore errno */
- errno = save_errno;
-}
-/*
-** ENDMAILER -- Wait for mailer to terminate.
-**
-** We should never get fatal errors (e.g., segmentation
-** violation), so we report those specially. For other
-** errors, we choose a status message (into statmsg),
-** and if it represents an error, we print it.
-**
-** Parameters:
-** mci -- the mailer connection info.
-** e -- the current envelope.
-** pv -- the parameter vector that invoked the mailer
-** (for error messages).
-**
-** Returns:
-** exit code of mailer.
-**
-** Side Effects:
-** none.
-*/
-
-static jmp_buf EndWaitTimeout;
-
-static void
-endwaittimeout(ignore)
- int ignore;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(EndWaitTimeout, 1);
-}
-
-int
-endmailer(mci, e, pv)
- register MCI *mci;
- register ENVELOPE *e;
- char **pv;
-{
- int st;
- int save_errno = errno;
- char buf[MAXLINE];
- SM_EVENT *ev = NULL;
-
-
- mci_unlock_host(mci);
-
- /* close output to mailer */
- if (mci->mci_out != NULL)
- {
- (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
- mci->mci_out = NULL;
- }
-
- /* 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) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf);
- }
-
-#if SASL
- /* close SASL connection */
- if (bitset(MCIF_AUTHACT, mci->mci_flags))
- {
- sasl_dispose(&mci->mci_conn);
- mci->mci_flags &= ~MCIF_AUTHACT;
- }
-#endif /* SASL */
-
-#if STARTTLS
- /* shutdown TLS */
- (void) endtlsclt(mci);
-#endif /* STARTTLS */
-
- /* now close the input */
- if (mci->mci_in != NULL)
- {
- (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
- mci->mci_in = 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;
-
- /* put a timeout around the wait */
- if (mci->mci_mailer->m_wait > 0)
- {
- if (setjmp(EndWaitTimeout) == 0)
- ev = sm_setevent(mci->mci_mailer->m_wait,
- endwaittimeout, 0);
- else
- {
- syserr("endmailer %s: wait timeout (%ld)",
- mci->mci_mailer->m_name,
- (long) mci->mci_mailer->m_wait);
- return EX_TEMPFAIL;
- }
- }
-
- /* wait for the mailer process, collect status */
- st = waitfor(mci->mci_pid);
- save_errno = errno;
- if (ev != NULL)
- sm_clrevent(ev);
- errno = save_errno;
-
- if (st == -1)
- {
- syserr("endmailer %s: wait", mci->mci_mailer->m_name);
- return EX_SOFTWARE;
- }
-
- if (WIFEXITED(st))
- {
- /* normal death -- return status */
- return (WEXITSTATUS(st));
- }
-
- /* it died a horrid death */
- 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)
- {
- register char **av;
-
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:");
- for (av = pv; *av != NULL; av++)
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s",
- *av);
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n");
- }
-
- ExitStat = EX_TEMPFAIL;
- return EX_TEMPFAIL;
-}
-/*
-** GIVERESPONSE -- Interpret an error response from a mailer
-**
-** Parameters:
-** 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.
-** ctladdr -- the controlling address for the recipient
-** address(es).
-** xstart -- the transaction start time, for computing
-** transaction delays.
-** e -- the current envelope.
-** to -- the current recipient (NULL if none).
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Errors may be incremented.
-** ExitStat may be set.
-*/
-
-void
-giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to)
- int status;
- char *dsn;
- register MAILER *m;
- register MCI *mci;
- ADDRESS *ctladdr;
- time_t xstart;
- ENVELOPE *e;
- ADDRESS *to;
-{
- register const char *statmsg;
- int errnum = errno;
- int off = 4;
- bool usestat = false;
- char dsnbuf[ENHSCLEN];
- char buf[MAXLINE];
- char *exmsg;
-
- if (e == NULL)
- {
- syserr("giveresponse: null envelope");
- /* NOTREACHED */
- SM_ASSERT(0);
- }
-
- /*
- ** Compute status message from code.
- */
-
- exmsg = sm_sysexmsg(status);
- if (status == 0)
- {
- statmsg = "250 2.0.0 Sent";
- if (e->e_statmsg != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf), "%s (%s)",
- statmsg,
- shortenstring(e->e_statmsg, 403));
- statmsg = buf;
- }
- }
- else if (exmsg == NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "554 5.3.0 unknown mailer error %d",
- status);
- status = EX_UNAVAILABLE;
- statmsg = buf;
- usestat = true;
- }
- else if (status == EX_TEMPFAIL)
- {
- char *bp = buf;
-
- (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp));
- bp += strlen(bp);
-#if NAMED_BIND
- if (h_errno == TRY_AGAIN)
- statmsg = sm_errstring(h_errno + E_DNSBASE);
- else
-#endif /* NAMED_BIND */
- {
- if (errnum != 0)
- statmsg = sm_errstring(errnum);
- else
- statmsg = SmtpError;
- }
- 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 != NULL && mci->mci_host != NULL)
- {
- (void) sm_strlcpyn(bp,
- SPACELEFT(buf, bp),
- 2, ": ",
- mci->mci_host);
- bp += strlen(bp);
- }
- break;
- }
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ",
- statmsg);
- usestat = true;
- }
- statmsg = buf;
- }
-#if NAMED_BIND
- else if (status == EX_NOHOST && h_errno != 0)
- {
- statmsg = sm_errstring(h_errno + E_DNSBASE);
- (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", exmsg + 1,
- statmsg);
- statmsg = buf;
- usestat = true;
- }
-#endif /* NAMED_BIND */
- else
- {
- statmsg = exmsg;
- if (*statmsg++ == ':' && errnum != 0)
- {
- (void) sm_snprintf(buf, sizeof(buf), "%s: %s", statmsg,
- sm_errstring(errnum));
- statmsg = buf;
- usestat = true;
- }
- else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", statmsg,
- shortenstring(e->e_statmsg, 403));
- statmsg = buf;
- usestat = true;
- }
- }
-
- /*
- ** Print the message as appropriate
- */
-
- if (status == EX_OK || status == EX_TEMPFAIL)
- {
- extern char MsgBuf[];
-
- if ((off = isenhsc(statmsg + 4, ' ')) > 0)
- {
- if (dsn == NULL)
- {
- (void) sm_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)
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n",
- &MsgBuf[4]);
- }
- else
- {
- char mbuf[ENHSCLEN + 4];
-
- Errors++;
- if ((off = isenhsc(statmsg + 4, ' ')) > 0 &&
- off < sizeof(mbuf) - 4)
- {
- if (dsn == NULL)
- {
- (void) sm_snprintf(dsnbuf, sizeof(dsnbuf),
- "%.*s", off, statmsg + 4);
- dsn = dsnbuf;
- }
- off += 5;
-
- /* copy only part of statmsg to mbuf */
- (void) sm_strlcpy(mbuf, statmsg, off);
- (void) sm_strlcat(mbuf, " %s", sizeof(mbuf));
- }
- else
- {
- dsnbuf[0] = '\0';
- (void) sm_snprintf(mbuf, sizeof(mbuf), "%.3s %%s",
- statmsg);
- off = 4;
- }
- usrerr(mbuf, &statmsg[off]);
- }
-
- /*
- ** Final cleanup.
- ** Log a record of the transaction. Compute the new
- ** ExitStat -- if we already had an error, stick with
- ** that.
- */
-
- if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
- LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6))
- logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e);
-
- if (tTd(11, 2))
- sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n",
- status,
- dsn == NULL ? "<NULL>" : dsn,
- e->e_message == NULL ? "<NULL>" : e->e_message,
- errnum);
-
- if (status != EX_TEMPFAIL)
- setstat(status);
- if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL))
- e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off);
- if (status != EX_OK && to != NULL && to->q_message == NULL)
- {
- if (!usestat && e->e_message != NULL)
- to->q_message = sm_rpool_strdup_x(e->e_rpool,
- e->e_message);
- else
- to->q_message = sm_rpool_strdup_x(e->e_rpool,
- statmsg + off);
- }
- errno = 0;
- SM_SET_H_ERRNO(0);
-}
-/*
-** LOGDELIVERY -- log the delivery in the system log
-**
-** Care is taken to avoid logging lines that are too long, because
-** some versions of syslog have an unfortunate proclivity for core
-** dumping. This is a hack, to be sure, that is at best empirical.
-**
-** Parameters:
-** m -- the mailer info. Can be NULL for initial queue.
-** mci -- the mailer connection info -- can be NULL if the
-** 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.
-** e -- the current envelope.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** none
-*/
-
-void
-logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
- MAILER *m;
- register MCI *mci;
- char *dsn;
- const char *status;
- ADDRESS *ctladdr;
- time_t xstart;
- register ENVELOPE *e;
-{
- register char *bp;
- register char *p;
- int l;
- time_t now = curtime();
- char buf[1024];
-
-#if (SYSLOG_BUFSIZE) >= 256
- /* ctladdr: max 106 bytes */
- bp = buf;
- if (ctladdr != NULL)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=",
- shortenstring(ctladdr->q_paddr, 83));
- bp += strlen(bp);
- if (bitset(QGOODUID, ctladdr->q_flags))
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
- (int) ctladdr->q_uid,
- (int) ctladdr->q_gid);
- bp += strlen(bp);
- }
- }
-
- /* delay & xdelay: max 41 bytes */
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=",
- pintvl(now - e->e_ctime, true));
- bp += strlen(bp);
-
- if (xstart != (time_t) 0)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
- pintvl(now - xstart, true));
- bp += strlen(bp);
- }
-
- /* mailer: assume about 19 bytes (max 10 byte mailer name) */
- if (m != NULL)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
- m->m_name);
- bp += strlen(bp);
- }
-
- /* pri: changes with each delivery attempt */
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld",
- e->e_msgpriority);
- bp += strlen(bp);
-
- /* relay: max 66 bytes for IPv4 addresses */
- if (mci != NULL && mci->mci_host != NULL)
- {
- extern SOCKADDR CurHostAddr;
-
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=",
- shortenstring(mci->mci_host, 40));
- bp += strlen(bp);
-
- if (CurHostAddr.sa.sa_family != 0)
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]",
- anynet_ntoa(&CurHostAddr));
- }
- }
- else if (strcmp(status, "quarantined") == 0)
- {
- if (e->e_quarmsg != NULL)
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- ", quarantine=%s",
- shortenstring(e->e_quarmsg, 40));
- }
- else if (strcmp(status, "queued") != 0)
- {
- p = macvalue('h', e);
- if (p != NULL && p[0] != '\0')
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- ", relay=%s", shortenstring(p, 40));
- }
- }
- bp += strlen(bp);
-
- /* dsn */
- if (dsn != NULL && *dsn != '\0')
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=",
- shortenstring(dsn, ENHSCLEN));
- bp += strlen(bp);
- }
-
-#if _FFR_LOG_NTRIES
- /* ntries */
- if (e->e_ntries >= 0)
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- ", ntries=%d", e->e_ntries + 1);
- bp += strlen(bp);
- }
-#endif /* _FFR_LOG_NTRIES */
-
-# 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);
- (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp));
- bp += 3;
- }
-
- (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp));
- bp += strlen(bp);
-
- (void) sm_strlcpy(bp, shortenstring(status, STATLEN),
- SPACELEFT(buf, bp));
-
- /* id, to: max 13 + TOBUFSIZE bytes */
- l = SYSLOG_BUFSIZE - 100 - strlen(buf);
- if (l < 0)
- l = 0;
- p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
- while (strlen(p) >= l)
- {
- register char *q;
-
- for (q = p + l; q > p; q--)
- {
- if (*q == ',')
- break;
- }
- if (p == q)
- break;
- sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s",
- (int) (++q - p), p, buf);
- p = q;
- }
- sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf);
-
-#else /* (SYSLOG_BUFSIZE) >= 256 */
-
- l = SYSLOG_BUFSIZE - 85;
- if (l < 0)
- l = 0;
- p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to;
- while (strlen(p) >= l)
- {
- register char *q;
-
- for (q = p + l; q > p; q--)
- {
- if (*q == ',')
- break;
- }
- if (p == q)
- break;
-
- sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]",
- (int) (++q - p), p);
- p = q;
- }
- sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p);
-
- if (ctladdr != NULL)
- {
- bp = buf;
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=",
- shortenstring(ctladdr->q_paddr, 83));
- bp += strlen(bp);
- if (bitset(QGOODUID, ctladdr->q_flags))
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
- ctladdr->q_uid, ctladdr->q_gid);
- bp += strlen(bp);
- }
- sm_syslog(LOG_INFO, e->e_id, "%s", buf);
- }
- bp = buf;
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=",
- pintvl(now - e->e_ctime, true));
- bp += strlen(bp);
- if (xstart != (time_t) 0)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=",
- pintvl(now - xstart, true));
- bp += strlen(bp);
- }
-
- if (m != NULL)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=",
- m->m_name);
- bp += strlen(bp);
- }
- sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
-
- buf[0] = '\0';
- bp = buf;
- if (mci != NULL && mci->mci_host != NULL)
- {
- extern SOCKADDR CurHostAddr;
-
- (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s",
- mci->mci_host);
- bp += strlen(bp);
-
- if (CurHostAddr.sa.sa_family != 0)
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- " [%.100s]",
- anynet_ntoa(&CurHostAddr));
- }
- else if (strcmp(status, "quarantined") == 0)
- {
- if (e->e_quarmsg != NULL)
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- ", quarantine=%.100s",
- e->e_quarmsg);
- }
- else if (strcmp(status, "queued") != 0)
- {
- p = macvalue('h', e);
- if (p != NULL && p[0] != '\0')
- (void) sm_snprintf(buf, sizeof(buf), "relay=%.100s", p);
- }
- if (buf[0] != '\0')
- sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
-
- sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63));
-#endif /* (SYSLOG_BUFSIZE) >= 256 */
-}
-/*
-** PUTFROMLINE -- output a UNIX-style from line (or whatever)
-**
-** This can be made an arbitrary message separator by changing $l
-**
-** One of the ugliest hacks seen by human eyes is contained herein:
-** UUCP wants those stupid "remote from <host>" lines. Why oh why
-** does a well-meaning programmer such as myself have to deal with
-** this kind of antique garbage????
-**
-** Parameters:
-** mci -- the connection information.
-** e -- the envelope.
-**
-** Returns:
-** true iff line was written successfully
-**
-** Side Effects:
-** outputs some text to fp.
-*/
-
-bool
-putfromline(mci, e)
- register MCI *mci;
- ENVELOPE *e;
-{
- char *template = UnixFromLine;
- char buf[MAXLINE];
- char xbuf[MAXLINE];
-
- if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
- return true;
-
- mci->mci_flags |= MCIF_INHEADER;
-
- if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
- {
- char *bang;
-
- expand("\201g", buf, sizeof(buf), e);
- bang = strchr(buf, '!');
- if (bang == NULL)
- {
- 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);
- at = hname;
- }
- else
- *at++ = '\0';
- (void) sm_snprintf(xbuf, sizeof(xbuf),
- "From %.800s \201d remote from %.100s\n",
- buf, at);
- }
- else
- {
- *bang++ = '\0';
- (void) sm_snprintf(xbuf, sizeof(xbuf),
- "From %.800s \201d remote from %.100s\n",
- bang, buf);
- template = xbuf;
- }
- }
- expand(template, buf, sizeof(buf), e);
- return putxline(buf, strlen(buf), mci, PXLF_HEADER);
-}
-
-/*
-** PUTBODY -- put the body of a message.
-**
-** Parameters:
-** mci -- the connection information.
-** e -- the envelope to put out.
-** separator -- if non-NULL, a message separator that must
-** not be permitted in the resulting message.
-**
-** Returns:
-** true iff message was written successfully
-**
-** Side Effects:
-** The message is written onto fp.
-*/
-
-/* values for output state variable */
-#define OSTATE_HEAD 0 /* at beginning of line */
-#define OSTATE_CR 1 /* read a carriage return */
-#define OSTATE_INLINE 2 /* putting rest of line */
-
-bool
-putbody(mci, e, separator)
- register MCI *mci;
- register ENVELOPE *e;
- char *separator;
-{
- bool dead = false;
- bool ioerr = false;
- int save_errno;
- char buf[MAXLINE];
-#if MIME8TO7
- char *boundaries[MAXMIMENESTING + 1];
-#endif /* MIME8TO7 */
-
- /*
- ** Output the body of the message
- */
-
- if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
- {
- char *df = queuename(e, DATAFL_LETTER);
-
- e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
- SM_IO_RDONLY_B, NULL);
- if (e->e_dfp == NULL)
- {
- 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)
- {
- if (bitset(MCIF_INHEADER, mci->mci_flags))
- {
- if (!putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
- }
- if (!putline("<<< No Message Collected >>>", mci))
- goto writeerr;
- goto endofmessage;
- }
-
- if (e->e_dfino == (ino_t) 0)
- {
- struct stat stbuf;
-
- if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf)
- < 0)
- e->e_dfino = -1;
- else
- {
- e->e_dfdev = stbuf.st_dev;
- e->e_dfino = stbuf.st_ino;
- }
- }
-
- /* paranoia: the data file should always be in a rewound state */
- (void) bfrewind(e->e_dfp);
-
- /* simulate an I/O timeout when used as source */
- if (tTd(84, 101))
- sleep(319);
-
-#if MIME8TO7
- if (bitset(MCIF_CVT8TO7, mci->mci_flags))
- {
- /*
- ** Do 8 to 7 bit MIME conversion.
- */
-
- /* make sure it looks like a MIME message */
- if (hvalue("MIME-Version", e->e_header) == NULL &&
- !putline("MIME-Version: 1.0", mci))
- goto writeerr;
-
- if (hvalue("Content-Type", e->e_header) == NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Content-Type: text/plain; charset=%s",
- defcharset(e));
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- /* now do the hard work */
- boundaries[0] = NULL;
- mci->mci_flags |= MCIF_INHEADER;
- if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER, 0) ==
- SM_IO_EOF)
- goto writeerr;
- }
-# if MIME7TO8
- else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
- {
- if (!mime7to8(mci, e->e_header, e))
- goto writeerr;
- }
-# 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;
-
- /*
- ** 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;
-
- if (mime8to7(mci, e->e_header, e, boundaries,
- M87F_OUTER|M87F_NO8TO7, 0) == SM_IO_EOF)
- goto writeerr;
-
- /* restore SuprErrs */
- SuprErrs = oldsuprerrs;
- }
- else
-#endif /* MIME8TO7 */
- {
- int ostate;
- register char *bp;
- register char *pbp;
- register int c;
- register char *xp;
- int padc;
- char *buflim;
- int pos = 0;
- char peekbuf[12];
-
- if (bitset(MCIF_INHEADER, mci->mci_flags))
- {
- if (!putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
- }
-
- /* determine end of buffer; allow for short mailer lines */
- buflim = &buf[sizeof(buf) - 1];
- if (mci->mci_mailer->m_linelimit > 0 &&
- mci->mci_mailer->m_linelimit < sizeof(buf) - 1)
- buflim = &buf[mci->mci_mailer->m_linelimit - 1];
-
- /* copy temp file to output with mapping */
- ostate = OSTATE_HEAD;
- bp = buf;
- pbp = peekbuf;
- while (!sm_io_error(mci->mci_out) && !dead)
- {
- if (pbp > peekbuf)
- c = *--pbp;
- else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT))
- == SM_IO_EOF)
- break;
- if (bitset(MCIF_7BIT, mci->mci_flags))
- c &= 0x7f;
- switch (ostate)
- {
- case OSTATE_HEAD:
- if (c == '\0' &&
- bitnset(M_NONULLS,
- mci->mci_mailer->m_flags))
- break;
- if (c != '\r' && c != '\n' && bp < buflim)
- {
- *bp++ = c;
- break;
- }
-
- /* check beginning of line for special cases */
- *bp = '\0';
- pos = 0;
- padc = SM_IO_EOF;
- if (buf[0] == 'F' &&
- bitnset(M_ESCFROM, mci->mci_mailer->m_flags)
- && strncmp(buf, "From ", 5) == 0)
- {
- padc = '>';
- }
- if (buf[0] == '-' && buf[1] == '-' &&
- separator != NULL)
- {
- /* possible separator */
- int sl = strlen(separator);
-
- if (strncmp(&buf[2], separator, sl)
- == 0)
- padc = ' ';
- }
- if (buf[0] == '.' &&
- bitnset(M_XDOT, mci->mci_mailer->m_flags))
- {
- padc = '.';
- }
-
- /* now copy out saved line */
- if (TrafficLogFile != NULL)
- {
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT,
- "%05d >>> ",
- (int) CurrentPid);
- if (padc != SM_IO_EOF)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- padc);
- for (xp = buf; xp < bp; xp++)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- (unsigned char) *xp);
- if (c == '\n')
- (void) sm_io_fputs(TrafficLogFile,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol);
- }
- if (padc != SM_IO_EOF)
- {
- if (sm_io_putc(mci->mci_out,
- SM_TIME_DEFAULT, padc)
- == SM_IO_EOF)
- {
- dead = true;
- continue;
- }
- pos++;
- }
- for (xp = buf; xp < bp; xp++)
- {
- if (sm_io_putc(mci->mci_out,
- SM_TIME_DEFAULT,
- (unsigned char) *xp)
- == SM_IO_EOF)
- {
- dead = true;
- break;
- }
- }
- if (dead)
- continue;
- if (c == '\n')
- {
- if (sm_io_fputs(mci->mci_out,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol)
- == SM_IO_EOF)
- break;
- pos = 0;
- }
- else
- {
- pos += bp - buf;
- if (c != '\r')
- {
- SM_ASSERT(pbp < peekbuf +
- sizeof(peekbuf));
- *pbp++ = c;
- }
- }
-
- bp = buf;
-
- /* determine next state */
- if (c == '\n')
- ostate = OSTATE_HEAD;
- else if (c == '\r')
- ostate = OSTATE_CR;
- else
- ostate = OSTATE_INLINE;
- continue;
-
- case OSTATE_CR:
- if (c == '\n')
- {
- /* got CRLF */
- if (sm_io_fputs(mci->mci_out,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol)
- == SM_IO_EOF)
- continue;
-
- if (TrafficLogFile != NULL)
- {
- (void) sm_io_fputs(TrafficLogFile,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol);
- }
- pos = 0;
- ostate = OSTATE_HEAD;
- continue;
- }
-
- /* had a naked carriage return */
- SM_ASSERT(pbp < peekbuf + sizeof(peekbuf));
- *pbp++ = c;
- c = '\r';
- ostate = OSTATE_INLINE;
- goto putch;
-
- case OSTATE_INLINE:
- if (c == '\r')
- {
- ostate = OSTATE_CR;
- continue;
- }
- if (c == '\0' &&
- bitnset(M_NONULLS,
- mci->mci_mailer->m_flags))
- break;
-putch:
- if (mci->mci_mailer->m_linelimit > 0 &&
- pos >= mci->mci_mailer->m_linelimit - 1 &&
- c != '\n')
- {
- int d;
-
- /* check next character for EOL */
- if (pbp > peekbuf)
- d = *(pbp - 1);
- else if ((d = sm_io_getc(e->e_dfp,
- SM_TIME_DEFAULT))
- != SM_IO_EOF)
- {
- SM_ASSERT(pbp < peekbuf +
- sizeof(peekbuf));
- *pbp++ = d;
- }
-
- if (d == '\n' || d == SM_IO_EOF)
- {
- if (TrafficLogFile != NULL)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- (unsigned char) c);
- if (sm_io_putc(mci->mci_out,
- SM_TIME_DEFAULT,
- (unsigned char) c)
- == SM_IO_EOF)
- {
- dead = true;
- continue;
- }
- pos++;
- continue;
- }
-
- if (sm_io_putc(mci->mci_out,
- SM_TIME_DEFAULT, '!')
- == SM_IO_EOF ||
- sm_io_fputs(mci->mci_out,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol)
- == SM_IO_EOF)
- {
- dead = true;
- continue;
- }
-
- if (TrafficLogFile != NULL)
- {
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT,
- "!%s",
- mci->mci_mailer->m_eol);
- }
- ostate = OSTATE_HEAD;
- SM_ASSERT(pbp < peekbuf +
- sizeof(peekbuf));
- *pbp++ = c;
- continue;
- }
- if (c == '\n')
- {
- if (TrafficLogFile != NULL)
- (void) sm_io_fputs(TrafficLogFile,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol);
- if (sm_io_fputs(mci->mci_out,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol)
- == SM_IO_EOF)
- continue;
- pos = 0;
- ostate = OSTATE_HEAD;
- }
- else
- {
- if (TrafficLogFile != NULL)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- (unsigned char) c);
- if (sm_io_putc(mci->mci_out,
- SM_TIME_DEFAULT,
- (unsigned char) c)
- == SM_IO_EOF)
- {
- dead = true;
- continue;
- }
- pos++;
- ostate = OSTATE_INLINE;
- }
- break;
- }
- }
-
- /* make sure we are at the beginning of a line */
- if (bp > buf)
- {
- if (TrafficLogFile != NULL)
- {
- for (xp = buf; xp < bp; xp++)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- (unsigned char) *xp);
- }
- for (xp = buf; xp < bp; xp++)
- {
- if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
- (unsigned char) *xp)
- == SM_IO_EOF)
- {
- dead = true;
- break;
- }
- }
- pos += bp - buf;
- }
- if (!dead && pos > 0)
- {
- if (TrafficLogFile != NULL)
- (void) sm_io_fputs(TrafficLogFile,
- SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol);
- if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol) == SM_IO_EOF)
- goto writeerr;
- }
- }
-
- if (sm_io_error(e->e_dfp))
- {
- syserr("putbody: %s/%cf%s: read error",
- qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
- DATAFL_LETTER, e->e_id);
- ExitStat = EX_IOERR;
- ioerr = true;
- }
-
-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.
- */
-
- save_errno = errno;
- if (e->e_dfp != NULL)
- (void) bfrewind(e->e_dfp);
-
- /* some mailers want extra blank line at end of message */
- if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
- buf[0] != '\0' && buf[0] != '\n')
- {
- if (!putline("", mci))
- goto writeerr;
- }
-
- if (!dead &&
- (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF ||
- (sm_io_error(mci->mci_out) && errno != EPIPE)))
- {
- save_errno = errno;
- syserr("putbody: write error");
- ExitStat = EX_IOERR;
- ioerr = true;
- }
-
- errno = save_errno;
- return !dead && !ioerr;
-
- writeerr:
- return false;
-}
-
-/*
-** MAILFILE -- Send a message to a file.
-**
-** If the file has the set-user-ID/set-group-ID bits set, but NO
-** execute bits, sendmail will try to become the owner of that file
-** rather than the real user. Obviously, this only works if
-** sendmail runs as root.
-**
-** This could be done as a subordinate mailer, except that it
-** is used implicitly to save messages in ~/dead.letter. We
-** view this as being sufficiently important as to include it
-** here. For example, if the system is dying, we shouldn't have
-** to create another process plus some pipes to save the message.
-**
-** Parameters:
-** filename -- the name of the file to send to.
-** mailer -- mailer definition for recipient -- if NULL,
-** use FileMailer.
-** ctladdr -- the controlling address header -- includes
-** the userid/groupid to be when sending.
-** sfflags -- flags for opening.
-** e -- the current envelope.
-**
-** Returns:
-** The exit code associated with the operation.
-**
-** Side Effects:
-** none.
-*/
-
-# define RETURN(st) exit(st);
-
-static jmp_buf CtxMailfileTimeout;
-
-int
-mailfile(filename, mailer, ctladdr, sfflags, e)
- char *volatile filename;
- MAILER *volatile mailer;
- ADDRESS *ctladdr;
- volatile long sfflags;
- register ENVELOPE *e;
-{
- register SM_FILE_T *f;
- register pid_t pid = -1;
- volatile int mode;
- int len;
- off_t curoff;
- bool suidwarn = geteuid() == 0;
- char *p;
- char *volatile realfile;
- SM_EVENT *ev;
- char buf[MAXPATHLEN];
- char targetfile[MAXPATHLEN];
-
- if (tTd(11, 1))
- {
- sm_dprintf("mailfile %s\n ctladdr=", filename);
- printaddr(sm_debug_file(), ctladdr, false);
- }
-
- if (mailer == NULL)
- mailer = FileMailer;
-
- if (e->e_xfp != NULL)
- (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
-
- /*
- ** Special case /dev/null. This allows us to restrict file
- ** delivery to regular files only.
- */
-
- if (sm_path_isdevnull(filename))
- return EX_OK;
-
- /* check for 8-bit available */
- if (bitset(EF_HAS8BIT, e->e_flags) &&
- bitnset(M_7BITS, mailer->m_flags) &&
- (bitset(EF_DONT_MIME, e->e_flags) ||
- !(bitset(MM_MIME8BIT, MimeMode) ||
- (bitset(EF_IS_MIME, e->e_flags) &&
- bitset(MM_CVTMIME, MimeMode)))))
- {
- e->e_status = "5.6.3";
- usrerrenh(e->e_status,
- "554 Cannot send 8-bit data to 7-bit destination");
- errno = 0;
- 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 >= sizeof(targetfile))
- {
- syserr("mailfile: filename too long (%s/%s)",
- SafeFileEnv, filename);
- return EX_CANTCREAT;
- }
- (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof(targetfile));
- realfile = targetfile + len;
- if (*filename == '/')
- filename++;
- if (*filename != '\0')
- {
- /* paranoia: trailing / should be removed in readcf */
- if (targetfile[len - 1] != '/')
- (void) sm_strlcat(targetfile,
- "/", sizeof(targetfile));
- (void) sm_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 >= sizeof(targetfile))
- {
- syserr("mailfile: filename too long (%s/%s)",
- targetfile, filename);
- return EX_CANTCREAT;
- }
- realfile = targetfile + len;
- if (targetfile[len - 1] != '/')
- (void) sm_strlcat(targetfile, "/", sizeof(targetfile));
- if (*filename == '/')
- (void) sm_strlcat(targetfile, filename + 1,
- sizeof(targetfile));
- else
- (void) sm_strlcat(targetfile, filename,
- sizeof(targetfile));
- }
- else
- {
- if (sm_strlcpy(targetfile, filename, sizeof(targetfile)) >=
- sizeof(targetfile))
- {
- syserr("mailfile: filename too long (%s)", filename);
- return EX_CANTCREAT;
- }
- realfile = targetfile;
- }
-
- /*
- ** Fork so we can change permissions here.
- ** Note that we MUST use fork, not vfork, because of
- ** the complications of calling subroutines, etc.
- */
-
-
- /*
- ** Dispose of SIGCHLD signal catchers that may be laying
- ** around so that the waitfor() below will get it.
- */
-
- (void) sm_signal(SIGCHLD, SIG_DFL);
-
- DOFORK(fork);
-
- if (pid < 0)
- return EX_OSERR;
- else if (pid == 0)
- {
- /* child -- actually write to file */
- struct stat stb;
- MCI mcibuf;
- int err;
- volatile int oflags = O_WRONLY|O_APPEND;
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- CurrentPid = getpid();
-
- if (e->e_lockfp != NULL)
- {
- int fd;
-
- fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
- /* SM_ASSERT(fd >= 0); */
- if (fd >= 0)
- (void) close(fd);
- }
-
- (void) sm_signal(SIGINT, SIG_DFL);
- (void) sm_signal(SIGHUP, SIG_DFL);
- (void) sm_signal(SIGTERM, SIG_DFL);
- (void) umask(OldUmask);
- e->e_to = filename;
- ExitStat = EX_OK;
-
- if (setjmp(CtxMailfileTimeout) != 0)
- {
- RETURN(EX_TEMPFAIL);
- }
-
- if (TimeOuts.to_fileopen > 0)
- ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout,
- 0);
- else
- ev = NULL;
-
- /* check file mode to see if set-user-ID */
- if (stat(targetfile, &stb) < 0)
- mode = FileMode;
- else
- mode = stb.st_mode;
-
- /* limit the errors to those actually caused in the child */
- errno = 0;
- ExitStat = EX_OK;
-
- /* 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 set-user-ID and set-group-ID bits */
- mode &= ~(S_ISGID|S_ISUID);
- if (tTd(11, 20))
- sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n");
- }
-
- /* we have to open the data file BEFORE setuid() */
- if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
- {
- char *df = queuename(e, DATAFL_LETTER);
-
- e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df,
- SM_IO_RDONLY_B, NULL);
- if (e->e_dfp == NULL)
- {
- syserr("mailfile: Cannot open %s for %s from %s",
- df, e->e_to, e->e_from.q_paddr);
- }
- }
-
- /* select a new user to run as */
- if (!bitset(SFF_RUNASREALUID, sfflags))
- {
- if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
- {
- RealUserName = NULL;
- if (mailer->m_uid == NO_UID)
- RealUid = RunAsUid;
- else
- RealUid = mailer->m_uid;
- if (RunAsUid != 0 && RealUid != RunAsUid)
- {
- /* Only root can change the uid */
- syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d",
- (int) RunAsUid, (int) RealUid);
- RETURN(EX_TEMPFAIL);
- }
- }
- else if (bitset(S_ISUID, mode))
- {
- RealUserName = NULL;
- RealUid = stb.st_uid;
- }
- else if (ctladdr != NULL && ctladdr->q_uid != 0)
- {
- if (ctladdr->q_ruser != NULL)
- RealUserName = ctladdr->q_ruser;
- else
- RealUserName = ctladdr->q_user;
- RealUid = ctladdr->q_uid;
- }
- else if (mailer != NULL && mailer->m_uid != NO_UID)
- {
- RealUserName = DefUser;
- RealUid = mailer->m_uid;
- }
- else
- {
- RealUserName = DefUser;
- RealUid = DefUid;
- }
-
- /* select a new group to run as */
- if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
- {
- if (mailer->m_gid == NO_GID)
- RealGid = RunAsGid;
- else
- RealGid = mailer->m_gid;
- if (RunAsUid != 0 &&
- (RealGid != getgid() ||
- RealGid != getegid()))
- {
- /* Only root can change the gid */
- syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d",
- (int) RealGid, (int) RunAsUid,
- (int) getgid(), (int) getegid());
- RETURN(EX_TEMPFAIL);
- }
- }
- else if (bitset(S_ISGID, mode))
- RealGid = stb.st_gid;
- else if (ctladdr != NULL &&
- ctladdr->q_uid == DefUid &&
- ctladdr->q_gid == 0)
- {
- /*
- ** Special case: This means it is an
- ** alias and we should act as DefaultUser.
- ** See alias()'s comments.
- */
-
- RealGid = DefGid;
- RealUserName = DefUser;
- }
- else if (ctladdr != NULL && ctladdr->q_uid != 0)
- RealGid = ctladdr->q_gid;
- else if (mailer != NULL && mailer->m_gid != NO_GID)
- RealGid = mailer->m_gid;
- else
- RealGid = DefGid;
- }
-
- /* last ditch */
- if (!bitset(SFF_ROOTOK, sfflags))
- {
- if (RealUid == 0)
- RealUid = DefUid;
- if (RealGid == 0)
- RealGid = DefGid;
- }
-
- /* set group id list (needs /etc/group access) */
- if (RealUserName != NULL && !DontInitGroups)
- {
- if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
- {
- syserr("mailfile: initgroups(%s, %d) failed",
- RealUserName, RealGid);
- RETURN(EX_TEMPFAIL);
- }
- }
- else
- {
- GIDSET_T gidset[1];
-
- gidset[0] = RealGid;
- if (setgroups(1, gidset) == -1 && suidwarn)
- {
- syserr("mailfile: setgroups() failed");
- RETURN(EX_TEMPFAIL);
- }
- }
-
- /*
- ** If you have a safe environment, go into it.
- */
-
- if (realfile != targetfile)
- {
- char save;
-
- save = *realfile;
- *realfile = '\0';
- if (tTd(11, 20))
- sm_dprintf("mailfile: chroot %s\n", targetfile);
- if (chroot(targetfile) < 0)
- {
- syserr("mailfile: Cannot chroot(%s)",
- targetfile);
- RETURN(EX_CANTCREAT);
- }
- *realfile = save;
- }
-
- if (tTd(11, 40))
- sm_dprintf("mailfile: deliver to %s\n", realfile);
-
- if (chdir("/") < 0)
- {
- syserr("mailfile: cannot chdir(/)");
- RETURN(EX_CANTCREAT);
- }
-
- /* now reset the group and user ids */
- endpwent();
- sm_mbdb_terminate();
- if (setgid(RealGid) < 0 && suidwarn)
- {
- syserr("mailfile: setgid(%ld) failed", (long) RealGid);
- RETURN(EX_TEMPFAIL);
- }
- vendor_set_uid(RealUid);
- if (setuid(RealUid) < 0 && suidwarn)
- {
- syserr("mailfile: setuid(%ld) failed", (long) RealUid);
- RETURN(EX_TEMPFAIL);
- }
-
- if (tTd(11, 2))
- sm_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;
-
- for (p = mailer->m_execdir; p != NULL; p = q)
- {
- q = strchr(p, ':');
- if (q != NULL)
- *q = '\0';
- expand(p, buf, sizeof(buf), e);
- if (q != NULL)
- *q++ = ':';
- if (tTd(11, 20))
- sm_dprintf("mailfile: trydir %s\n",
- buf);
- if (buf[0] != '\0' && chdir(buf) >= 0)
- break;
- }
- }
-
- /*
- ** 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 (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
- sfflags |= SFF_NOHLINK;
- sfflags &= ~SFF_OPENASROOT;
- f = safefopen(realfile, oflags, mode, sfflags);
- if (f == NULL)
- {
- if (transienterror(errno))
- {
- usrerr("454 4.3.0 cannot open %s: %s",
- shortenstring(realfile, MAXSHORTSTR),
- sm_errstring(errno));
- RETURN(EX_TEMPFAIL);
- }
- else
- {
- usrerr("554 5.3.0 cannot open %s: %s",
- shortenstring(realfile, MAXSHORTSTR),
- sm_errstring(errno));
- RETURN(EX_CANTCREAT);
- }
- }
- if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
- &stb))
- {
- syserr("554 5.3.0 file changed after open");
- RETURN(EX_CANTCREAT);
- }
- if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0)
- {
- syserr("554 5.3.0 cannot fstat %s",
- sm_errstring(errno));
- RETURN(EX_CANTCREAT);
- }
-
- curoff = stb.st_size;
-
- if (ev != NULL)
- sm_clrevent(ev);
-
- memset(&mcibuf, '\0', sizeof(mcibuf));
- mcibuf.mci_mailer = mailer;
- mcibuf.mci_out = f;
- if (bitnset(M_7BITS, mailer->m_flags))
- mcibuf.mci_flags |= MCIF_7BIT;
-
- /* clear out per-message flags from connection structure */
- mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
-
- if (bitset(EF_HAS8BIT, e->e_flags) &&
- !bitset(EF_DONT_MIME, e->e_flags) &&
- bitnset(M_7BITS, mailer->m_flags))
- mcibuf.mci_flags |= MCIF_CVT8TO7;
-
-#if MIME7TO8
- if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
- !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
- (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
- (sm_strcasecmp(p, "quoted-printable") == 0 ||
- sm_strcasecmp(p, "base64") == 0) &&
- (p = hvalue("Content-Type", e->e_header)) != NULL)
- {
- /* may want to convert 7 -> 8 */
- /* XXX should really parse it here -- and use a class XXX */
- if (sm_strncasecmp(p, "text/plain", 10) == 0 &&
- (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
- mcibuf.mci_flags |= MCIF_CVT7TO8;
- }
-#endif /* MIME7TO8 */
-
- if (!putfromline(&mcibuf, e) ||
- !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) ||
- !(*e->e_putbody)(&mcibuf, e, NULL) ||
- !putline("\n", &mcibuf) ||
- (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
- (SuperSafe != SAFE_NO &&
- fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
- sm_io_error(f)))
- {
- setstat(EX_IOERR);
-#if !NOFTRUNCATE
- (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
- curoff);
-#endif /* !NOFTRUNCATE */
- }
-
- /* reset ISUID & ISGID bits for paranoid systems */
-#if HASFCHMOD
- (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL),
- (MODE_T) mode);
-#else /* HASFCHMOD */
- (void) chmod(filename, (MODE_T) mode);
-#endif /* HASFCHMOD */
- if (sm_io_close(f, SM_TIME_DEFAULT) < 0)
- setstat(EX_IOERR);
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
- (void) setuid(RealUid);
- exit(ExitStat);
- /* NOTREACHED */
- }
- else
- {
- /* parent -- wait for exit status */
- int st;
-
- st = waitfor(pid);
- if (st == -1)
- {
- syserr("mailfile: %s: wait", mailer->m_name);
- return EX_SOFTWARE;
- }
- if (WIFEXITED(st))
- {
- errno = 0;
- return (WEXITSTATUS(st));
- }
- else
- {
- syserr("mailfile: %s: child died on signal %d",
- mailer->m_name, st);
- return EX_UNAVAILABLE;
- }
- /* NOTREACHED */
- }
- return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */
-}
-
-static void
-mailfiletimeout(ignore)
- int ignore;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(CtxMailfileTimeout, 1);
-}
-/*
-** HOSTSIGNATURE -- return the "signature" for a host.
-**
-** 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.
-**
-** Parameters:
-** m -- the mailer describing this host.
-** host -- the host name.
-**
-** Returns:
-** The signature for this host.
-**
-** Side Effects:
-** Can tweak the symbol table.
-*/
-
-#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */
-
-char *
-hostsignature(m, host)
- register MAILER *m;
- char *host;
-{
- register char *p;
- register STAB *s;
- time_t now;
-#if NAMED_BIND
- char sep = ':';
- char prevsep = ':';
- int i;
- int len;
- int nmx;
- int hl;
- char *hp;
- char *endp;
- int oldoptions = _res.options;
- char *mxhosts[MAXMXHOSTS + 1];
- unsigned short mxprefs[MAXMXHOSTS + 1];
-#endif /* NAMED_BIND */
-
- if (tTd(17, 3))
- sm_dprintf("hostsignature(%s)\n", host);
-
- /*
- ** If local delivery (and not remote), just return a constant.
- */
-
- if (bitnset(M_LOCALMAILER, m->m_flags) &&
- strcmp(m->m_mailer, "[IPC]") != 0 &&
- !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0))
- return "localhost";
-
- /* an empty host does not have MX records */
- if (*host == '\0')
- return "_empty_";
-
- /*
- ** Check to see if this uses IPC -- if not, it can't have MX records.
- */
-
- if (strcmp(m->m_mailer, "[IPC]") != 0 ||
- CurEnv->e_sendmode == SM_DEFER)
- {
- /* just an ordinary mailer or deferred mode */
- 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.
- */
-
- now = curtime();
- s = stab(host, ST_HOSTSIG, ST_ENTER);
- if (s->s_hostsig.hs_sig != NULL)
- {
- if (s->s_hostsig.hs_exp >= now)
- {
- if (tTd(17, 3))
- sm_dprintf("hostsignature(): stab(%s) found %s\n", host,
- s->s_hostsig.hs_sig);
- return s->s_hostsig.hs_sig;
- }
-
- /* signature is expired: clear it */
- sm_free(s->s_hostsig.hs_sig);
- s->s_hostsig.hs_sig = NULL;
- }
-
- /* set default TTL */
- s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL;
-
- /*
- ** Not already there or expired -- create a signature.
- */
-
-#if NAMED_BIND
- if (ConfigLevel < 2)
- _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */
-
- for (hp = host; hp != NULL; hp = endp)
- {
-#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))
- {
- /* skip MX lookups */
- nmx = 1;
- mxhosts[0] = hp;
- }
- else
- {
- auto int rcode;
- int ttl;
-
- nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true,
- &ttl);
- if (nmx <= 0)
- {
- int save_errno;
- register MCI *mci;
-
- /* update the connection info for this host */
- save_errno = errno;
- mci = mci_get(hp, m);
- mci->mci_errno = save_errno;
- mci->mci_herrno = h_errno;
- mci->mci_lastuse = now;
- 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))
- sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n",
- nmx, mxhosts[0]);
-
- /*
- ** Set new TTL: we use only one!
- ** We could try to use the minimum instead.
- */
-
- s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
- }
-
- len = 0;
- for (i = 0; i < nmx; i++)
- len += strlen(mxhosts[i]) + 1;
- if (s->s_hostsig.hs_sig != NULL)
- len += strlen(s->s_hostsig.hs_sig) + 1;
- if (len < 0 || len >= MAXHOSTSIGNATURE)
- {
- sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d",
- host, MAXHOSTSIGNATURE, len);
- len = MAXHOSTSIGNATURE;
- }
- p = sm_pmalloc_x(len);
- if (s->s_hostsig.hs_sig != NULL)
- {
- (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len);
- sm_free(s->s_hostsig.hs_sig); /* XXX */
- s->s_hostsig.hs_sig = p;
- hl = strlen(p);
- p += hl;
- *p++ = prevsep;
- len -= hl + 1;
- }
- else
- s->s_hostsig.hs_sig = 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)
- {
- if (mxprefs[i] == mxprefs[i - 1])
- *p++ = ',';
- else
- *p++ = ':';
- len--;
- }
- (void) sm_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++ = sep;
- prevsep = sep;
- }
- makelower(s->s_hostsig.hs_sig);
- if (ConfigLevel < 2)
- _res.options = oldoptions;
-#else /* NAMED_BIND */
- /* not using BIND -- the signature is just the host name */
- /*
- ** 'host' points to storage that will be freed after we are
- ** done processing the current envelope, so we copy it.
- */
- s->s_hostsig.hs_sig = sm_pstrdup_x(host);
-#endif /* NAMED_BIND */
- if (tTd(17, 1))
- sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig);
- return s->s_hostsig.hs_sig;
-}
-/*
-** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array.
-**
-** The signature describes how we are going to send this -- it
-** 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.
-** mailer -- mailer.
-**
-** 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;
-{
- unsigned short curpref = 0;
- int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */
- char *hp, *endp;
- unsigned 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 unsigned 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 STARTTLS
-static SSL_CTX *clt_ctx = NULL;
-static bool tls_ok_clt = true;
-
-/*
-** SETCLTTLS -- client side TLS: allow/disallow.
-**
-** Parameters:
-** tls_ok -- should tls be done?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** sets tls_ok_clt (static variable in this module)
-*/
-
-void
-setclttls(tls_ok)
- bool tls_ok;
-{
- tls_ok_clt = tls_ok;
- return;
-}
-/*
-** INITCLTTLS -- initialize client side TLS
-**
-** Parameters:
-** tls_ok -- should tls initialization be done?
-**
-** Returns:
-** succeeded?
-**
-** Side Effects:
-** sets tls_ok_clt (static variable in this module)
-*/
-
-bool
-initclttls(tls_ok)
- bool tls_ok;
-{
- if (!tls_ok_clt)
- return false;
- tls_ok_clt = tls_ok;
- if (!tls_ok_clt)
- return false;
- if (clt_ctx != NULL)
- return true; /* already done */
- tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile,
- CltKeyFile, CACertPath, CACertFile, DHParams);
- return tls_ok_clt;
-}
-
-/*
-** STARTTLS -- try to start secure connection (client side)
-**
-** Parameters:
-** m -- the mailer.
-** mci -- the mailer connection info.
-** e -- the envelope.
-**
-** Returns:
-** success?
-** (maybe this should be some other code than EX_
-** that denotes which stage failed.)
-*/
-
-static int
-starttls(m, mci, e)
- MAILER *m;
- MCI *mci;
- ENVELOPE *e;
-{
- int smtpresult;
- int result = 0;
- int rfd, wfd;
- SSL *clt_ssl = NULL;
- time_t tlsstart;
-
- if (clt_ctx == NULL && !initclttls(true))
- return EX_TEMPFAIL;
- smtpmessage("STARTTLS", m, mci);
-
- /* get the reply */
- smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL,
- XS_STARTTLS);
-
- /* check return code from server */
- if (REPLYTYPE(smtpresult) == 4)
- return EX_TEMPFAIL;
- if (smtpresult == 501)
- return EX_USAGE;
- if (smtpresult == -1)
- return smtpresult;
-
- /* not an expected reply but we have to deal with it */
- if (REPLYTYPE(smtpresult) == 5)
- return EX_UNAVAILABLE;
- if (smtpresult != 220)
- return EX_PROTOCOL;
-
- if (LogLevel > 13)
- sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok");
-
- /* start connection */
- if ((clt_ssl = SSL_new(clt_ctx)) == NULL)
- {
- if (LogLevel > 5)
- {
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS=client, error: SSL_new failed");
- if (LogLevel > 9)
- tlslogerr("client");
- }
- return EX_SOFTWARE;
- }
-
- rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL);
- wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL);
-
- /* SSL_clear(clt_ssl); ? */
- if (rfd < 0 || wfd < 0 ||
- (result = SSL_set_rfd(clt_ssl, rfd)) != 1 ||
- (result = SSL_set_wfd(clt_ssl, wfd)) != 1)
- {
- if (LogLevel > 5)
- {
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS=client, error: SSL_set_xfd failed=%d",
- result);
- if (LogLevel > 9)
- tlslogerr("client");
- }
- return EX_SOFTWARE;
- }
- SSL_set_connect_state(clt_ssl);
- tlsstart = curtime();
-
-ssl_retry:
- if ((result = SSL_connect(clt_ssl)) <= 0)
- {
- int i, ssl_err;
-
- ssl_err = SSL_get_error(clt_ssl, result);
- i = tls_retry(clt_ssl, rfd, wfd, tlsstart,
- TimeOuts.to_starttls, ssl_err, "client");
- if (i > 0)
- goto ssl_retry;
-
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=client, error: connect failed=%d, SSL_error=%d, errno=%d, retry=%d",
- result, ssl_err, errno, i);
- if (LogLevel > 8)
- tlslogerr("client");
- }
-
- SSL_free(clt_ssl);
- clt_ssl = NULL;
- return EX_SOFTWARE;
- }
- mci->mci_ssl = clt_ssl;
- result = tls_get_info(mci->mci_ssl, false, mci->mci_host,
- &mci->mci_macro, true);
-
- /* switch to use TLS... */
- if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0)
- return EX_OK;
-
- /* 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?
-*/
-
-static 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;
-}
-# endif /* STARTTLS */
-# if STARTTLS || SASL
-/*
-** ISCLTFLGSET -- check whether client flag is set.
-**
-** Parameters:
-** e -- envelope.
-** flag -- flag to check in {client_flags}
-**
-** Returns:
-** true iff flag is set.
-*/
-
-static bool
-iscltflgset(e, flag)
- ENVELOPE *e;
- int flag;
-{
- char *p;
-
- p = macvalue(macid("{client_flags}"), e);
- if (p == NULL)
- return false;
- for (; *p != '\0'; p++)
- {
- /* look for just this one flag */
- if (*p == (char) flag)
- return true;
- }
- return false;
-}
-# endif /* STARTTLS || SASL */
diff --git a/contrib/sendmail/src/domain.c b/contrib/sendmail/src/domain.c
deleted file mode 100644
index 394b0d3f..0000000
--- a/contrib/sendmail/src/domain.c
+++ /dev/null
@@ -1,1166 +0,0 @@
-/*
- * Copyright (c) 1998-2004, 2006 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.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sendmail.h>
-#include "map.h"
-
-#if NAMED_BIND
-SM_RCSID("@(#)$Id: domain.c,v 8.202 2006/12/19 01:15:07 ca Exp $ (with name server)")
-#else /* NAMED_BIND */
-SM_RCSID("@(#)$Id: domain.c,v 8.202 2006/12/19 01:15:07 ca Exp $ (without name server)")
-#endif /* NAMED_BIND */
-
-#if NAMED_BIND
-
-# include <arpa/inet.h>
-
-
-/*
-** The standard udp packet size PACKETSZ (512) is not sufficient for some
-** nameserver answers containing very many resource records. The resolver
-** may switch to tcp and retry if it detects udp packet overflow.
-** Also note that the resolver routines res_query and res_search return
-** the size of the *un*truncated answer in case the supplied answer buffer
-** it not big enough to accommodate the entire answer.
-*/
-
-# ifndef MAXPACKET
-# define MAXPACKET 8192 /* max packet size used internally by BIND */
-# endif /* ! MAXPACKET */
-
-typedef union
-{
- HEADER qb1;
- unsigned char qb2[MAXPACKET];
-} querybuf;
-
-# ifndef MXHOSTBUFSIZE
-# define MXHOSTBUFSIZE (128 * MAXMXHOSTS)
-# endif /* ! MXHOSTBUFSIZE */
-
-static char MXHostBuf[MXHOSTBUFSIZE];
-#if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2)
- ERROR: _MXHOSTBUFSIZE is out of range
-#endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */
-
-# ifndef MAXDNSRCH
-# define MAXDNSRCH 6 /* number of possible domains to search */
-# endif /* ! MAXDNSRCH */
-
-# ifndef RES_DNSRCH_VARIABLE
-# define RES_DNSRCH_VARIABLE _res.dnsrch
-# endif /* ! RES_DNSRCH_VARIABLE */
-
-# ifndef NO_DATA
-# define NO_DATA NO_ADDRESS
-# endif /* ! NO_DATA */
-
-# ifndef HFIXEDSZ
-# define HFIXEDSZ 12 /* sizeof(HEADER) */
-# endif /* ! HFIXEDSZ */
-
-# 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 unsigned char *
-# endif /* defined(__RES) && (__RES >= 19940415) */
-
-static int mxrand __P((char *));
-static int fallbackmxrr __P((int, unsigned short *, char **));
-
-/*
-** GETFALLBACKMXRR -- get MX resource records for fallback MX host.
-**
-** We have to initialize this once before doing anything else.
-** Moreover, we have to repeat this from time to time to avoid
-** stale data, e.g., in persistent queue runners.
-** This should be done in a parent process so the child
-** processes have the right data.
-**
-** Parameters:
-** host -- the name of the fallback MX host.
-**
-** Returns:
-** number of MX records.
-**
-** Side Effects:
-** Populates NumFallbackMXHosts and fbhosts.
-** Sets renewal time (based on TTL).
-*/
-
-int NumFallbackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */
-static char *fbhosts[MAXMXHOSTS + 1];
-
-int
-getfallbackmxrr(host)
- char *host;
-{
- int i, rcode;
- int ttl;
- static time_t renew = 0;
-
-#if 0
- /* This is currently done before this function is called. */
- if (host == NULL || *host == '\0')
- return 0;
-#endif /* 0 */
- if (NumFallbackMXHosts > 0 && renew > curtime())
- return NumFallbackMXHosts;
- if (host[0] == '[')
- {
- fbhosts[0] = host;
- NumFallbackMXHosts = 1;
- }
- else
- {
- /* free old data */
- for (i = 0; i < NumFallbackMXHosts; i++)
- sm_free(fbhosts[i]);
-
- /* get new data */
- NumFallbackMXHosts = getmxrr(host, fbhosts, NULL, false,
- &rcode, false, &ttl);
- renew = curtime() + ttl;
- for (i = 0; i < NumFallbackMXHosts; i++)
- fbhosts[i] = newstr(fbhosts[i]);
- }
- return NumFallbackMXHosts;
-}
-
-/*
-** FALLBACKMXRR -- add MX resource records for fallback MX host to list.
-**
-** Parameters:
-** nmx -- current number of MX records.
-** prefs -- array of preferences.
-** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS)
-**
-** Returns:
-** new number of MX records.
-**
-** Side Effects:
-** If FallbackMX was set, it appends the MX records for
-** that host to mxhosts (and modifies prefs accordingly).
-*/
-
-static int
-fallbackmxrr(nmx, prefs, mxhosts)
- int nmx;
- unsigned short *prefs;
- char **mxhosts;
-{
- int i;
-
- for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++)
- {
- if (nmx > 0)
- prefs[nmx] = prefs[nmx - 1] + 1;
- else
- prefs[nmx] = 0;
- mxhosts[nmx++] = fbhosts[i];
- }
- return nmx;
-}
-
-/*
-** GETMXRR -- get MX resource records for a domain
-**
-** Parameters:
-** 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.
-** rcode -- a pointer to an EX_ status code.
-** tryfallback -- add also fallback MX host?
-** pttl -- pointer to return TTL (can be NULL).
-**
-** Returns:
-** The number of MX records found.
-** -1 if there is an internal failure.
-** If no MX records are found, mxhosts[0] is set to host
-** and 1 is returned.
-**
-** Side Effects:
-** The entries made for mxhosts point to a static array
-** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied,
-** if it must be preserved across calls to this function.
-*/
-
-int
-getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode, tryfallback, pttl)
- char *host;
- char **mxhosts;
- unsigned short *mxprefs;
- bool droplocalhost;
- int *rcode;
- bool tryfallback;
- int *pttl;
-{
- register unsigned char *eom, *cp;
- register int i, j, n;
- int nmx = 0;
- register char *bp;
- HEADER *hp;
- querybuf answer;
- int ancount, qdcount, buflen;
- bool seenlocal = false;
- unsigned short pref, type;
- unsigned short localpref = 256;
- char *fallbackMX = FallbackMX;
- bool trycanon = false;
- unsigned short *prefs;
- int (*resfunc) __P((const char *, int, int, u_char *, int));
- unsigned short prefer[MAXMXHOSTS];
- int weight[MAXMXHOSTS];
- int ttl = 0;
- extern int res_query(), res_search();
-
- if (tTd(8, 2))
- sm_dprintf("getmxrr(%s, droplocalhost=%d)\n",
- host, droplocalhost);
- *rcode = EX_OK;
- if (pttl != NULL)
- *pttl = SM_DEFAULT_TTL;
- if (*host == '\0')
- return 0;
-
- if ((fallbackMX != NULL && droplocalhost &&
- wordinclass(fallbackMX, 'w')) || !tryfallback)
- {
- /* don't use fallback for this pass */
- fallbackMX = NULL;
- }
-
- if (mxprefs != NULL)
- prefs = mxprefs;
- else
- prefs = prefer;
-
- /* efficiency hack -- numeric or non-MX lookups */
- if (host[0] == '[')
- goto punt;
-
- /*
- ** If we don't have MX records in our host switch, don't
- ** try for MX records. Note that this really isn't "right",
- ** since we might be set up to try NIS first and then DNS;
- ** if the host is found in NIS we really shouldn't be doing
- ** MX lookups. However, that should be a degenerate case.
- */
-
- if (!UseNameServer)
- goto punt;
- if (HasWildcardMX && ConfigLevel >= 6)
- resfunc = res_query;
- else
- resfunc = res_search;
-
- errno = 0;
- n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer,
- sizeof(answer));
- if (n < 0)
- {
- if (tTd(8, 1))
- sm_dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
- host, errno, h_errno);
- switch (h_errno)
- {
- case NO_DATA:
- trycanon = true;
- /* FALLTHROUGH */
-
- case NO_RECOVERY:
- /* no MX data on this host */
- goto punt;
-
- case HOST_NOT_FOUND:
-# if BROKEN_RES_SEARCH
- case 0: /* Ultrix resolver retns failure w/ h_errno=0 */
-# endif /* BROKEN_RES_SEARCH */
- /* host doesn't exist in DNS; might be in /etc/hosts */
- trycanon = true;
- *rcode = EX_NOHOST;
- goto punt;
-
- case TRY_AGAIN:
- case -1:
- /* couldn't connect to the name server */
- if (fallbackMX != NULL)
- {
- /* name server is hosed -- push to fallback */
- return fallbackmxrr(nmx, prefs, mxhosts);
- }
- /* it might come up later; better queue it up */
- *rcode = EX_TEMPFAIL;
- break;
-
- default:
- syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)",
- host, h_errno);
- *rcode = EX_OSERR;
- break;
- }
-
- /* irreconcilable differences */
- return -1;
- }
-
- /* avoid problems after truncation in tcp packets */
- if (n > sizeof(answer))
- n = sizeof(answer);
-
- /* find first satisfactory answer */
- hp = (HEADER *)&answer;
- cp = (unsigned char *)&answer + HFIXEDSZ;
- eom = (unsigned char *)&answer + n;
- for (qdcount = ntohs((unsigned short) hp->qdcount);
- qdcount--;
- cp += n + QFIXEDSZ)
- {
- if ((n = dn_skipname(cp, eom)) < 0)
- goto punt;
- }
-
- /* NOTE: see definition of MXHostBuf! */
- buflen = sizeof(MXHostBuf) - 1;
- SM_ASSERT(buflen > 0);
- bp = MXHostBuf;
- ancount = ntohs((unsigned short) hp->ancount);
-
- /* See RFC 1035 for layout of RRs. */
- /* XXX leave room for FallbackMX ? */
- while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
- {
- if ((n = dn_expand((unsigned char *)&answer, eom, cp,
- (RES_UNC_T) bp, buflen)) < 0)
- break;
- cp += n;
- GETSHORT(type, cp);
- cp += INT16SZ; /* skip over class */
- GETLONG(ttl, cp);
- GETSHORT(n, cp); /* rdlength */
- if (type != T_MX)
- {
- if (tTd(8, 8) || _res.options & RES_DEBUG)
- sm_dprintf("unexpected answer type %d, size %d\n",
- type, n);
- cp += n;
- continue;
- }
- GETSHORT(pref, cp);
- if ((n = dn_expand((unsigned char *)&answer, eom, cp,
- (RES_UNC_T) bp, buflen)) < 0)
- break;
- cp += n;
- n = strlen(bp);
-# if 0
- /* Can this happen? */
- if (n == 0)
- {
- if (LogLevel > 4)
- sm_syslog(LOG_ERR, NOQID,
- "MX records for %s contain empty string",
- host);
- continue;
- }
-# endif /* 0 */
- if (wordinclass(bp, 'w'))
- {
- if (tTd(8, 3))
- sm_dprintf("found localhost (%s) in MX list, pref=%d\n",
- bp, pref);
- if (droplocalhost)
- {
- if (!seenlocal || pref < localpref)
- localpref = pref;
- seenlocal = true;
- continue;
- }
- weight[nmx] = 0;
- }
- else
- weight[nmx] = mxrand(bp);
- prefs[nmx] = pref;
- mxhosts[nmx++] = bp;
- bp += n;
- if (bp[-1] != '.')
- {
- *bp++ = '.';
- n++;
- }
- *bp++ = '\0';
- if (buflen < n + 1)
- {
- /* don't want to wrap buflen */
- break;
- }
- buflen -= n + 1;
- }
-
- /* return only one TTL entry, that should be sufficient */
- if (ttl > 0 && pttl != NULL)
- *pttl = ttl;
-
- /* sort the records */
- for (i = 0; i < nmx; i++)
- {
- for (j = i + 1; j < nmx; j++)
- {
- if (prefs[i] > prefs[j] ||
- (prefs[i] == prefs[j] && weight[i] > weight[j]))
- {
- register int temp;
- register char *temp1;
-
- temp = prefs[i];
- prefs[i] = prefs[j];
- prefs[j] = temp;
- temp1 = mxhosts[i];
- mxhosts[i] = mxhosts[j];
- mxhosts[j] = temp1;
- temp = weight[i];
- weight[i] = weight[j];
- weight[j] = temp;
- }
- }
- if (seenlocal && prefs[i] >= localpref)
- {
- /* truncate higher preference part of list */
- nmx = i;
- }
- }
-
- /* delete duplicates from list (yes, some bozos have duplicates) */
- for (i = 0; i < nmx - 1; )
- {
- if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0)
- i++;
- else
- {
- /* compress out duplicate */
- for (j = i + 1; j < nmx; j++)
- {
- mxhosts[j] = mxhosts[j + 1];
- prefs[j] = prefs[j + 1];
- }
- nmx--;
- }
- }
-
- if (nmx == 0)
- {
-punt:
- 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
- ** has an MX, and this should have been caught
- ** earlier in the config file.
- **
- ** Some sites prefer to go ahead and try the
- ** A record anyway; that case is handled by
- ** setting TryNullMXList. I believe this is a
- ** bad idea, but it's up to you....
- */
-
- if (TryNullMXList)
- {
- SM_SET_H_ERRNO(0);
- errno = 0;
- h = sm_gethostbyname(host, AF_INET);
- if (h == NULL)
- {
- if (errno == ETIMEDOUT ||
- h_errno == TRY_AGAIN ||
- (errno == ECONNREFUSED &&
- UseNameServer))
- {
- *rcode = EX_TEMPFAIL;
- return -1;
- }
-# if NETINET6
- SM_SET_H_ERRNO(0);
- errno = 0;
- h = sm_gethostbyname(host, AF_INET6);
- if (h == NULL &&
- (errno == ETIMEDOUT ||
- h_errno == TRY_AGAIN ||
- (errno == ECONNREFUSED &&
- UseNameServer)))
- {
- *rcode = EX_TEMPFAIL;
- return -1;
- }
-# endif /* NETINET6 */
- }
- }
-
- if (h == NULL)
- {
- *rcode = EX_CONFIG;
- syserr("MX list for %s points back to %s",
- host, MyHostName);
- return -1;
- }
-# if NETINET6
- freehostent(h);
- h = NULL;
-# endif /* NETINET6 */
- }
- if (strlen(host) >= sizeof(MXHostBuf))
- {
- *rcode = EX_CONFIG;
- syserr("Host name %s too long",
- shortenstring(host, MAXSHORTSTR));
- return -1;
- }
- (void) sm_strlcpy(MXHostBuf, host, sizeof(MXHostBuf));
- 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 (anynet_pton(AF_INET6, &MXHostBuf[1],
- &tmp6.sin6_addr) == 1)
- {
- nmx++;
- *p = ']';
- }
-# endif /* NETINET6 */
- else
- {
- trycanon = true;
- mxhosts[0]++;
- }
- }
- }
- if (trycanon &&
- getcanonname(mxhosts[0], sizeof(MXHostBuf) - 2, false, pttl))
- {
- /* XXX MXHostBuf == "" ? is that possible? */
- bp = &MXHostBuf[strlen(MXHostBuf)];
- if (bp[-1] != '.')
- {
- *bp++ = '.';
- *bp = '\0';
- }
- nmx = 1;
- }
- }
-
- /* if we have a default lowest preference, include that */
- if (fallbackMX != NULL && !seenlocal)
- {
- nmx = fallbackmxrr(nmx, prefs, mxhosts);
- }
- return nmx;
-}
-/*
-** MXRAND -- create a randomizer for equal MX preferences
-**
-** If two MX hosts have equal preferences we want to randomize
-** the selection. But in order for signatures to be the same,
-** we need to randomize the same way each time. This function
-** computes a pseudo-random hash function from the host name.
-**
-** Parameters:
-** host -- the name of the host.
-**
-** Returns:
-** A random but repeatable value based on the host name.
-*/
-
-static int
-mxrand(host)
- register char *host;
-{
- int hfunc;
- static unsigned int seed;
-
- if (seed == 0)
- {
- seed = (int) curtime() & 0xffff;
- if (seed == 0)
- seed++;
- }
-
- if (tTd(17, 9))
- sm_dprintf("mxrand(%s)", host);
-
- hfunc = seed;
- while (*host != '\0')
- {
- int c = *host++;
-
- if (isascii(c) && isupper(c))
- c = tolower(c);
- hfunc = ((hfunc << 1) ^ c) % 2003;
- }
-
- hfunc &= 0xff;
- hfunc++;
-
- if (tTd(17, 9))
- sm_dprintf(" = %d\n", hfunc);
- return hfunc;
-}
-/*
-** BESTMX -- find the best MX for a name
-**
-** This is really a hack, but I don't see any obvious way
-** to generalize it at the moment.
-*/
-
-/* ARGSUSED3 */
-char *
-bestmx_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- int nmx;
- int saveopts = _res.options;
- int i;
- ssize_t len = 0;
- char *result;
- char *mxhosts[MAXMXHOSTS + 1];
-#if _FFR_BESTMX_BETTER_TRUNCATION
- char *buf;
-#else /* _FFR_BESTMX_BETTER_TRUNCATION */
- char *p;
- char buf[PSBUFSIZE / 2];
-#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
-
- _res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
- nmx = getmxrr(name, mxhosts, NULL, false, statp, false, NULL);
- _res.options = saveopts;
- if (nmx <= 0)
- return NULL;
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, name, strlen(name), NULL);
- if ((map->map_coldelim == '\0') || (nmx == 1))
- return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
-
- /*
- ** We were given a -z flag (return all MXs) and there are multiple
- ** ones. We need to build them all into a list.
- */
-
-#if _FFR_BESTMX_BETTER_TRUNCATION
- for (i = 0; i < nmx; i++)
- {
- if (strchr(mxhosts[i], map->map_coldelim) != NULL)
- {
- syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
- mxhosts[i], map->map_coldelim);
- return NULL;
- }
- len += strlen(mxhosts[i]) + 1;
- if (len < 0)
- {
- len -= strlen(mxhosts[i]) + 1;
- break;
- }
- }
- buf = (char *) sm_malloc(len);
- if (buf == NULL)
- {
- *statp = EX_UNAVAILABLE;
- return NULL;
- }
- *buf = '\0';
- for (i = 0; i < nmx; i++)
- {
- int end;
-
- end = sm_strlcat(buf, mxhosts[i], len);
- if (i != nmx && end + 1 < len)
- {
- buf[end] = map->map_coldelim;
- buf[end + 1] = '\0';
- }
- }
-
- /* Cleanly truncate for rulesets */
- truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim);
-#else /* _FFR_BESTMX_BETTER_TRUNCATION */
- p = buf;
- for (i = 0; i < nmx; i++)
- {
- size_t slen;
-
- if (strchr(mxhosts[i], map->map_coldelim) != NULL)
- {
- syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
- mxhosts[i], map->map_coldelim);
- return NULL;
- }
- slen = strlen(mxhosts[i]);
- if (len + slen + 2 > sizeof(buf))
- break;
- if (i > 0)
- {
- *p++ = map->map_coldelim;
- len++;
- }
- (void) sm_strlcpy(p, mxhosts[i], sizeof(buf) - len);
- p += slen;
- len += slen;
- }
-#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
-
- result = map_rewrite(map, buf, len, av);
-#if _FFR_BESTMX_BETTER_TRUNCATION
- sm_free(buf);
-#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
- return result;
-}
-/*
-** DNS_GETCANONNAME -- get the canonical name for named host using DNS
-**
-** This algorithm tries to be smart about wildcard MX records.
-** 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.
-**
-** If we match an MX in one pass and lose it in the next, we use
-** the old one. For example, consider an MX matching *.FOO.BAR.COM.
-** A hostname bletch.foo.bar.com will match against this MX, but
-** will stop matching when we try bletch.bar.com -- so we know
-** that bletch.foo.bar.com must have been right. This fails if
-** there was also an MX record matching *.BAR.COM, but there are
-** some things that just can't be fixed.
-**
-** Parameters:
-** host -- a buffer containing the name of the host.
-** This is a value-result parameter.
-** hbsize -- the size of the host buffer.
-** trymx -- if set, try MX records as well as A and CNAME.
-** statp -- pointer to place to store status.
-** pttl -- pointer to return TTL (can be NULL).
-**
-** Returns:
-** true -- if the host matched.
-** false -- otherwise.
-*/
-
-bool
-dns_getcanonname(host, hbsize, trymx, statp, pttl)
- char *host;
- int hbsize;
- bool trymx;
- int *statp;
- int *pttl;
-{
- register unsigned char *eom, *ap;
- register char *cp;
- register int n;
- HEADER *hp;
- querybuf answer;
- int ancount, qdcount;
- int ret;
- char **domain;
- int type;
- int ttl = 0;
- char **dp;
- char *mxmatch;
- bool amatch;
- bool gotmx = false;
- int qtype;
- int initial;
- int loopcnt;
- char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)];
- char *searchlist[MAXDNSRCH + 2];
-
- if (tTd(8, 2))
- sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
-
- if ((_res.options & RES_INIT) == 0 && res_init() == -1)
- {
- *statp = EX_UNAVAILABLE;
- return false;
- }
-
- *statp = EX_OK;
-
- /*
- ** Initialize domain search list. If there is at least one
- ** dot in the name, search the unmodified name first so we
- ** find "vse.CS" in Czechoslovakia instead of in the local
- ** 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.
- */
-
- loopcnt = 0;
-cnameloop:
- /* Check for dots in the name */
- for (cp = host, n = 0; *cp != '\0'; cp++)
- if (*cp == '.')
- n++;
-
- /*
- ** 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
- ** tree desired, append each local domain component to the
- ** search list; if name contains no dots and default domain
- ** name is desired, append default domain name to search list;
- ** else if name ends in a dot, remove that dot.
- */
-
- dp = searchlist;
- if (n > 0)
- *dp++ = "";
- if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
- {
- /* 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))
- {
- *dp++ = _res.defdname;
- }
- else if (*cp == '.')
- {
- *cp = '\0';
- }
- *dp = NULL;
-
- /*
- ** Now loop through the search list, appending each domain in turn
- ** name and searching for a match.
- */
-
- mxmatch = NULL;
- initial = T_A;
-# if NETINET6
- if (InetMode == AF_INET6)
- initial = T_AAAA;
-# endif /* NETINET6 */
- qtype = initial;
-
- for (dp = searchlist; *dp != NULL; )
- {
- if (qtype == initial)
- gotmx = false;
- if (tTd(8, 5))
- sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n",
- host, *dp,
-# if NETINET6
- qtype == T_AAAA ? "AAAA" :
-# endif /* NETINET6 */
- qtype == T_A ? "A" :
- qtype == T_MX ? "MX" :
- "???");
- errno = 0;
- ret = res_querydomain(host, *dp, C_IN, qtype,
- answer.qb2, sizeof(answer.qb2));
- if (ret <= 0)
- {
- int save_errno = errno;
-
- if (tTd(8, 7))
- sm_dprintf("\tNO: errno=%d, h_errno=%d\n",
- save_errno, h_errno);
-
- if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN)
- {
- /*
- ** the name server seems to be down or broken.
- */
-
- SM_SET_H_ERRNO(TRY_AGAIN);
- if (**dp == '\0')
- {
- if (*statp == EX_OK)
- *statp = EX_TEMPFAIL;
- goto nexttype;
- }
- *statp = EX_TEMPFAIL;
-
- if (WorkAroundBrokenAAAA)
- {
- /*
- ** Only return if not TRY_AGAIN as an
- ** attempt with a different qtype may
- ** succeed (res_querydomain() calls
- ** res_query() calls res_send() which
- ** sets errno to ETIMEDOUT if the
- ** nameservers could be contacted but
- ** didn't give an answer).
- */
-
- if (save_errno != ETIMEDOUT)
- return false;
- }
- else
- return false;
- }
-
-nexttype:
- if (h_errno != HOST_NOT_FOUND)
- {
- /* might have another type of interest */
-# if NETINET6
- if (qtype == T_AAAA)
- {
- qtype = T_A;
- continue;
- }
- else
-# endif /* NETINET6 */
- if (qtype == T_A && !gotmx &&
- (trymx || **dp == '\0'))
- {
- qtype = T_MX;
- continue;
- }
- }
-
- /* definite no -- try the next domain */
- dp++;
- qtype = initial;
- continue;
- }
- else if (tTd(8, 7))
- sm_dprintf("\tYES\n");
-
- /* avoid problems after truncation in tcp packets */
- if (ret > sizeof(answer))
- ret = sizeof(answer);
- SM_ASSERT(ret >= 0);
-
- /*
- ** Appear to have a match. Confirm it by searching for A or
- ** CNAME records. If we don't have a local domain
- ** wild card MX record, we will accept MX as well.
- */
-
- hp = (HEADER *) &answer;
- ap = (unsigned char *) &answer + HFIXEDSZ;
- eom = (unsigned char *) &answer + ret;
-
- /* skip question part of response -- we know what we asked */
- for (qdcount = ntohs((unsigned short) hp->qdcount);
- qdcount--;
- ap += ret + QFIXEDSZ)
- {
- if ((ret = dn_skipname(ap, eom)) < 0)
- {
- if (tTd(8, 20))
- sm_dprintf("qdcount failure (%d)\n",
- ntohs((unsigned short) hp->qdcount));
- *statp = EX_SOFTWARE;
- return false; /* ???XXX??? */
- }
- }
-
- amatch = false;
- for (ancount = ntohs((unsigned short) hp->ancount);
- --ancount >= 0 && ap < eom;
- ap += n)
- {
- n = dn_expand((unsigned char *) &answer, eom, ap,
- (RES_UNC_T) nbuf, sizeof(nbuf));
- if (n < 0)
- break;
- ap += n;
- GETSHORT(type, ap);
- ap += INT16SZ; /* skip over class */
- GETLONG(ttl, ap);
- GETSHORT(n, ap); /* rdlength */
- switch (type)
- {
- case T_MX:
- gotmx = true;
- if (**dp != '\0' && HasWildcardMX)
- {
- /*
- ** If we are using MX matches and have
- ** not yet gotten one, save this one
- ** but keep searching for an A or
- ** CNAME match.
- */
-
- if (trymx && mxmatch == NULL)
- mxmatch = *dp;
- continue;
- }
-
- /*
- ** If we did not append a domain name, this
- ** must have been a canonical name to start
- ** with. Even if we did append a domain name,
- ** in the absence of a wildcard MX this must
- ** still be a real MX match.
- ** Such MX matches are as good as an A match,
- ** fall through.
- */
- /* FALLTHROUGH */
-
-# if NETINET6
- case T_AAAA:
-# endif /* NETINET6 */
- case T_A:
- /* Flag that a good match was found */
- amatch = true;
-
- /* continue in case a CNAME also exists */
- continue;
-
- case T_CNAME:
- if (DontExpandCnames)
- {
- /* got CNAME -- guaranteed canonical */
- amatch = true;
- break;
- }
-
- if (loopcnt++ > MAXCNAMEDEPTH)
- {
- /*XXX should notify postmaster XXX*/
- message("DNS failure: CNAME loop for %s",
- host);
- if (CurEnv->e_message == NULL)
- {
- char ebuf[MAXLINE];
-
- (void) sm_snprintf(ebuf,
- sizeof(ebuf),
- "Deferred: DNS failure: CNAME loop for %.100s",
- host);
- CurEnv->e_message =
- sm_rpool_strdup_x(
- CurEnv->e_rpool, ebuf);
- }
- SM_SET_H_ERRNO(NO_RECOVERY);
- *statp = EX_CONFIG;
- return false;
- }
-
- /* value points at name */
- if ((ret = dn_expand((unsigned char *)&answer,
- eom, ap, (RES_UNC_T) nbuf,
- sizeof(nbuf))) < 0)
- break;
- (void) sm_strlcpy(host, nbuf, hbsize);
-
- /*
- ** RFC 1034 section 3.6 specifies that CNAME
- ** should point at the canonical name -- but
- ** urges software to try again anyway.
- */
-
- goto cnameloop;
-
- default:
- /* not a record of interest */
- continue;
- }
- }
-
- if (amatch)
- {
- /*
- ** Got a good match -- either an A, CNAME, or an
- ** exact MX record. Save it and get out of here.
- */
-
- mxmatch = *dp;
- break;
- }
-
- /*
- ** Nothing definitive yet.
- ** If this was a T_A query and we haven't yet found a MX
- ** match, try T_MX if allowed to do so.
- ** Otherwise, try the next domain.
- */
-
-# if NETINET6
- if (qtype == T_AAAA)
- qtype = T_A;
- else
-# endif /* NETINET6 */
- if (qtype == T_A && !gotmx && (trymx || **dp == '\0'))
- qtype = T_MX;
- else
- {
- qtype = initial;
- dp++;
- }
- }
-
- /* if nothing was found, we are done */
- if (mxmatch == NULL)
- {
- if (*statp == EX_OK)
- *statp = EX_NOHOST;
- return false;
- }
-
- /*
- ** Create canonical name and return.
- ** If saved domain name is null, name was already canonical.
- ** Otherwise append the saved domain name.
- */
-
- (void) sm_snprintf(nbuf, sizeof(nbuf), "%.*s%s%.*s", MAXDNAME, host,
- *mxmatch == '\0' ? "" : ".",
- MAXDNAME, mxmatch);
- (void) sm_strlcpy(host, nbuf, hbsize);
- if (tTd(8, 5))
- sm_dprintf("dns_getcanonname: %s\n", host);
- *statp = EX_OK;
-
- /* return only one TTL entry, that should be sufficient */
- if (ttl > 0 && pttl != NULL)
- *pttl = ttl;
- return true;
-}
-#endif /* NAMED_BIND */
diff --git a/contrib/sendmail/src/envelope.c b/contrib/sendmail/src/envelope.c
deleted file mode 100644
index 20b0ba2..0000000
--- a/contrib/sendmail/src/envelope.c
+++ /dev/null
@@ -1,1293 +0,0 @@
-/*
- * Copyright (c) 1998-2003, 2006 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: envelope.c,v 8.304 2007/04/18 17:15:49 ca Exp $")
-
-/*
-** CLRSESSENVELOPE -- clear session oriented data in an envelope
-**
-** Parameters:
-** e -- the envelope to clear.
-**
-** Returns:
-** none.
-*/
-
-void
-clrsessenvelope(e)
- ENVELOPE *e;
-{
-#if SASL
- macdefine(&e->e_macro, A_PERM, macid("{auth_type}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{auth_authen}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{auth_author}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{auth_ssf}"), "");
-#endif /* SASL */
-#if STARTTLS
- macdefine(&e->e_macro, A_PERM, macid("{cert_issuer}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{cert_subject}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{cipher_bits}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{cipher}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{tls_version}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{verify}"), "");
-# if _FFR_TLS_1
- macdefine(&e->e_macro, A_PERM, macid("{alg_bits}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{cn_issuer}"), "");
- macdefine(&e->e_macro, A_PERM, macid("{cn_subject}"), "");
-# endif /* _FFR_TLS_1 */
-#endif /* STARTTLS */
-}
-
-/*
-** NEWENVELOPE -- fill in a new envelope
-**
-** Supports inheritance.
-**
-** Parameters:
-** e -- the new envelope to fill in.
-** parent -- the envelope to be the parent of e.
-** rpool -- either NULL, or a pointer to a resource pool
-** from which envelope memory is allocated, and
-** to which envelope resources are attached.
-**
-** Returns:
-** e.
-**
-** Side Effects:
-** none.
-*/
-
-ENVELOPE *
-newenvelope(e, parent, rpool)
- register ENVELOPE *e;
- register ENVELOPE *parent;
- SM_RPOOL_T *rpool;
-{
- int sendmode, dm;
-
- /*
- ** This code used to read:
- ** if (e == parent && e->e_parent != NULL)
- ** parent = e->e_parent;
- ** So if e == parent && e->e_parent == NULL then we would
- ** set e->e_parent = e, which creates a loop in the e_parent chain.
- ** This meant macvalue() could go into an infinite loop.
- */
-
- dm = DM_NOTSET;
- if (parent != NULL)
- {
- char *str;
-
- sendmode = parent->e_sendmode;
- str = macvalue(macid("{deliveryMode}"), parent);
- if (str != NULL)
- dm = (int) str[0];
- }
- else
- sendmode = DM_NOTSET;
-
- if (e == parent)
- parent = e->e_parent;
- clearenvelope(e, true, rpool);
- if (e == CurEnv)
- memmove((char *) &e->e_from,
- (char *) &NullAddress,
- sizeof(e->e_from));
- else
- 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 _FFR_SESSID
- e->e_sessid = e->e_id;
-#endif /* _FFR_SESSID */
- if (parent != NULL)
- {
- e->e_msgpriority = parent->e_msgsize;
-#if _FFR_SESSID
- if (parent->e_sessid != NULL)
- e->e_sessid = sm_rpool_strdup_x(rpool,
- parent->e_sessid);
-#endif /* _FFR_SESSID */
-
- if (parent->e_quarmsg == NULL)
- {
- e->e_quarmsg = NULL;
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), "");
- }
- else
- {
- e->e_quarmsg = sm_rpool_strdup_x(rpool,
- parent->e_quarmsg);
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), e->e_quarmsg);
- }
- }
- e->e_puthdr = putheader;
- e->e_putbody = putbody;
- if (CurEnv->e_xfp != NULL)
- (void) sm_io_flush(CurEnv->e_xfp, SM_TIME_DEFAULT);
- if (sendmode != DM_NOTSET)
- e->e_sendmode = sendmode;
- if (dm != DM_NOTSET)
- set_delivery_mode(dm, e);
-
- return e;
-}
-
-/* values for msg_timeout, see also IS_* below for usage (bit layout) */
-#define MSG_T_O 0x01 /* normal timeout */
-#define MSG_T_O_NOW 0x02 /* NOW timeout */
-#define MSG_NOT_BY 0x04 /* Deliver-By time exceeded, mode R */
-#define MSG_WARN 0x10 /* normal queue warning */
-#define MSG_WARN_BY 0x20 /* Deliver-By time exceeded, mode N */
-
-#define IS_MSG_ERR(x) (((x) & 0x0f) != 0) /* return an error */
-
-/* immediate return */
-#define IS_IMM_RET(x) (((x) & (MSG_T_O_NOW|MSG_NOT_BY)) != 0)
-#define IS_MSG_WARN(x) (((x) & 0xf0) != 0) /* return a warning */
-
-/*
-** DROPENVELOPE -- deallocate an envelope.
-**
-** Parameters:
-** e -- the envelope to deallocate.
-** fulldrop -- if set, do return receipts.
-** split -- if true, split by recipient if message is queued up
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** housekeeping necessary to dispose of an envelope.
-** Unlocks this queue file.
-*/
-
-void
-dropenvelope(e, fulldrop, split)
- register ENVELOPE *e;
- bool fulldrop;
- bool split;
-{
- bool panic = false;
- bool queueit = false;
- int msg_timeout = 0;
- bool failure_return = false;
- bool delay_return = false;
- bool success_return = false;
- bool pmnotify = bitset(EF_PM_NOTIFY, e->e_flags);
- bool done = false;
- register ADDRESS *q;
- char *id = e->e_id;
- time_t now;
- char buf[MAXLINE];
-
- if (tTd(50, 1))
- {
- sm_dprintf("dropenvelope %p: id=", e);
- xputs(sm_debug_file(), e->e_id);
- sm_dprintf(", flags=");
- printenvflags(e);
- if (tTd(50, 10))
- {
- sm_dprintf("sendq=");
- printaddr(sm_debug_file(), e->e_sendqueue, true);
- }
- }
-
- if (LogLevel > 84)
- sm_syslog(LOG_DEBUG, id,
- "dropenvelope, e_flags=0x%lx, OpMode=%c, pid=%d",
- e->e_flags, OpMode, (int) CurrentPid);
-
- /* we must have an id to remove disk files */
- if (id == NULL)
- return;
-
- /* if verify-only mode, we can skip most of this */
- if (OpMode == MD_VERIFY)
- goto simpledrop;
-
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- /* post statistics */
- poststats(StatFile);
-
- /*
- ** Extract state information from dregs of send list.
- */
-
- now = curtime();
- if (now >= e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass])
- msg_timeout = MSG_T_O;
- if (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
- now >= e->e_ctime + e->e_deliver_by &&
- !bitset(EF_RESPONSE, e->e_flags))
- {
- msg_timeout = MSG_NOT_BY;
- e->e_flags |= EF_FATALERRS|EF_CLRQUEUE;
- }
- else if (TimeOuts.to_q_return[e->e_timeoutclass] == NOW &&
- !bitset(EF_RESPONSE, e->e_flags))
- {
- msg_timeout = MSG_T_O_NOW;
- e->e_flags |= EF_FATALERRS|EF_CLRQUEUE;
- }
-
- e->e_flags &= ~EF_QUEUERUN;
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_UNDELIVERED(q->q_state))
- queueit = true;
-
- /* see if a notification is needed */
- if (bitset(QPINGONFAILURE, q->q_flags) &&
- ((IS_MSG_ERR(msg_timeout) &&
- QS_IS_UNDELIVERED(q->q_state)) ||
- QS_IS_BADADDR(q->q_state) ||
- IS_IMM_RET(msg_timeout)))
- {
- failure_return = true;
- if (!done && q->q_owner == NULL &&
- !emptyaddr(&e->e_from))
- {
- (void) sendtolist(e->e_from.q_paddr, NULLADDR,
- &e->e_errorqueue, 0, e);
- done = true;
- }
- }
- else if ((bitset(QPINGONSUCCESS, q->q_flags) &&
- ((QS_IS_SENT(q->q_state) &&
- bitnset(M_LOCALMAILER, q->q_mailer->m_flags)) ||
- bitset(QRELAYED|QEXPANDED|QDELIVERED, q->q_flags))) ||
- bitset(QBYTRACE, q->q_flags) ||
- bitset(QBYNRELAY, q->q_flags))
- {
- success_return = true;
- }
- }
-
- if (e->e_class < 0)
- e->e_flags |= EF_NO_BODY_RETN;
-
- /*
- ** See if the message timed out.
- */
-
- if (!queueit)
- /* EMPTY */
- /* nothing to do */ ;
- else if (IS_MSG_ERR(msg_timeout))
- {
- if (failure_return)
- {
- if (msg_timeout == MSG_NOT_BY)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "delivery time expired %lds",
- e->e_deliver_by);
- }
- else
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Cannot send message for %s",
- pintvl(TimeOuts.to_q_return[e->e_timeoutclass],
- false));
- }
-
- /* don't free, allocated from e_rpool */
- e->e_message = sm_rpool_strdup_x(e->e_rpool, buf);
- message(buf);
- e->e_flags |= EF_CLRQUEUE;
- }
- if (msg_timeout == MSG_NOT_BY)
- {
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "Delivery time (%lds) expired\n",
- e->e_deliver_by);
- }
- else
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "Message could not be delivered for %s\n",
- pintvl(TimeOuts.to_q_return[e->e_timeoutclass],
- false));
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "Message will be deleted from queue\n");
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_UNDELIVERED(q->q_state))
- {
- q->q_state = QS_BADADDR;
- if (msg_timeout == MSG_NOT_BY)
- q->q_status = "5.4.7";
- else
- q->q_status = "4.4.7";
- }
- }
- }
- else
- {
- if (TimeOuts.to_q_warning[e->e_timeoutclass] > 0 &&
- now >= e->e_ctime +
- TimeOuts.to_q_warning[e->e_timeoutclass])
- msg_timeout = MSG_WARN;
- else if (IS_DLVR_NOTIFY(e) &&
- e->e_deliver_by > 0 &&
- now >= e->e_ctime + e->e_deliver_by)
- msg_timeout = MSG_WARN_BY;
-
- if (IS_MSG_WARN(msg_timeout))
- {
- if (!bitset(EF_WARNING|EF_RESPONSE, e->e_flags) &&
- e->e_class >= 0 &&
- e->e_from.q_paddr != NULL &&
- strcmp(e->e_from.q_paddr, "<>") != 0 &&
- sm_strncasecmp(e->e_from.q_paddr, "owner-", 6) != 0 &&
- (strlen(e->e_from.q_paddr) <= 8 ||
- sm_strcasecmp(&e->e_from.q_paddr[strlen(e->e_from.q_paddr) - 8],
- "-request") != 0))
- {
- for (q = e->e_sendqueue; q != NULL;
- q = q->q_next)
- {
- if (QS_IS_UNDELIVERED(q->q_state)
-#if _FFR_NODELAYDSN_ON_HOLD
- && !bitnset(M_HOLD,
- q->q_mailer->m_flags)
-#endif /* _FFR_NODELAYDSN_ON_HOLD */
- )
- {
- if (msg_timeout ==
- MSG_WARN_BY &&
- (bitset(QPINGONDELAY,
- q->q_flags) ||
- !bitset(QHASNOTIFY,
- q->q_flags))
- )
- {
- q->q_flags |= QBYNDELAY;
- delay_return = true;
- }
- if (bitset(QPINGONDELAY,
- q->q_flags))
- {
- q->q_flags |= QDELAYED;
- delay_return = true;
- }
- }
- }
- }
- if (delay_return)
- {
- if (msg_timeout == MSG_WARN_BY)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Warning: Delivery time (%lds) exceeded",
- e->e_deliver_by);
- }
- else
- (void) sm_snprintf(buf, sizeof(buf),
- "Warning: could not send message for past %s",
- pintvl(TimeOuts.to_q_warning[e->e_timeoutclass],
- false));
-
- /* don't free, allocated from e_rpool */
- e->e_message = sm_rpool_strdup_x(e->e_rpool,
- buf);
- message(buf);
- e->e_flags |= EF_WARNING;
- }
- if (msg_timeout == MSG_WARN_BY)
- {
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "Warning: Delivery time (%lds) exceeded\n",
- e->e_deliver_by);
- }
- else
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "Warning: message still undelivered after %s\n",
- pintvl(TimeOuts.to_q_warning[e->e_timeoutclass],
- false));
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "Will keep trying until message is %s old\n",
- pintvl(TimeOuts.to_q_return[e->e_timeoutclass],
- false));
- }
- }
-
- if (tTd(50, 2))
- sm_dprintf("failure_return=%d delay_return=%d success_return=%d queueit=%d\n",
- failure_return, delay_return, success_return, queueit);
-
- /*
- ** If we had some fatal error, but no addresses are marked as
- ** bad, mark them _all_ as bad.
- */
-
- if (bitset(EF_FATALERRS, e->e_flags) && !failure_return)
- {
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if ((QS_IS_OK(q->q_state) ||
- QS_IS_VERIFIED(q->q_state)) &&
- bitset(QPINGONFAILURE, q->q_flags))
- {
- failure_return = true;
- q->q_state = QS_BADADDR;
- }
- }
- }
-
- /*
- ** Send back return receipts as requested.
- */
-
- if (success_return && !failure_return && !delay_return && fulldrop &&
- !bitset(PRIV_NORECEIPTS, PrivacyFlags) &&
- strcmp(e->e_from.q_paddr, "<>") != 0)
- {
- auto ADDRESS *rlist = NULL;
-
- if (tTd(50, 8))
- sm_dprintf("dropenvelope(%s): sending return receipt\n",
- id);
- e->e_flags |= EF_SENDRECEIPT;
- (void) sendtolist(e->e_from.q_paddr, NULLADDR, &rlist, 0, e);
- (void) returntosender("Return receipt", rlist, RTSF_NO_BODY, e);
- }
- e->e_flags &= ~EF_SENDRECEIPT;
-
- /*
- ** Arrange to send error messages if there are fatal errors.
- */
-
- if ((failure_return || delay_return) && e->e_errormode != EM_QUIET)
- {
- if (tTd(50, 8))
- sm_dprintf("dropenvelope(%s): saving mail\n", id);
- panic = savemail(e, !bitset(EF_NO_BODY_RETN, e->e_flags));
- }
-
- /*
- ** Arrange to send warning messages to postmaster as requested.
- */
-
- if ((failure_return || pmnotify) &&
- PostMasterCopy != NULL &&
- !bitset(EF_RESPONSE, e->e_flags) &&
- e->e_class >= 0)
- {
- auto ADDRESS *rlist = NULL;
- char pcopy[MAXNAME];
-
- if (failure_return)
- {
- expand(PostMasterCopy, pcopy, sizeof(pcopy), e);
-
- if (tTd(50, 8))
- sm_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);
- }
-
- /*
- ** Instantiate or deinstantiate the queue.
- */
-
-simpledrop:
- if (tTd(50, 8))
- sm_dprintf("dropenvelope(%s): at simpledrop, queueit=%d\n",
- id, queueit);
- if (!queueit || bitset(EF_CLRQUEUE, e->e_flags))
- {
- if (tTd(50, 1))
- {
- sm_dprintf("\n===== Dropping queue files for %s... queueit=%d, e_flags=",
- e->e_id, queueit);
- printenvflags(e);
- }
- if (!panic)
- {
- if (e->e_dfp != NULL)
- {
- (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
- e->e_dfp = NULL;
- }
- (void) xunlink(queuename(e, DATAFL_LETTER));
- }
- if (panic && QueueMode == QM_LOST)
- {
- /*
- ** leave the Qf file behind as
- ** the delivery attempt failed.
- */
-
- /* EMPTY */
- }
- else
- if (xunlink(queuename(e, ANYQFL_LETTER)) == 0)
- {
- /* add to available space in filesystem */
- updfs(e, -1, panic ? 0 : -1, "dropenvelope");
- }
-
- 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 (!split)
- queueup(e, false, true);
- else
- {
- ENVELOPE *oldsib;
- ENVELOPE *ee;
-
- /*
- ** Save old sibling and set it to NULL to avoid
- ** queueing up the same envelopes again.
- ** This requires that envelopes in that list have
- ** been take care of before (or at some other place).
- */
-
- oldsib = e->e_sibling;
- e->e_sibling = NULL;
- if (!split_by_recipient(e) &&
- bitset(EF_FATALERRS, e->e_flags))
- {
- syserr("!dropenvelope(%s): cannot commit data file %s, uid=%d",
- e->e_id, queuename(e, DATAFL_LETTER),
- (int) geteuid());
- }
- for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
- queueup(ee, false, true);
- queueup(e, false, true);
-
- /* clean up */
- for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling)
- {
- /* now unlock the job */
- if (tTd(50, 8))
- sm_dprintf("dropenvelope(%s): unlocking job\n",
- ee->e_id);
- closexscript(ee);
- unlockqueue(ee);
-
- /* this envelope is marked unused */
- if (ee->e_dfp != NULL)
- {
- (void) sm_io_close(ee->e_dfp,
- SM_TIME_DEFAULT);
- ee->e_dfp = NULL;
- }
- ee->e_id = NULL;
- ee->e_flags &= ~EF_HAS_DF;
- }
- e->e_sibling = oldsib;
- }
- }
-
- /* now unlock the job */
- if (tTd(50, 8))
- sm_dprintf("dropenvelope(%s): unlocking job\n", id);
- closexscript(e);
- unlockqueue(e);
-
- /* make sure that this envelope is marked unused */
- if (e->e_dfp != NULL)
- {
- (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
- e->e_dfp = NULL;
- }
- e->e_id = NULL;
- e->e_flags &= ~EF_HAS_DF;
-}
-/*
-** CLEARENVELOPE -- clear an envelope without unlocking
-**
-** This is normally used by a child process to get a clean
-** envelope without disturbing the parent.
-**
-** Parameters:
-** e -- the envelope to clear.
-** fullclear - if set, the current envelope is total
-** garbage and should be ignored; otherwise,
-** release any resources it may indicate.
-** rpool -- either NULL, or a pointer to a resource pool
-** from which envelope memory is allocated, and
-** to which envelope resources are attached.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Closes files associated with the envelope.
-** Marks the envelope as unallocated.
-*/
-
-void
-clearenvelope(e, fullclear, rpool)
- register ENVELOPE *e;
- bool fullclear;
- SM_RPOOL_T *rpool;
-{
- register HDR *bh;
- register HDR **nhp;
- extern ENVELOPE BlankEnvelope;
- char **p;
-
- if (!fullclear)
- {
- /* clear out any file information */
- if (e->e_xfp != NULL)
- (void) sm_io_close(e->e_xfp, SM_TIME_DEFAULT);
- if (e->e_dfp != NULL)
- (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
- e->e_xfp = e->e_dfp = NULL;
- }
-
- /*
- ** Copy BlankEnvelope into *e.
- ** It is not safe to simply copy pointers to strings;
- ** the strings themselves must be copied (or set to NULL).
- ** The problem is that when we assign a new string value to
- ** a member of BlankEnvelope, we free the old string.
- ** We did not need to do this copying in sendmail 8.11 :-(
- ** and it is a potential performance hit. Reference counted
- ** strings are one way out.
- */
-
- *e = BlankEnvelope;
- e->e_message = NULL;
- e->e_qfletter = '\0';
- e->e_quarmsg = NULL;
- macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
-
- /*
- ** Copy the macro table.
- ** We might be able to avoid this by zeroing the macro table
- ** and always searching BlankEnvelope.e_macro after e->e_macro
- ** in macvalue().
- */
-
- for (p = &e->e_macro.mac_table[0];
- p <= &e->e_macro.mac_table[MAXMACROID];
- ++p)
- {
- if (*p != NULL)
- *p = sm_rpool_strdup_x(rpool, *p);
- }
-
- /*
- ** XXX There are many strings in the envelope structure
- ** XXX that we are not attempting to copy here.
- ** XXX Investigate this further.
- */
-
- e->e_rpool = rpool;
- e->e_macro.mac_rpool = rpool;
- if (Verbose)
- set_delivery_mode(SM_DELIVER, e);
- bh = BlankEnvelope.e_header;
- nhp = &e->e_header;
- while (bh != NULL)
- {
- *nhp = (HDR *) sm_rpool_malloc_x(rpool, sizeof(*bh));
- memmove((char *) *nhp, (char *) bh, sizeof(*bh));
- bh = bh->h_link;
- nhp = &(*nhp)->h_link;
- }
-}
-/*
-** INITSYS -- initialize instantiation of system
-**
-** In Daemon mode, this is done in the child.
-**
-** Parameters:
-** e -- the envelope to use.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Initializes the system macros, some global variables,
-** etc. In particular, the current time in various
-** forms is set.
-*/
-
-void
-initsys(e)
- register ENVELOPE *e;
-{
- char buf[10];
-#ifdef TTYNAME
- static char ybuf[60]; /* holds tty id */
- register char *p;
- extern char *ttyname();
-#endif /* TTYNAME */
-
- /*
- ** Give this envelope a reality.
- ** I.e., an id, a transcript, and a creation time.
- ** We don't select the queue until all of the recipients are known.
- */
-
- openxscript(e);
- e->e_ctime = curtime();
- e->e_qfletter = '\0';
-
- /*
- ** Set OutChannel to something useful if stdout isn't it.
- ** This arranges that any extra stuff the mailer produces
- ** gets sent back to the user on error (because it is
- ** tucked away in the transcript).
- */
-
- if (OpMode == MD_DAEMON && bitset(EF_QUEUERUN, e->e_flags) &&
- e->e_xfp != NULL)
- OutChannel = e->e_xfp;
-
- /*
- ** Set up some basic system macros.
- */
-
- /* process id */
- (void) sm_snprintf(buf, sizeof(buf), "%d", (int) CurrentPid);
- macdefine(&e->e_macro, A_TEMP, 'p', buf);
-
- /* hop count */
- (void) sm_snprintf(buf, sizeof(buf), "%d", e->e_hopcount);
- macdefine(&e->e_macro, A_TEMP, 'c', buf);
-
- /* time as integer, unix time, arpa time */
- settime(e);
-
- /* Load average */
- sm_getla();
-
-#ifdef TTYNAME
- /* tty name */
- if (macvalue('y', e) == NULL)
- {
- p = ttyname(2);
- if (p != NULL)
- {
- if (strrchr(p, '/') != NULL)
- p = strrchr(p, '/') + 1;
- (void) sm_strlcpy(ybuf, sizeof(ybuf), p);
- macdefine(&e->e_macro, A_PERM, 'y', ybuf);
- }
- }
-#endif /* TTYNAME */
-}
-/*
-** SETTIME -- set the current time.
-**
-** Parameters:
-** e -- the envelope in which the macros should be set.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets the various time macros -- $a, $b, $d, $t.
-*/
-
-void
-settime(e)
- register ENVELOPE *e;
-{
- register char *p;
- auto time_t now;
- char buf[30];
- register struct tm *tm;
-
- now = curtime();
- (void) sm_snprintf(buf, sizeof(buf), "%ld", (long) now);
- macdefine(&e->e_macro, A_TEMP, macid("{time}"), buf);
- tm = gmtime(&now);
- (void) sm_snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d",
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_hour, tm->tm_min);
- macdefine(&e->e_macro, A_TEMP, 't', buf);
- (void) sm_strlcpy(buf, ctime(&now), sizeof(buf));
- p = strchr(buf, '\n');
- if (p != NULL)
- *p = '\0';
- macdefine(&e->e_macro, A_TEMP, 'd', buf);
- macdefine(&e->e_macro, A_TEMP, 'b', arpadate(buf));
- if (macvalue('a', e) == NULL)
- macdefine(&e->e_macro, A_PERM, 'a', macvalue('b', e));
-}
-/*
-** OPENXSCRIPT -- Open transcript file
-**
-** Creates a transcript file for possible eventual mailing or
-** sending back.
-**
-** Parameters:
-** e -- the envelope to create the transcript in/for.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** Creates the transcript file.
-*/
-
-#ifndef O_APPEND
-# define O_APPEND 0
-#endif /* ! O_APPEND */
-
-void
-openxscript(e)
- register ENVELOPE *e;
-{
- register char *p;
-
- 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, XSCRPT_LETTER);
- e->e_xfp = bfopen(p, FileMode, XscriptFileBufferSize,
- SFF_NOTEXCL|SFF_OPENASROOT);
-
- if (e->e_xfp == NULL)
- {
- syserr("Can't create transcript file %s", p);
- e->e_xfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
- SM_PATH_DEVNULL, SM_IO_RDWR, NULL);
- if (e->e_xfp == NULL)
- syserr("!Can't open %s", SM_PATH_DEVNULL);
- }
- (void) sm_io_setvbuf(e->e_xfp, SM_TIME_DEFAULT, NULL, SM_IO_LBF, 0);
- if (tTd(46, 9))
- {
- sm_dprintf("openxscript(%s):\n ", p);
- dumpfd(sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL), true,
- false);
- }
-}
-/*
-** CLOSEXSCRIPT -- close the transcript file.
-**
-** Parameters:
-** e -- the envelope containing the transcript to close.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** none.
-*/
-
-void
-closexscript(e)
- register ENVELOPE *e;
-{
- if (e->e_xfp == NULL)
- return;
-#if 0
- if (e->e_lockfp == NULL)
- syserr("closexscript: job not locked");
-#endif /* 0 */
- (void) sm_io_close(e->e_xfp, SM_TIME_DEFAULT);
- e->e_xfp = NULL;
-}
-/*
-** SETSENDER -- set the person who this message is from
-**
-** Under certain circumstances allow the user to say who
-** s/he is (using -f or -r). These are:
-** 1. The user's uid is zero (root).
-** 2. The user's login name is in an approved list (typically
-** from a network server).
-** 3. The address the user is trying to claim has a
-** "!" character in it (since #2 doesn't do it for
-** us if we are dialing out for UUCP).
-** A better check to replace #3 would be if the
-** effective uid is "UUCP" -- this would require me
-** to rewrite getpwent to "grab" uucp as it went by,
-** make getname more nasty, do another passwd file
-** scan, or compile the UID of "UUCP" into the code,
-** all of which are reprehensible.
-**
-** Assuming all of these fail, we figure out something
-** ourselves.
-**
-** Parameters:
-** from -- the person we would like to believe this message
-** is from, as specified on the command line.
-** e -- the envelope in which we would like the sender set.
-** delimptr -- if non-NULL, set to the location of the
-** trailing delimiter.
-** delimchar -- the character that will delimit the sender
-** address.
-** internal -- set if this address is coming from an internal
-** source such as an owner alias.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** sets sendmail's notion of who the from person is.
-*/
-
-void
-setsender(from, e, delimptr, delimchar, internal)
- char *from;
- register ENVELOPE *e;
- char **delimptr;
- int delimchar;
- bool internal;
-{
- register char **pvp;
- char *realname = NULL;
- char *bp;
- char buf[MAXNAME + 2];
- char pvpbuf[PSBUFSIZE];
- extern char *FullName;
-
- if (tTd(45, 1))
- sm_dprintf("setsender(%s)\n", from == NULL ? "" : from);
-
- /* may be set from earlier calls */
- macdefine(&e->e_macro, A_PERM, 'x', "");
-
- /*
- ** Figure out the real user executing us.
- ** Username can return errno != 0 on non-errors.
- */
-
- if (bitset(EF_QUEUERUN, e->e_flags) || OpMode == MD_SMTP ||
- OpMode == MD_ARPAFTP || OpMode == MD_DAEMON)
- realname = from;
- if (realname == NULL || realname[0] == '\0')
- realname = username();
-
- if (ConfigLevel < 2)
- SuprErrs = true;
-
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e s");
-
- /* 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, false) == NULL ||
- 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)
- {
- /* log garbage addresses for traceback */
- if (from != NULL && LogLevel > 2)
- {
- char *p;
- char ebuf[MAXNAME * 2 + 2];
-
- p = macvalue('_', e);
- if (p == NULL)
- {
- char *host = RealHostName;
-
- if (host == NULL)
- host = MyHostName;
- (void) sm_snprintf(ebuf, sizeof(ebuf),
- "%.*s@%.*s", MAXNAME,
- realname, MAXNAME, host);
- p = ebuf;
- }
- sm_syslog(LOG_NOTICE, e->e_id,
- "setsender: %s: invalid or unparsable, received from %s",
- shortenstring(from, 83), p);
- }
- if (from != NULL)
- {
- if (!QS_IS_BADADDR(e->e_from.q_state))
- {
- /* it was a bogus mailer in the from addr */
- e->e_status = "5.1.7";
- usrerrenh(e->e_status,
- "553 Invalid sender address");
- }
- SuprErrs = true;
- }
- if (from == realname ||
- parseaddr(from = realname,
- &e->e_from, RF_COPYALL|RF_SENDERADDR, ' ',
- NULL, e, false) == NULL)
- {
- char nbuf[100];
-
- SuprErrs = true;
- expand("\201n", nbuf, sizeof(nbuf), e);
- from = sm_rpool_strdup_x(e->e_rpool, nbuf);
- if (parseaddr(from, &e->e_from, RF_COPYALL, ' ',
- NULL, e, false) == NULL &&
- parseaddr(from = "postmaster", &e->e_from,
- RF_COPYALL, ' ', NULL, e, false) == NULL)
- syserr("553 5.3.0 setsender: can't even parse postmaster!");
- }
- }
- else
- FromFlag = true;
- e->e_from.q_state = QS_SENDER;
- if (tTd(45, 5))
- {
- sm_dprintf("setsender: QS_SENDER ");
- printaddr(sm_debug_file(), &e->e_from, false);
- }
- SuprErrs = false;
-
-#if USERDB
- if (bitnset(M_CHECKUDB, e->e_from.q_mailer->m_flags))
- {
- register char *p;
-
- p = udbsender(e->e_from.q_user, e->e_rpool);
- if (p != NULL)
- from = p;
- }
-#endif /* USERDB */
-
- if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
- {
- SM_MBDB_T user;
-
- if (!internal)
- {
- /* if the user already given fullname don't redefine */
- if (FullName == NULL)
- FullName = macvalue('x', e);
- if (FullName != NULL)
- {
- if (FullName[0] == '\0')
- FullName = NULL;
- else
- FullName = newstr(FullName);
- }
- }
-
- if (e->e_from.q_user[0] != '\0' &&
- sm_mbdb_lookup(e->e_from.q_user, &user) == EX_OK)
- {
- /*
- ** Process passwd file entry.
- */
-
- /* extract home directory */
- if (*user.mbdb_homedir == '\0')
- e->e_from.q_home = NULL;
- else if (strcmp(user.mbdb_homedir, "/") == 0)
- e->e_from.q_home = "";
- else
- e->e_from.q_home = sm_rpool_strdup_x(e->e_rpool,
- user.mbdb_homedir);
- macdefine(&e->e_macro, A_PERM, 'z', e->e_from.q_home);
-
- /* extract user and group id */
- if (user.mbdb_uid != SM_NO_UID)
- {
- e->e_from.q_uid = user.mbdb_uid;
- e->e_from.q_gid = user.mbdb_gid;
- e->e_from.q_flags |= QGOODUID;
- }
-
- /* extract full name from passwd file */
- if (FullName == NULL && !internal &&
- user.mbdb_fullname[0] != '\0' &&
- strcmp(user.mbdb_name, e->e_from.q_user) == 0)
- {
- FullName = newstr(user.mbdb_fullname);
- }
- }
- else
- {
- e->e_from.q_home = NULL;
- }
- if (FullName != NULL && !internal)
- macdefine(&e->e_macro, A_TEMP, 'x', FullName);
- }
- else if (!internal && OpMode != MD_DAEMON && OpMode != MD_SMTP)
- {
- if (e->e_from.q_home == NULL)
- {
- e->e_from.q_home = getenv("HOME");
- if (e->e_from.q_home != NULL)
- {
- if (*e->e_from.q_home == '\0')
- e->e_from.q_home = NULL;
- else if (strcmp(e->e_from.q_home, "/") == 0)
- e->e_from.q_home++;
- }
- }
- e->e_from.q_uid = RealUid;
- e->e_from.q_gid = RealGid;
- e->e_from.q_flags |= QGOODUID;
- }
-
- /*
- ** Rewrite the from person to dispose of possible implicit
- ** links in the net.
- */
-
- pvp = prescan(from, delimchar, pvpbuf, sizeof(pvpbuf), NULL,
- IntTokenTab, false);
- if (pvp == NULL)
- {
- /* 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));
- finis(true, true, ExitStat);
- }
- (void) REWRITE(pvp, 3, e);
- (void) REWRITE(pvp, 1, e);
- (void) REWRITE(pvp, 4, e);
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL);
- bp = buf + 1;
- cataddr(pvp, NULL, bp, sizeof(buf) - 2, '\0', false);
- if (*bp == '@' && !bitnset(M_NOBRACKET, e->e_from.q_mailer->m_flags))
- {
- /* heuristic: route-addr: add angle brackets */
- (void) sm_strlcat(bp, ">", sizeof(buf) - 1);
- *--bp = '<';
- }
- e->e_sender = sm_rpool_strdup_x(e->e_rpool, bp);
- macdefine(&e->e_macro, A_PERM, 'f', e->e_sender);
-
- /* save the domain spec if this mailer wants it */
- if (e->e_from.q_mailer != NULL &&
- bitnset(M_CANONICAL, e->e_from.q_mailer->m_flags))
- {
- char **lastat;
-
- /* get rid of any pesky angle brackets */
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e s");
- (void) REWRITE(pvp, 3, e);
- (void) REWRITE(pvp, 1, e);
- (void) REWRITE(pvp, 4, e);
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL);
-
- /* strip off to the last "@" sign */
- for (lastat = NULL; *pvp != NULL; pvp++)
- {
- if (strcmp(*pvp, "@") == 0)
- lastat = pvp;
- }
- if (lastat != NULL)
- {
- e->e_fromdomain = copyplist(lastat, true, e->e_rpool);
- if (tTd(45, 3))
- {
- sm_dprintf("Saving from domain: ");
- printav(sm_debug_file(), e->e_fromdomain);
- }
- }
- }
-}
-/*
-** PRINTENVFLAGS -- print envelope flags for debugging
-**
-** Parameters:
-** e -- the envelope with the flags to be printed.
-**
-** Returns:
-** none.
-*/
-
-struct eflags
-{
- char *ef_name;
- unsigned long ef_bit;
-};
-
-static struct eflags EnvelopeFlags[] =
-{
- { "OLDSTYLE", EF_OLDSTYLE },
- { "INQUEUE", EF_INQUEUE },
- { "NO_BODY_RETN", EF_NO_BODY_RETN },
- { "CLRQUEUE", EF_CLRQUEUE },
- { "SENDRECEIPT", EF_SENDRECEIPT },
- { "FATALERRS", EF_FATALERRS },
- { "DELETE_BCC", EF_DELETE_BCC },
- { "RESPONSE", EF_RESPONSE },
- { "RESENT", EF_RESENT },
- { "VRFYONLY", EF_VRFYONLY },
- { "WARNING", EF_WARNING },
- { "QUEUERUN", EF_QUEUERUN },
- { "GLOBALERRS", EF_GLOBALERRS },
- { "PM_NOTIFY", EF_PM_NOTIFY },
- { "METOO", EF_METOO },
- { "LOGSENDER", EF_LOGSENDER },
- { "NORECEIPT", EF_NORECEIPT },
- { "HAS8BIT", EF_HAS8BIT },
- { "NL_NOT_EOL", EF_NL_NOT_EOL },
- { "CRLF_NOT_EOL", EF_CRLF_NOT_EOL },
- { "RET_PARAM", EF_RET_PARAM },
- { "HAS_DF", EF_HAS_DF },
- { "IS_MIME", EF_IS_MIME },
- { "DONT_MIME", EF_DONT_MIME },
- { "DISCARD", EF_DISCARD },
- { "TOOBIG", EF_TOOBIG },
- { "SPLIT", EF_SPLIT },
- { "UNSAFE", EF_UNSAFE },
- { NULL, 0 }
-};
-
-void
-printenvflags(e)
- register ENVELOPE *e;
-{
- register struct eflags *ef;
- bool first = true;
-
- sm_dprintf("%lx", e->e_flags);
- for (ef = EnvelopeFlags; ef->ef_name != NULL; ef++)
- {
- if (!bitset(ef->ef_bit, e->e_flags))
- continue;
- if (first)
- sm_dprintf("<%s", ef->ef_name);
- else
- sm_dprintf(",%s", ef->ef_name);
- first = false;
- }
- if (!first)
- sm_dprintf(">\n");
-}
diff --git a/contrib/sendmail/src/err.c b/contrib/sendmail/src/err.c
deleted file mode 100644
index 5825666..0000000
--- a/contrib/sendmail/src/err.c
+++ /dev/null
@@ -1,1158 +0,0 @@
-/*
- * Copyright (c) 1998-2003 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: err.c,v 8.196 2006/11/10 23:14:08 ca Exp $")
-
-#if LDAPMAP
-# include <lber.h>
-# include <ldap.h> /* for LDAP error codes */
-#endif /* LDAPMAP */
-
-static void putoutmsg __P((char *, bool, bool));
-static void puterrmsg __P((char *));
-static char *fmtmsg __P((char *, const char *, const char *, const char *,
- int, const char *, va_list));
-
-/*
-** FATAL_ERROR -- handle a fatal exception
-**
-** This function is installed as the default exception handler
-** in the main sendmail process, and in all child processes
-** that we create. Its job is to handle exceptions that are not
-** handled at a lower level.
-**
-** The theory is that unhandled exceptions will be 'fatal' class
-** exceptions (with an "F:" prefix), such as the out-of-memory
-** exception "F:sm.heap". As such, they are handled by exiting
-** the process in exactly the same way that xalloc() in Sendmail 8.10
-** exits the process when it fails due to lack of memory:
-** we call syserr with a message beginning with "!".
-**
-** Parameters:
-** exc -- exception which is terminating this process
-**
-** Returns:
-** none
-*/
-
-void
-fatal_error(exc)
- SM_EXC_T *exc;
-{
- static char buf[256];
- SM_FILE_T f;
-
- /*
- ** This function may be called when the heap is exhausted.
- ** The following code writes the message for 'exc' into our
- ** static buffer without allocating memory or raising exceptions.
- */
-
- sm_strio_init(&f, buf, sizeof(buf));
- sm_exc_write(exc, &f);
- (void) sm_io_flush(&f, SM_TIME_DEFAULT);
-
- /*
- ** Terminate the process after logging an error and cleaning up.
- ** Problems:
- ** - syserr decides what class of error this is by looking at errno.
- ** That's no good; we should look at the exc structure.
- ** - The cleanup code should be moved out of syserr
- ** and into individual exception handlers
- ** that are part of the module they clean up after.
- */
-
- errno = ENOMEM;
- syserr("!%s", buf);
-}
-
-/*
-** SYSERR -- Print error message.
-**
-** Prints an error message via sm_io_printf to the diagnostic output.
-**
-** If the first character of the syserr message is `!' it will
-** log this as an ALERT message and exit immediately. This can
-** leave queue files in an indeterminate state, so it should not
-** be used lightly.
-**
-** If the first character of the syserr message is '!' or '@'
-** then syserr knows that the process is about to be terminated,
-** so the SMTP reply code defaults to 421. Otherwise, the
-** reply code defaults to 451 or 554, depending on errno.
-**
-** Parameters:
-** fmt -- the format string. An optional '!' or '@',
-** followed by an optional three-digit SMTP
-** reply code, followed by message text.
-** (others) -- parameters
-**
-** Returns:
-** none
-** Raises E:mta.quickabort if QuickAbort is set.
-**
-** Side Effects:
-** increments Errors.
-** sets ExitStat.
-*/
-
-char MsgBuf[BUFSIZ*2]; /* text of most recent message */
-static char HeldMessageBuf[sizeof(MsgBuf)]; /* for held messages */
-
-#if NAMED_BIND && !defined(NO_DATA)
-# define NO_DATA NO_ADDRESS
-#endif /* NAMED_BIND && !defined(NO_DATA) */
-
-void
-/*VARARGS1*/
-#ifdef __STDC__
-syserr(const char *fmt, ...)
-#else /* __STDC__ */
-syserr(fmt, va_alist)
- const char *fmt;
- va_dcl
-#endif /* __STDC__ */
-{
- register char *p;
- int save_errno = errno;
- bool panic;
- bool exiting;
- char *user;
- char *enhsc;
- char *errtxt;
- struct passwd *pw;
- char ubuf[80];
- SM_VA_LOCAL_DECL
-
- switch (*fmt)
- {
- case '!':
- ++fmt;
- panic = true;
- exiting = true;
- break;
- case '@':
- ++fmt;
- panic = false;
- exiting = true;
- break;
- default:
- panic = false;
- exiting = false;
- break;
- }
-
- /* format and output the error message */
- if (exiting)
- {
- /*
- ** Since we are terminating the process,
- ** we are aborting the entire SMTP session,
- ** rather than just the current transaction.
- */
-
- p = "421";
- enhsc = "4.0.0";
- }
- else if (save_errno == 0)
- {
- p = "554";
- enhsc = "5.0.0";
- }
- else
- {
- p = "451";
- enhsc = "4.0.0";
- }
- SM_VA_START(ap, fmt);
- errtxt = fmtmsg(MsgBuf, (char *) NULL, p, enhsc, save_errno, fmt, ap);
- SM_VA_END(ap);
- puterrmsg(MsgBuf);
-
- /* save this message for mailq printing */
- if (!panic && CurEnv != NULL)
- {
- char *nmsg = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
-
- if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
- sm_free(CurEnv->e_message);
- CurEnv->e_message = nmsg;
- }
-
- /* determine exit status if not already set */
- if (ExitStat == EX_OK)
- {
- if (save_errno == 0)
- ExitStat = EX_SOFTWARE;
- else
- ExitStat = EX_OSERR;
- if (tTd(54, 1))
- sm_dprintf("syserr: ExitStat = %d\n", ExitStat);
- }
-
- pw = sm_getpwuid(RealUid);
- if (pw != NULL)
- user = pw->pw_name;
- else
- {
- user = ubuf;
- (void) sm_snprintf(ubuf, sizeof(ubuf), "UID%d", (int) RealUid);
- }
-
- if (LogLevel > 0)
- sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
- CurEnv == NULL ? NOQID : CurEnv->e_id,
- "SYSERR(%s): %.900s",
- user, errtxt);
- switch (save_errno)
- {
- case EBADF:
- case ENFILE:
- case EMFILE:
- case ENOTTY:
-#ifdef EFBIG
- case EFBIG:
-#endif /* EFBIG */
-#ifdef ESPIPE
- case ESPIPE:
-#endif /* ESPIPE */
-#ifdef EPIPE
- case EPIPE:
-#endif /* EPIPE */
-#ifdef ENOBUFS
- case ENOBUFS:
-#endif /* ENOBUFS */
-#ifdef ESTALE
- case ESTALE:
-#endif /* ESTALE */
- printopenfds(true);
- mci_dump_all(smioout, true);
- break;
- }
- if (panic)
- {
-#if XLA
- xla_all_end();
-#endif /* XLA */
- sync_queue_time();
- if (tTd(0, 1))
- abort();
- exit(EX_OSERR);
- }
- errno = 0;
- if (QuickAbort)
- sm_exc_raisenew_x(&EtypeQuickAbort, 2);
-}
-/*
-** USRERR -- Signal user error.
-**
-** This is much like syserr except it is for user errors.
-**
-** Parameters:
-** fmt -- the format string. If it does not begin with
-** a three-digit SMTP reply code, 550 is assumed.
-** (others) -- sm_io_printf strings
-**
-** Returns:
-** none
-** Raises E:mta.quickabort if QuickAbort is set.
-**
-** Side Effects:
-** increments Errors.
-*/
-
-/*VARARGS1*/
-void
-#ifdef __STDC__
-usrerr(const char *fmt, ...)
-#else /* __STDC__ */
-usrerr(fmt, va_alist)
- const char *fmt;
- va_dcl
-#endif /* __STDC__ */
-{
- char *enhsc;
- char *errtxt;
- SM_VA_LOCAL_DECL
-
- if (fmt[0] == '5' || fmt[0] == '6')
- enhsc = "5.0.0";
- else if (fmt[0] == '4' || fmt[0] == '8')
- enhsc = "4.0.0";
- else if (fmt[0] == '2')
- enhsc = "2.0.0";
- else
- enhsc = NULL;
- SM_VA_START(ap, fmt);
- errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
- SM_VA_END(ap);
-
- if (SuprErrs)
- return;
-
- /* save this message for mailq printing */
- switch (MsgBuf[0])
- {
- case '4':
- case '8':
- if (CurEnv->e_message != NULL)
- break;
-
- /* FALLTHROUGH */
-
- case '5':
- case '6':
- if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
- sm_free(CurEnv->e_message);
- if (MsgBuf[0] == '6')
- {
- char buf[MAXLINE];
-
- (void) sm_snprintf(buf, sizeof(buf),
- "Postmaster warning: %.*s",
- (int) sizeof(buf) - 22, errtxt);
- CurEnv->e_message =
- sm_rpool_strdup_x(CurEnv->e_rpool, buf);
- }
- else
- {
- CurEnv->e_message =
- sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
- }
- break;
- }
-
- puterrmsg(MsgBuf);
- if (LogLevel > 3 && LogUsrErrs)
- sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
- if (QuickAbort)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-}
-/*
-** USRERRENH -- Signal user error.
-**
-** Same as usrerr but with enhanced status code.
-**
-** Parameters:
-** enhsc -- the enhanced status code.
-** fmt -- the format string. If it does not begin with
-** a three-digit SMTP reply code, 550 is assumed.
-** (others) -- sm_io_printf strings
-**
-** Returns:
-** none
-** Raises E:mta.quickabort if QuickAbort is set.
-**
-** Side Effects:
-** increments Errors.
-*/
-
-/*VARARGS1*/
-void
-#ifdef __STDC__
-usrerrenh(char *enhsc, const char *fmt, ...)
-#else /* __STDC__ */
-usrerrenh(enhsc, fmt, va_alist)
- char *enhsc;
- const char *fmt;
- va_dcl
-#endif /* __STDC__ */
-{
- char *errtxt;
- SM_VA_LOCAL_DECL
-
- if (enhsc == NULL || *enhsc == '\0')
- {
- if (fmt[0] == '5' || fmt[0] == '6')
- enhsc = "5.0.0";
- else if (fmt[0] == '4' || fmt[0] == '8')
- enhsc = "4.0.0";
- else if (fmt[0] == '2')
- enhsc = "2.0.0";
- }
- SM_VA_START(ap, fmt);
- errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "550", enhsc, 0, fmt, ap);
- SM_VA_END(ap);
-
- if (SuprErrs)
- return;
-
- /* save this message for mailq printing */
- switch (MsgBuf[0])
- {
- case '4':
- case '8':
- if (CurEnv->e_message != NULL)
- break;
-
- /* FALLTHROUGH */
-
- case '5':
- case '6':
- if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
- sm_free(CurEnv->e_message);
- if (MsgBuf[0] == '6')
- {
- char buf[MAXLINE];
-
- (void) sm_snprintf(buf, sizeof(buf),
- "Postmaster warning: %.*s",
- (int) sizeof(buf) - 22, errtxt);
- CurEnv->e_message =
- sm_rpool_strdup_x(CurEnv->e_rpool, buf);
- }
- else
- {
- CurEnv->e_message =
- sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
- }
- break;
- }
-
- puterrmsg(MsgBuf);
- if (LogLevel > 3 && LogUsrErrs)
- sm_syslog(LOG_NOTICE, CurEnv->e_id, "%.900s", errtxt);
- if (QuickAbort)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-}
-/*
-** MESSAGE -- print message (not necessarily an error)
-**
-** Parameters:
-** msg -- the message (sm_io_printf fmt) -- it can begin with
-** an SMTP reply code. If not, 050 is assumed.
-** (others) -- sm_io_printf arguments
-**
-** Returns:
-** none
-**
-** Side Effects:
-** none.
-*/
-
-/*VARARGS1*/
-void
-#ifdef __STDC__
-message(const char *msg, ...)
-#else /* __STDC__ */
-message(msg, va_alist)
- const char *msg;
- va_dcl
-#endif /* __STDC__ */
-{
- char *errtxt;
- SM_VA_LOCAL_DECL
-
- errno = 0;
- SM_VA_START(ap, msg);
- errtxt = fmtmsg(MsgBuf, CurEnv->e_to, "050", (char *) NULL, 0, msg, ap);
- SM_VA_END(ap);
- putoutmsg(MsgBuf, false, false);
-
- /* save this message for mailq printing */
- switch (MsgBuf[0])
- {
- case '4':
- case '8':
- if (CurEnv->e_message != NULL)
- break;
- /* FALLTHROUGH */
-
- case '5':
- if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
- sm_free(CurEnv->e_message);
- CurEnv->e_message =
- sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
- break;
- }
-}
-/*
-** NMESSAGE -- print message (not necessarily an error)
-**
-** Just like "message" except it never puts the to... tag on.
-**
-** Parameters:
-** msg -- the message (sm_io_printf fmt) -- if it begins
-** with a three digit SMTP reply code, that is used,
-** otherwise 050 is assumed.
-** (others) -- sm_io_printf arguments
-**
-** Returns:
-** none
-**
-** Side Effects:
-** none.
-*/
-
-/*VARARGS1*/
-void
-#ifdef __STDC__
-nmessage(const char *msg, ...)
-#else /* __STDC__ */
-nmessage(msg, va_alist)
- const char *msg;
- va_dcl
-#endif /* __STDC__ */
-{
- char *errtxt;
- SM_VA_LOCAL_DECL
-
- errno = 0;
- SM_VA_START(ap, msg);
- errtxt = fmtmsg(MsgBuf, (char *) NULL, "050",
- (char *) NULL, 0, msg, ap);
- SM_VA_END(ap);
- putoutmsg(MsgBuf, false, false);
-
- /* save this message for mailq printing */
- switch (MsgBuf[0])
- {
- case '4':
- case '8':
- if (CurEnv->e_message != NULL)
- break;
- /* FALLTHROUGH */
-
- case '5':
- if (CurEnv->e_rpool == NULL && CurEnv->e_message != NULL)
- sm_free(CurEnv->e_message);
- CurEnv->e_message = sm_rpool_strdup_x(CurEnv->e_rpool, errtxt);
- break;
- }
-}
-/*
-** PUTOUTMSG -- output error message to transcript and channel
-**
-** Parameters:
-** msg -- message to output (in SMTP format).
-** holdmsg -- if true, don't output a copy of the message to
-** our output channel.
-** heldmsg -- if true, this is a previously held message;
-** don't log it to the transcript file.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Outputs msg to the transcript.
-** If appropriate, outputs it to the channel.
-** Deletes SMTP reply code number as appropriate.
-*/
-
-static void
-putoutmsg(msg, holdmsg, heldmsg)
- char *msg;
- bool holdmsg;
- bool heldmsg;
-{
- char msgcode = msg[0];
- char *errtxt = msg;
- char *id;
-
- /* display for debugging */
- if (tTd(54, 8))
- sm_dprintf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
- heldmsg ? " (held)" : "");
-
- /* map warnings to something SMTP can handle */
- if (msgcode == '6')
- msg[0] = '5';
- else if (msgcode == '8')
- msg[0] = '4';
- id = (CurEnv != NULL) ? CurEnv->e_id : NULL;
-
- /* output to transcript if serious */
- if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
- strchr("45", msg[0]) != NULL)
- (void) sm_io_fprintf(CurEnv->e_xfp, SM_TIME_DEFAULT, "%s\n",
- msg);
-
- if (LogLevel > 14 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
- sm_syslog(LOG_INFO, id,
- "--- %s%s%s", msg, holdmsg ? " (hold)" : "",
- heldmsg ? " (held)" : "");
-
- if (msgcode == '8')
- msg[0] = '0';
-
- /* output to channel if appropriate */
- if (!Verbose && msg[0] == '0')
- return;
- if (holdmsg)
- {
- /* save for possible future display */
- msg[0] = msgcode;
- if (HeldMessageBuf[0] == '5' && msgcode == '4')
- return;
- (void) sm_strlcpy(HeldMessageBuf, msg, sizeof(HeldMessageBuf));
- return;
- }
-
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
-
- if (OutChannel == NULL)
- return;
-
- /* find actual text of error (after SMTP status codes) */
- if (ISSMTPREPLY(errtxt))
- {
- int l;
-
- errtxt += 4;
- l = isenhsc(errtxt, ' ');
- if (l <= 0)
- l = isenhsc(errtxt, '\0');
- if (l > 0)
- errtxt += l + 1;
- }
-
- /* if DisConnected, OutChannel now points to the transcript */
- if (!DisConnected &&
- (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
- (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\r\n",
- msg);
- else
- (void) sm_io_fprintf(OutChannel, SM_TIME_DEFAULT, "%s\n",
- errtxt);
- if (TrafficLogFile != NULL)
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "%05d >>> %s\n", (int) CurrentPid,
- (OpMode == MD_SMTP || OpMode == MD_DAEMON)
- ? msg : errtxt);
-#if !PIPELINING
- /* XXX can't flush here for SMTP pipelining */
- if (msg[3] == ' ')
- (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
- if (!sm_io_error(OutChannel) || DisConnected)
- return;
-
- /*
- ** Error on output -- if reporting lost channel, just ignore it.
- ** Also, ignore errors from QUIT response (221 message) -- some
- ** rude servers don't read result.
- */
-
- if (InChannel == NULL || sm_io_eof(InChannel) ||
- sm_io_error(InChannel) || strncmp(msg, "221", 3) == 0)
- return;
-
- /* can't call syserr, 'cause we are using MsgBuf */
- HoldErrs = true;
- if (LogLevel > 0)
- sm_syslog(LOG_CRIT, id,
- "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
- CURHOSTNAME,
- shortenstring(msg, MAXSHORTSTR), sm_errstring(errno));
-#endif /* !PIPELINING */
-}
-/*
-** PUTERRMSG -- like putoutmsg, but does special processing for error messages
-**
-** Parameters:
-** msg -- the message to output.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets the fatal error bit in the envelope as appropriate.
-*/
-
-static void
-puterrmsg(msg)
- char *msg;
-{
- char msgcode = msg[0];
-
- /* output the message as usual */
- putoutmsg(msg, HoldErrs, false);
-
- /* be careful about multiple error messages */
- if (OnlyOneError)
- HoldErrs = true;
-
- /* signal the error */
- Errors++;
-
- if (CurEnv == NULL)
- return;
-
- if (msgcode == '6')
- {
- /* notify the postmaster */
- CurEnv->e_flags |= EF_PM_NOTIFY;
- }
- else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
- {
- /* mark long-term fatal errors */
- CurEnv->e_flags |= EF_FATALERRS;
- }
-}
-/*
-** ISENHSC -- check whether a string contains an enhanced status code
-**
-** Parameters:
-** s -- string with possible enhanced status code.
-** delim -- delim for enhanced status code.
-**
-** Returns:
-** 0 -- no enhanced status code.
-** >4 -- length of enhanced status code.
-**
-** Side Effects:
-** none.
-*/
-int
-isenhsc(s, delim)
- const char *s;
- int delim;
-{
- int l, h;
-
- if (s == NULL)
- return 0;
- if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
- return 0;
- h = 0;
- l = 2;
- while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
- ++h;
- if (h == 0 || s[l + h] != '.')
- return 0;
- l += h + 1;
- h = 0;
- while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
- ++h;
- if (h == 0 || s[l + h] != delim)
- return 0;
- return l + h;
-}
-/*
-** EXTENHSC -- check and extract an enhanced status code
-**
-** Parameters:
-** s -- string with possible enhanced status code.
-** delim -- delim for enhanced status code.
-** e -- pointer to storage for enhanced status code.
-** must be != NULL and have space for at least
-** 10 characters ([245].[0-9]{1,3}.[0-9]{1,3})
-**
-** Returns:
-** 0 -- no enhanced status code.
-** >4 -- length of enhanced status code.
-**
-** Side Effects:
-** fills e with enhanced status code.
-*/
-
-int
-extenhsc(s, delim, e)
- const char *s;
- int delim;
- char *e;
-{
- int l, h;
-
- if (s == NULL)
- return 0;
- if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
- return 0;
- h = 0;
- l = 2;
- e[0] = s[0];
- e[1] = '.';
- while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
- {
- e[l + h] = s[l + h];
- ++h;
- }
- if (h == 0 || s[l + h] != '.')
- return 0;
- e[l + h] = '.';
- l += h + 1;
- h = 0;
- while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
- {
- e[l + h] = s[l + h];
- ++h;
- }
- if (h == 0 || s[l + h] != delim)
- return 0;
- e[l + h] = '\0';
- return l + h;
-}
-/*
-** FMTMSG -- format a message into buffer.
-**
-** Parameters:
-** eb -- error buffer to get result -- MUST BE MsgBuf.
-** to -- the recipient tag for this message.
-** num -- default three digit SMTP reply code.
-** enhsc -- enhanced status code.
-** en -- the error number to display.
-** fmt -- format of string.
-** ap -- arguments for fmt.
-**
-** Returns:
-** pointer to error text beyond status codes.
-**
-** Side Effects:
-** none.
-*/
-
-static char *
-fmtmsg(eb, to, num, enhsc, eno, fmt, ap)
- register char *eb;
- const char *to;
- const char *num;
- const char *enhsc;
- int eno;
- const char *fmt;
- SM_VA_LOCAL_DECL
-{
- char del;
- int l;
- int spaceleft = sizeof(MsgBuf);
- char *errtxt;
-
- /* output the reply code */
- if (ISSMTPCODE(fmt))
- {
- num = fmt;
- fmt += 4;
- }
- if (num[3] == '-')
- del = '-';
- else
- del = ' ';
- if (SoftBounce && num[0] == '5')
- {
- /* replace 5 by 4 */
- (void) sm_snprintf(eb, spaceleft, "4%2.2s%c", num + 1, del);
- }
- else
- (void) sm_snprintf(eb, spaceleft, "%3.3s%c", num, del);
- eb += 4;
- spaceleft -= 4;
-
- if ((l = isenhsc(fmt, ' ' )) > 0 && l < spaceleft - 4)
- {
- /* copy enh.status code including trailing blank */
- l++;
- (void) sm_strlcpy(eb, fmt, l + 1);
- eb += l;
- spaceleft -= l;
- fmt += l;
- }
- else if ((l = isenhsc(enhsc, '\0')) > 0 && l < spaceleft - 4)
- {
- /* copy enh.status code */
- (void) sm_strlcpy(eb, enhsc, l + 1);
- eb[l] = ' ';
- eb[++l] = '\0';
- eb += l;
- spaceleft -= l;
- }
- if (SoftBounce && eb[-l] == '5')
- {
- /* replace 5 by 4 */
- eb[-l] = '4';
- }
- errtxt = eb;
-
- /* output the file name and line number */
- if (FileName != NULL)
- {
- (void) sm_snprintf(eb, spaceleft, "%s: line %d: ",
- shortenstring(FileName, 83), LineNumber);
- eb += (l = strlen(eb));
- spaceleft -= l;
- }
-
- /*
- ** output the "to" address only if it is defined and one of the
- ** following codes is used:
- ** 050 internal notices, e.g., alias expansion
- ** 250 Ok
- ** 252 Cannot VRFY user, but will accept message and attempt delivery
- ** 450 Requested mail action not taken: mailbox unavailable
- ** 550 Requested action not taken: mailbox unavailable
- ** 553 Requested action not taken: mailbox name not allowed
- **
- ** Notice: this still isn't "the right thing", this code shouldn't
- ** (indirectly) depend on CurEnv->e_to.
- */
-
- if (to != NULL && to[0] != '\0' &&
- (strncmp(num, "050", 3) == 0 ||
- strncmp(num, "250", 3) == 0 ||
- strncmp(num, "252", 3) == 0 ||
- strncmp(num, "450", 3) == 0 ||
- strncmp(num, "550", 3) == 0 ||
- strncmp(num, "553", 3) == 0))
- {
- (void) sm_strlcpyn(eb, spaceleft, 2,
- shortenstring(to, MAXSHORTSTR), "... ");
- spaceleft -= strlen(eb);
- while (*eb != '\0')
- *eb++ &= 0177;
- }
-
- /* output the message */
- (void) sm_vsnprintf(eb, spaceleft, fmt, ap);
- spaceleft -= strlen(eb);
- while (*eb != '\0')
- *eb++ &= 0177;
-
- /* output the error code, if any */
- if (eno != 0)
- (void) sm_strlcpyn(eb, spaceleft, 2, ": ", sm_errstring(eno));
-
- return errtxt;
-}
-/*
-** BUFFER_ERRORS -- arrange to buffer future error messages
-**
-** Parameters:
-** none
-**
-** Returns:
-** none.
-*/
-
-void
-buffer_errors()
-{
- HeldMessageBuf[0] = '\0';
- HoldErrs = true;
-}
-/*
-** FLUSH_ERRORS -- flush the held error message buffer
-**
-** Parameters:
-** print -- if set, print the message, otherwise just
-** delete it.
-**
-** Returns:
-** none.
-*/
-
-void
-flush_errors(print)
- bool print;
-{
- if (print && HeldMessageBuf[0] != '\0')
- putoutmsg(HeldMessageBuf, false, true);
- HeldMessageBuf[0] = '\0';
- HoldErrs = false;
-}
-/*
-** SM_ERRSTRING -- return string description of error code
-**
-** Parameters:
-** errnum -- the error number to translate
-**
-** Returns:
-** A string description of errnum.
-**
-** Side Effects:
-** none.
-*/
-
-const char *
-sm_errstring(errnum)
- int errnum;
-{
- char *dnsmsg;
- char *bp;
- static char buf[MAXLINE];
-#if HASSTRERROR
- char *err;
- char errbuf[30];
-#endif /* HASSTRERROR */
-#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
- extern char *sys_errlist[];
- extern int sys_nerr;
-#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
-
- /*
- ** Handle special network error codes.
- **
- ** These are 4.2/4.3bsd specific; they should be in daemon.c.
- */
-
- dnsmsg = NULL;
- switch (errnum)
- {
- case ETIMEDOUT:
- case ECONNRESET:
- bp = buf;
-#if HASSTRERROR
- err = strerror(errnum);
- if (err == NULL)
- {
- (void) sm_snprintf(errbuf, sizeof(errbuf),
- "Error %d", errnum);
- err = errbuf;
- }
- (void) sm_strlcpy(bp, err, SPACELEFT(buf, bp));
-#else /* HASSTRERROR */
- if (errnum >= 0 && errnum < sys_nerr)
- (void) sm_strlcpy(bp, sys_errlist[errnum],
- SPACELEFT(buf, bp));
- else
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- "Error %d", errnum);
-#endif /* HASSTRERROR */
- bp += strlen(bp);
- if (CurHostName != NULL)
- {
- if (errnum == ETIMEDOUT)
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- " with ");
- bp += strlen(bp);
- }
- else
- {
- bp = buf;
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- "Connection reset by ");
- bp += strlen(bp);
- }
- (void) sm_strlcpy(bp,
- shortenstring(CurHostName, MAXSHORTSTR),
- SPACELEFT(buf, bp));
- bp += strlen(buf);
- }
- if (SmtpPhase != NULL)
- {
- (void) sm_snprintf(bp, SPACELEFT(buf, bp),
- " during %s", SmtpPhase);
- }
- return buf;
-
- case EHOSTDOWN:
- if (CurHostName == NULL)
- break;
- (void) sm_snprintf(buf, sizeof(buf), "Host %s is down",
- shortenstring(CurHostName, MAXSHORTSTR));
- return buf;
-
- case ECONNREFUSED:
- if (CurHostName == NULL)
- break;
- (void) sm_strlcpyn(buf, sizeof(buf), 2, "Connection refused by ",
- shortenstring(CurHostName, MAXSHORTSTR));
- return buf;
-
-#if NAMED_BIND
- case HOST_NOT_FOUND + E_DNSBASE:
- dnsmsg = "host not found";
- break;
-
- case TRY_AGAIN + E_DNSBASE:
- dnsmsg = "host name lookup failure";
- break;
-
- case NO_RECOVERY + E_DNSBASE:
- dnsmsg = "non-recoverable error";
- break;
-
- case NO_DATA + E_DNSBASE:
- dnsmsg = "no data known";
- break;
-#endif /* NAMED_BIND */
-
- case EPERM:
- /* SunOS gives "Not owner" -- this is the POSIX message */
- return "Operation not permitted";
-
- /*
- ** Error messages used internally in sendmail.
- */
-
- case E_SM_OPENTIMEOUT:
- return "Timeout on file open";
-
- case E_SM_NOSLINK:
- return "Symbolic links not allowed";
-
- case E_SM_NOHLINK:
- return "Hard links not allowed";
-
- case E_SM_REGONLY:
- return "Regular files only";
-
- case E_SM_ISEXEC:
- return "Executable files not allowed";
-
- case E_SM_WWDIR:
- return "World writable directory";
-
- case E_SM_GWDIR:
- return "Group writable directory";
-
- case E_SM_FILECHANGE:
- return "File changed after open";
-
- case E_SM_WWFILE:
- return "World writable file";
-
- case E_SM_GWFILE:
- return "Group writable file";
-
- case E_SM_GRFILE:
- return "Group readable file";
-
- case E_SM_WRFILE:
- return "World readable file";
- }
-
- if (dnsmsg != NULL)
- {
- bp = buf;
- bp += sm_strlcpy(bp, "Name server: ", sizeof(buf));
- if (CurHostName != NULL)
- {
- (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2,
- shortenstring(CurHostName, MAXSHORTSTR), ": ");
- bp += strlen(bp);
- }
- (void) sm_strlcpy(bp, dnsmsg, SPACELEFT(buf, bp));
- return buf;
- }
-
-#if LDAPMAP
- if (errnum >= E_LDAPBASE)
- return ldap_err2string(errnum - E_LDAPBASE);
-#endif /* LDAPMAP */
-
-#if HASSTRERROR
- err = strerror(errnum);
- if (err == NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
- return buf;
- }
- return err;
-#else /* HASSTRERROR */
- if (errnum > 0 && errnum < sys_nerr)
- return sys_errlist[errnum];
-
- (void) sm_snprintf(buf, sizeof(buf), "Error %d", errnum);
- return buf;
-#endif /* HASSTRERROR */
-}
diff --git a/contrib/sendmail/src/headers.c b/contrib/sendmail/src/headers.c
deleted file mode 100644
index 8e70fed..0000000
--- a/contrib/sendmail/src/headers.c
+++ /dev/null
@@ -1,2311 +0,0 @@
-/*
- * Copyright (c) 1998-2004, 2006, 2007 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.
- *
- */
-
-#include <sendmail.h>
-#include <sm/sendmail.h>
-
-SM_RCSID("@(#)$Id: headers.c,v 8.312 2007/06/19 18:52:11 ca Exp $")
-
-static HDR *allocheader __P((char *, char *, int, SM_RPOOL_T *, bool));
-static size_t fix_mime_header __P((HDR *, ENVELOPE *));
-static int priencode __P((char *));
-static bool put_vanilla_header __P((HDR *, char *, MCI *));
-
-/*
-** SETUPHEADERS -- initialize headers in symbol table
-**
-** Parameters:
-** none
-**
-** Returns:
-** none
-*/
-
-void
-setupheaders()
-{
- struct hdrinfo *hi;
- STAB *s;
-
- for (hi = HdrInfo; hi->hi_field != NULL; hi++)
- {
- s = stab(hi->hi_field, ST_HEADER, ST_ENTER);
- s->s_header.hi_flags = hi->hi_flags;
- s->s_header.hi_ruleset = NULL;
- }
-}
-
-/*
-** DOCHOMPHEADER -- process and save a header line.
-**
-** Called by chompheader.
-**
-** Parameters:
-** line -- header as a text line.
-** pflag -- flags for chompheader() (from sendmail.h)
-** hdrp -- a pointer to the place to save the header.
-** e -- the envelope including this header.
-**
-** Returns:
-** flags for this header.
-**
-** Side Effects:
-** The header is saved on the header list.
-** Contents of 'line' are destroyed.
-*/
-
-static struct hdrinfo NormalHeader = { NULL, 0, NULL };
-static unsigned long dochompheader __P((char *, int, HDR **, ENVELOPE *));
-
-static unsigned long
-dochompheader(line, pflag, hdrp, e)
- char *line;
- int pflag;
- HDR **hdrp;
- ENVELOPE *e;
-{
- unsigned char mid = '\0';
- register char *p;
- register HDR *h;
- HDR **hp;
- char *fname;
- char *fvalue;
- bool cond = false;
- bool dropfrom;
- bool headeronly;
- STAB *s;
- struct hdrinfo *hi;
- bool nullheader = false;
- BITMAP256 mopts;
-
- headeronly = hdrp != NULL;
- if (!headeronly)
- hdrp = &e->e_header;
-
- /* strip off options */
- clrbitmap(mopts);
- p = line;
- if (!bitset(pflag, CHHDR_USER) && *p == '?')
- {
- int c;
- register char *q;
-
- q = strchr(++p, '?');
- if (q == NULL)
- goto hse;
-
- *q = '\0';
- c = *p & 0377;
-
- /* possibly macro conditional */
- if (c == MACROEXPAND)
- {
- /* catch ?$? */
- if (*++p == '\0')
- {
- *q = '?';
- goto hse;
- }
-
- mid = (unsigned char) *p++;
-
- /* catch ?$abc? */
- if (*p != '\0')
- {
- *q = '?';
- goto hse;
- }
- }
- else if (*p == '$')
- {
- /* catch ?$? */
- if (*++p == '\0')
- {
- *q = '?';
- goto hse;
- }
-
- mid = (unsigned char) macid(p);
- if (bitset(0200, mid))
- {
- p += strlen(macname(mid)) + 2;
- SM_ASSERT(p <= q);
- }
- else
- p++;
-
- /* catch ?$abc? */
- if (*p != '\0')
- {
- *q = '?';
- goto hse;
- }
- }
- else
- {
- while (*p != '\0')
- {
- if (!isascii(*p))
- {
- *q = '?';
- goto hse;
- }
-
- setbitn(bitidx(*p), mopts);
- cond = true;
- p++;
- }
- }
- p = q + 1;
- }
-
- /* find canonical name */
- fname = p;
- while (isascii(*p) && isgraph(*p) && *p != ':')
- p++;
- fvalue = p;
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p++ != ':' || fname == fvalue)
- {
-hse:
- syserr("553 5.3.0 header syntax error, line \"%s\"", line);
- return 0;
- }
- *fvalue = '\0';
- fvalue = p;
-
- /* if the field is null, go ahead and use the default */
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- nullheader = true;
-
- /* security scan: long field names are end-of-header */
- if (strlen(fname) > 100)
- return H_EOH;
-
- /* check to see if it represents a ruleset call */
- if (bitset(pflag, CHHDR_DEF))
- {
- char hbuf[50];
-
- (void) expand(fvalue, hbuf, sizeof(hbuf), e);
- for (p = hbuf; isascii(*p) && isspace(*p); )
- p++;
- if ((*p++ & 0377) == CALLSUBR)
- {
- auto char *endp;
- bool strc;
-
- strc = *p == '+'; /* strip comments? */
- if (strc)
- ++p;
- if (strtorwset(p, &endp, ST_ENTER) > 0)
- {
- *endp = '\0';
- s = stab(fname, ST_HEADER, ST_ENTER);
- if (LogLevel > 9 &&
- s->s_header.hi_ruleset != NULL)
- sm_syslog(LOG_WARNING, NOQID,
- "Warning: redefined ruleset for header=%s, old=%s, new=%s",
- fname,
- s->s_header.hi_ruleset, p);
- s->s_header.hi_ruleset = newstr(p);
- if (!strc)
- s->s_header.hi_flags |= H_STRIPCOMM;
- }
- return 0;
- }
- }
-
- /* see if it is a known type */
- s = stab(fname, ST_HEADER, ST_FIND);
- if (s != NULL)
- hi = &s->s_header;
- else
- hi = &NormalHeader;
-
- if (tTd(31, 9))
- {
- if (s == NULL)
- sm_dprintf("no header flags match\n");
- else
- sm_dprintf("header match, flags=%lx, ruleset=%s\n",
- hi->hi_flags,
- hi->hi_ruleset == NULL ? "<NULL>"
- : hi->hi_ruleset);
- }
-
- /* see if this is a resent message */
- if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
- bitset(H_RESENT, hi->hi_flags))
- e->e_flags |= EF_RESENT;
-
- /* if this is an Errors-To: header keep track of it now */
- if (UseErrorsTo && !bitset(pflag, CHHDR_DEF) && !headeronly &&
- bitset(H_ERRORSTO, hi->hi_flags))
- (void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e);
-
- /* if this means "end of header" quit now */
- if (!headeronly && bitset(H_EOH, hi->hi_flags))
- return hi->hi_flags;
-
- /*
- ** Horrible hack to work around problem with Lotus Notes SMTP
- ** mail gateway, which generates From: headers with newlines in
- ** them and the <address> on the second line. Although this is
- ** legal RFC 822, many MUAs don't handle this properly and thus
- ** never find the actual address.
- */
-
- if (bitset(H_FROM, hi->hi_flags) && SingleLineFromHeader)
- {
- while ((p = strchr(fvalue, '\n')) != NULL)
- *p = ' ';
- }
-
- /*
- ** If there is a check ruleset, verify it against the header.
- */
-
- if (bitset(pflag, CHHDR_CHECK))
- {
- int rscheckflags;
- char *rs;
-
- rscheckflags = RSF_COUNT;
- if (!bitset(hi->hi_flags, H_FROM|H_RCPT))
- rscheckflags |= RSF_UNSTRUCTURED;
-
- /* no ruleset? look for default */
- rs = hi->hi_ruleset;
- if (rs == NULL)
- {
- s = stab("*", ST_HEADER, ST_FIND);
- if (s != NULL)
- {
- rs = (&s->s_header)->hi_ruleset;
- if (bitset((&s->s_header)->hi_flags,
- H_STRIPCOMM))
- rscheckflags |= RSF_RMCOMM;
- }
- }
- else if (bitset(hi->hi_flags, H_STRIPCOMM))
- rscheckflags |= RSF_RMCOMM;
- if (rs != NULL)
- {
- int l, k;
- char qval[MAXNAME];
-
- l = 0;
- qval[l++] = '"';
-
- /* - 3 to avoid problems with " at the end */
- /* should be sizeof(qval), not MAXNAME */
- for (k = 0; fvalue[k] != '\0' && l < MAXNAME - 3; k++)
- {
- switch (fvalue[k])
- {
- /* XXX other control chars? */
- case '\011': /* ht */
- case '\012': /* nl */
- case '\013': /* vt */
- case '\014': /* np */
- case '\015': /* cr */
- qval[l++] = ' ';
- break;
- case '"':
- qval[l++] = '\\';
- /* FALLTHROUGH */
- default:
- qval[l++] = fvalue[k];
- break;
- }
- }
- qval[l++] = '"';
- qval[l] = '\0';
- k += strlen(fvalue + k);
- if (k >= MAXNAME)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "Warning: truncated header '%s' before check with '%s' len=%d max=%d",
- fname, rs, k, MAXNAME - 1);
- }
- macdefine(&e->e_macro, A_TEMP,
- macid("{currHeader}"), qval);
- macdefine(&e->e_macro, A_TEMP,
- macid("{hdr_name}"), fname);
-
- (void) sm_snprintf(qval, sizeof(qval), "%d", k);
- macdefine(&e->e_macro, A_TEMP, macid("{hdrlen}"), qval);
- if (bitset(H_FROM, hi->hi_flags))
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "h s");
- else if (bitset(H_RCPT, hi->hi_flags))
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "h r");
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "h");
- (void) rscheck(rs, fvalue, NULL, e, rscheckflags, 3,
- NULL, e->e_id, NULL);
- }
- }
-
- /*
- ** Drop explicit From: if same as what we would generate.
- ** This is to make MH (which doesn't always give a full name)
- ** insert the full name information in all circumstances.
- */
-
- dropfrom = false;
- p = "resent-from";
- if (!bitset(EF_RESENT, e->e_flags))
- p += 7;
- if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
- !bitset(EF_QUEUERUN, e->e_flags) && sm_strcasecmp(fname, p) == 0)
- {
- if (tTd(31, 2))
- {
- sm_dprintf("comparing header from (%s) against default (%s or %s)\n",
- fvalue, e->e_from.q_paddr, e->e_from.q_user);
- }
- if (e->e_from.q_paddr != NULL &&
- e->e_from.q_mailer != NULL &&
- bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
- (strcmp(fvalue, e->e_from.q_paddr) == 0 ||
- strcmp(fvalue, e->e_from.q_user) == 0))
- dropfrom = true;
- }
-
- /* delete default value for this header */
- for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link)
- {
- if (sm_strcasecmp(fname, h->h_field) == 0 &&
- !bitset(H_USER, h->h_flags) &&
- !bitset(H_FORCE, h->h_flags))
- {
- if (nullheader)
- {
- /* user-supplied value was null */
- return 0;
- }
- if (dropfrom)
- {
- /* make this look like the user entered it */
- h->h_flags |= H_USER;
- return hi->hi_flags;
- }
- h->h_value = NULL;
- if (!cond)
- {
- /* copy conditions from default case */
- memmove((char *) mopts, (char *) h->h_mflags,
- sizeof(mopts));
- }
- h->h_macro = mid;
- }
- }
-
- /* create a new node */
- h = (HDR *) sm_rpool_malloc_x(e->e_rpool, sizeof(*h));
- h->h_field = sm_rpool_strdup_x(e->e_rpool, fname);
- h->h_value = sm_rpool_strdup_x(e->e_rpool, fvalue);
- h->h_link = NULL;
- memmove((char *) h->h_mflags, (char *) mopts, sizeof(mopts));
- h->h_macro = mid;
- *hp = h;
- h->h_flags = hi->hi_flags;
- if (bitset(pflag, CHHDR_USER) || bitset(pflag, CHHDR_QUEUE))
- h->h_flags |= H_USER;
-
- /* strip EOH flag if parsing MIME headers */
- if (headeronly)
- h->h_flags &= ~H_EOH;
- if (bitset(pflag, CHHDR_DEF))
- h->h_flags |= H_DEFAULT;
- if (cond || mid != '\0')
- h->h_flags |= H_CHECK;
-
- /* hack to see if this is a new format message */
- if (!bitset(pflag, CHHDR_DEF) && !headeronly &&
- bitset(H_RCPT|H_FROM, h->h_flags) &&
- (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL ||
- strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL))
- {
- e->e_flags &= ~EF_OLDSTYLE;
- }
-
- return h->h_flags;
-}
-
-/*
-** CHOMPHEADER -- process and save a header line.
-**
-** Called by collect, readcf, and readqf to deal with header lines.
-** This is just a wrapper for dochompheader().
-**
-** Parameters:
-** line -- header as a text line.
-** pflag -- flags for chompheader() (from sendmail.h)
-** hdrp -- a pointer to the place to save the header.
-** e -- the envelope including this header.
-**
-** Returns:
-** flags for this header.
-**
-** Side Effects:
-** The header is saved on the header list.
-** Contents of 'line' are destroyed.
-*/
-
-
-unsigned long
-chompheader(line, pflag, hdrp, e)
- char *line;
- int pflag;
- HDR **hdrp;
- register ENVELOPE *e;
-{
- unsigned long rval;
-
- if (tTd(31, 6))
- {
- sm_dprintf("chompheader: ");
- xputs(sm_debug_file(), line);
- sm_dprintf("\n");
- }
-
- /* quote this if user (not config file) input */
- if (bitset(pflag, CHHDR_USER))
- {
- char xbuf[MAXLINE];
- char *xbp = NULL;
- int xbufs;
-
- xbufs = sizeof(xbuf);
- xbp = quote_internal_chars(line, xbuf, &xbufs);
- if (tTd(31, 7))
- {
- sm_dprintf("chompheader: quoted: ");
- xputs(sm_debug_file(), xbp);
- sm_dprintf("\n");
- }
- rval = dochompheader(xbp, pflag, hdrp, e);
- if (xbp != xbuf)
- sm_free(xbp);
- }
- else
- rval = dochompheader(line, pflag, hdrp, e);
-
- return rval;
-}
-
-/*
-** ALLOCHEADER -- allocate a header entry
-**
-** Parameters:
-** field -- the name of the header field (will not be copied).
-** value -- the value of the field (will be copied).
-** flags -- flags to add to h_flags.
-** rp -- resource pool for allocations
-** space -- add leading space?
-**
-** Returns:
-** Pointer to a newly allocated and populated HDR.
-**
-** Notes:
-** o field and value must be in internal format, i.e.,
-** metacharacters must be "quoted", see quote_internal_chars().
-** o maybe add more flags to decide:
-** - what to copy (field/value)
-** - whether to convert value to an internal format
-*/
-
-static HDR *
-allocheader(field, value, flags, rp, space)
- char *field;
- char *value;
- int flags;
- SM_RPOOL_T *rp;
- bool space;
-{
- HDR *h;
- STAB *s;
-
- /* find info struct */
- s = stab(field, ST_HEADER, ST_FIND);
-
- /* allocate space for new header */
- h = (HDR *) sm_rpool_malloc_x(rp, sizeof(*h));
- h->h_field = field;
- if (space)
- {
- size_t l;
- char *n;
-
- l = strlen(value);
- SM_ASSERT(l + 2 > l);
- n = sm_rpool_malloc_x(rp, l + 2);
- n[0] = ' ';
- n[1] = '\0';
- sm_strlcpy(n + 1, value, l + 1);
- h->h_value = n;
- }
- else
- h->h_value = sm_rpool_strdup_x(rp, value);
- h->h_flags = flags;
- if (s != NULL)
- h->h_flags |= s->s_header.hi_flags;
- clrbitmap(h->h_mflags);
- h->h_macro = '\0';
-
- return h;
-}
-
-/*
-** ADDHEADER -- add a header entry to the end of the queue.
-**
-** This bypasses the special checking of chompheader.
-**
-** Parameters:
-** field -- the name of the header field (will not be copied).
-** value -- the value of the field (will be copied).
-** flags -- flags to add to h_flags.
-** e -- envelope.
-** space -- add leading space?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** adds the field on the list of headers for this envelope.
-**
-** Notes: field and value must be in internal format, i.e.,
-** metacharacters must be "quoted", see quote_internal_chars().
-*/
-
-void
-addheader(field, value, flags, e, space)
- char *field;
- char *value;
- int flags;
- ENVELOPE *e;
- bool space;
-{
- register HDR *h;
- HDR **hp;
- HDR **hdrlist = &e->e_header;
-
- /* find current place in list -- keep back pointer? */
- for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link)
- {
- if (sm_strcasecmp(field, h->h_field) == 0)
- break;
- }
-
- /* allocate space for new header */
- h = allocheader(field, value, flags, e->e_rpool, space);
- h->h_link = *hp;
- *hp = h;
-}
-
-/*
-** INSHEADER -- insert a header entry at the specified index
-** This bypasses the special checking of chompheader.
-**
-** Parameters:
-** idx -- index into the header list at which to insert
-** field -- the name of the header field (will be copied).
-** value -- the value of the field (will be copied).
-** flags -- flags to add to h_flags.
-** e -- envelope.
-** space -- add leading space?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** inserts the field on the list of headers for this envelope.
-**
-** Notes:
-** - field and value must be in internal format, i.e.,
-** metacharacters must be "quoted", see quote_internal_chars().
-** - the header list contains headers that might not be
-** sent "out" (see putheader(): "skip"), hence there is no
-** reliable way to insert a header at an exact position
-** (except at the front or end).
-*/
-
-void
-insheader(idx, field, value, flags, e, space)
- int idx;
- char *field;
- char *value;
- int flags;
- ENVELOPE *e;
- bool space;
-{
- HDR *h, *srch, *last = NULL;
-
- /* allocate space for new header */
- h = allocheader(field, value, flags, e->e_rpool, space);
-
- /* find insertion position */
- for (srch = e->e_header; srch != NULL && idx > 0;
- srch = srch->h_link, idx--)
- last = srch;
-
- if (e->e_header == NULL)
- {
- e->e_header = h;
- h->h_link = NULL;
- }
- else if (srch == NULL)
- {
- SM_ASSERT(last != NULL);
- last->h_link = h;
- h->h_link = NULL;
- }
- else
- {
- h->h_link = srch->h_link;
- srch->h_link = h;
- }
-}
-
-/*
-** HVALUE -- return value of a header.
-**
-** Only "real" fields (i.e., ones that have not been supplied
-** as a default) are used.
-**
-** Parameters:
-** field -- the field name.
-** header -- the header list.
-**
-** Returns:
-** pointer to the value part (internal format).
-** NULL if not found.
-**
-** Side Effects:
-** none.
-*/
-
-char *
-hvalue(field, header)
- char *field;
- HDR *header;
-{
- register HDR *h;
-
- for (h = header; h != NULL; h = h->h_link)
- {
- if (!bitset(H_DEFAULT, h->h_flags) &&
- sm_strcasecmp(h->h_field, field) == 0)
- return h->h_value;
- }
- return NULL;
-}
-
-/*
-** ISHEADER -- predicate telling if argument is a header.
-**
-** A line is a header if it has a single word followed by
-** optional white space followed by a colon.
-**
-** Header fields beginning with two dashes, although technically
-** permitted by RFC822, are automatically rejected in order
-** to make MIME work out. Without this we could have a technically
-** legal header such as ``--"foo:bar"'' that would also be a legal
-** MIME separator.
-**
-** Parameters:
-** h -- string to check for possible headerness.
-**
-** Returns:
-** true if h is a header.
-** false otherwise.
-**
-** Side Effects:
-** none.
-*/
-
-bool
-isheader(h)
- char *h;
-{
- char *s;
-
- s = h;
- if (s[0] == '-' && s[1] == '-')
- return false;
-
- while (*s > ' ' && *s != ':' && *s != '\0')
- s++;
-
- if (h == s)
- return false;
-
- /* following technically violates RFC822 */
- while (isascii(*s) && isspace(*s))
- s++;
-
- return (*s == ':');
-}
-
-/*
-** EATHEADER -- run through the stored header and extract info.
-**
-** Parameters:
-** e -- the envelope to process.
-** full -- if set, do full processing (e.g., compute
-** message priority). This should not be set
-** when reading a queue file because some info
-** needed to compute the priority is wrong.
-** log -- call logsender()?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets a bunch of global variables from information
-** in the collected header.
-*/
-
-void
-eatheader(e, full, log)
- register ENVELOPE *e;
- bool full;
- bool log;
-{
- register HDR *h;
- register char *p;
- int hopcnt = 0;
- char buf[MAXLINE];
-
- /*
- ** Set up macros for possible expansion in headers.
- */
-
- macdefine(&e->e_macro, A_PERM, 'f', e->e_sender);
- macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
- if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0')
- macdefine(&e->e_macro, A_PERM, 'u', e->e_origrcpt);
- else
- macdefine(&e->e_macro, A_PERM, 'u', NULL);
-
- /* full name of from person */
- p = hvalue("full-name", e->e_header);
- if (p != NULL)
- {
- if (!rfc822_string(p))
- {
- /*
- ** Quote a full name with special characters
- ** as a comment so crackaddr() doesn't destroy
- ** the name portion of the address.
- */
-
- p = addquotes(p, e->e_rpool);
- }
- macdefine(&e->e_macro, A_PERM, 'x', p);
- }
-
- if (tTd(32, 1))
- sm_dprintf("----- collected header -----\n");
- e->e_msgid = NULL;
- for (h = e->e_header; h != NULL; h = h->h_link)
- {
- if (tTd(32, 1))
- sm_dprintf("%s:", h->h_field);
- if (h->h_value == NULL)
- {
- if (tTd(32, 1))
- sm_dprintf("<NULL>\n");
- continue;
- }
-
- /* do early binding */
- if (bitset(H_DEFAULT, h->h_flags) &&
- !bitset(H_BINDLATE, h->h_flags))
- {
- if (tTd(32, 1))
- {
- sm_dprintf("(");
- xputs(sm_debug_file(), h->h_value);
- sm_dprintf(") ");
- }
- expand(h->h_value, buf, sizeof(buf), e);
- if (buf[0] != '\0' &&
- (buf[0] != ' ' || buf[1] != '\0'))
- {
- if (bitset(H_FROM, h->h_flags))
- expand(crackaddr(buf, e),
- buf, sizeof(buf), e);
- h->h_value = sm_rpool_strdup_x(e->e_rpool, buf);
- h->h_flags &= ~H_DEFAULT;
- }
- }
- if (tTd(32, 1))
- {
- xputs(sm_debug_file(), h->h_value);
- sm_dprintf("\n");
- }
-
- /* count the number of times it has been processed */
- if (bitset(H_TRACE, h->h_flags))
- hopcnt++;
-
- /* send to this person if we so desire */
- if (GrabTo && bitset(H_RCPT, h->h_flags) &&
- !bitset(H_DEFAULT, h->h_flags) &&
- (!bitset(EF_RESENT, e->e_flags) ||
- bitset(H_RESENT, h->h_flags)))
- {
-#if 0
- int saveflags = e->e_flags;
-#endif /* 0 */
-
- (void) sendtolist(denlstring(h->h_value, true, false),
- NULLADDR, &e->e_sendqueue, 0, e);
-
-#if 0
- /*
- ** Change functionality so a fatal error on an
- ** address doesn't affect the entire envelope.
- */
-
- /* delete fatal errors generated by this address */
- if (!bitset(EF_FATALERRS, saveflags))
- e->e_flags &= ~EF_FATALERRS;
-#endif /* 0 */
- }
-
- /* save the message-id for logging */
- p = "resent-message-id";
- if (!bitset(EF_RESENT, e->e_flags))
- p += 7;
- if (sm_strcasecmp(h->h_field, p) == 0)
- {
- e->e_msgid = h->h_value;
- while (isascii(*e->e_msgid) && isspace(*e->e_msgid))
- e->e_msgid++;
- macdefine(&e->e_macro, A_PERM, macid("{msg_id}"),
- e->e_msgid);
- }
- }
- if (tTd(32, 1))
- sm_dprintf("----------------------------\n");
-
- /* if we are just verifying (that is, sendmail -t -bv), drop out now */
- if (OpMode == MD_VERIFY)
- return;
-
- /* store hop count */
- if (hopcnt > e->e_hopcount)
- {
- e->e_hopcount = hopcnt;
- (void) sm_snprintf(buf, sizeof(buf), "%d", e->e_hopcount);
- macdefine(&e->e_macro, A_TEMP, 'c', buf);
- }
-
- /* message priority */
- p = hvalue("precedence", e->e_header);
- if (p != NULL)
- e->e_class = priencode(p);
- if (e->e_class < 0)
- e->e_timeoutclass = TOC_NONURGENT;
- else if (e->e_class > 0)
- e->e_timeoutclass = TOC_URGENT;
- if (full)
- {
- e->e_msgpriority = e->e_msgsize
- - e->e_class * WkClassFact
- + e->e_nrcpts * WkRecipFact;
- }
-
- /* check for DSN to properly set e_timeoutclass */
- p = hvalue("content-type", e->e_header);
- if (p != NULL)
- {
- bool oldsupr;
- char **pvp;
- char pvpbuf[MAXLINE];
- extern unsigned char MimeTokenTab[256];
-
- /* tokenize header */
- oldsupr = SuprErrs;
- SuprErrs = true;
- pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL,
- MimeTokenTab, false);
- SuprErrs = oldsupr;
-
- /* Check if multipart/report */
- if (pvp != NULL && pvp[0] != NULL &&
- pvp[1] != NULL && pvp[2] != NULL &&
- sm_strcasecmp(*pvp++, "multipart") == 0 &&
- strcmp(*pvp++, "/") == 0 &&
- sm_strcasecmp(*pvp++, "report") == 0)
- {
- /* Look for report-type=delivery-status */
- while (*pvp != NULL)
- {
- /* skip to semicolon separator */
- while (*pvp != NULL && strcmp(*pvp, ";") != 0)
- pvp++;
-
- /* skip semicolon */
- if (*pvp++ == NULL || *pvp == NULL)
- break;
-
- /* look for report-type */
- if (sm_strcasecmp(*pvp++, "report-type") != 0)
- continue;
-
- /* skip equal */
- if (*pvp == NULL || strcmp(*pvp, "=") != 0)
- continue;
-
- /* check value */
- if (*++pvp != NULL &&
- sm_strcasecmp(*pvp,
- "delivery-status") == 0)
- e->e_timeoutclass = TOC_DSN;
-
- /* found report-type, no need to continue */
- break;
- }
- }
- }
-
- /* message timeout priority */
- p = hvalue("priority", e->e_header);
- if (p != NULL)
- {
- /* (this should be in the configuration file) */
- if (sm_strcasecmp(p, "urgent") == 0)
- e->e_timeoutclass = TOC_URGENT;
- else if (sm_strcasecmp(p, "normal") == 0)
- e->e_timeoutclass = TOC_NORMAL;
- else if (sm_strcasecmp(p, "non-urgent") == 0)
- e->e_timeoutclass = TOC_NONURGENT;
- else if (bitset(EF_RESPONSE, e->e_flags))
- e->e_timeoutclass = TOC_DSN;
- }
- else if (bitset(EF_RESPONSE, e->e_flags))
- e->e_timeoutclass = TOC_DSN;
-
- /* date message originated */
- p = hvalue("posted-date", e->e_header);
- if (p == NULL)
- p = hvalue("date", e->e_header);
- if (p != NULL)
- macdefine(&e->e_macro, A_PERM, 'a', p);
-
- /* check to see if this is a MIME message */
- if ((e->e_bodytype != NULL &&
- sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0) ||
- hvalue("MIME-Version", e->e_header) != NULL)
- {
- e->e_flags |= EF_IS_MIME;
- if (HasEightBits)
- e->e_bodytype = "8BITMIME";
- }
- else if ((p = hvalue("Content-Type", e->e_header)) != NULL)
- {
- /* this may be an RFC 1049 message */
- p = strpbrk(p, ";/");
- if (p == NULL || *p == ';')
- {
- /* yep, it is */
- e->e_flags |= EF_DONT_MIME;
- }
- }
-
- /*
- ** From person in antiquated ARPANET mode
- ** required by UK Grey Book e-mail gateways (sigh)
- */
-
- if (OpMode == MD_ARPAFTP)
- {
- register struct hdrinfo *hi;
-
- for (hi = HdrInfo; hi->hi_field != NULL; hi++)
- {
- if (bitset(H_FROM, hi->hi_flags) &&
- (!bitset(H_RESENT, hi->hi_flags) ||
- bitset(EF_RESENT, e->e_flags)) &&
- (p = hvalue(hi->hi_field, e->e_header)) != NULL)
- break;
- }
- if (hi->hi_field != NULL)
- {
- if (tTd(32, 2))
- sm_dprintf("eatheader: setsender(*%s == %s)\n",
- hi->hi_field, p);
- setsender(p, e, NULL, '\0', true);
- }
- }
-
- /*
- ** Log collection information.
- */
-
- if (log && bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4)
- {
- logsender(e, e->e_msgid);
- e->e_flags &= ~EF_LOGSENDER;
- }
-}
-
-/*
-** LOGSENDER -- log sender information
-**
-** Parameters:
-** e -- the envelope to log
-** msgid -- the message id
-**
-** Returns:
-** none
-*/
-
-void
-logsender(e, msgid)
- register ENVELOPE *e;
- char *msgid;
-{
- char *name;
- register char *sbp;
- register char *p;
- char hbuf[MAXNAME + 1];
- char sbuf[MAXLINE + 1];
- char mbuf[MAXNAME + 1];
-
- /* don't allow newlines in the message-id */
- /* XXX do we still need this? sm_syslog() replaces control chars */
- if (msgid != NULL)
- {
- size_t l;
-
- l = strlen(msgid);
- if (l > sizeof(mbuf) - 1)
- l = sizeof(mbuf) - 1;
- memmove(mbuf, msgid, l);
- mbuf[l] = '\0';
- p = mbuf;
- while ((p = strchr(p, '\n')) != NULL)
- *p++ = ' ';
- }
-
- if (bitset(EF_RESPONSE, e->e_flags))
- name = "[RESPONSE]";
- else if ((name = macvalue('_', e)) != NULL)
- /* EMPTY */
- ;
- else if (RealHostName == NULL)
- name = "localhost";
- else if (RealHostName[0] == '[')
- name = RealHostName;
- else
- {
- name = hbuf;
- (void) sm_snprintf(hbuf, sizeof(hbuf), "%.80s", RealHostName);
- if (RealHostAddr.sa.sa_family != 0)
- {
- p = &hbuf[strlen(hbuf)];
- (void) sm_snprintf(p, SPACELEFT(hbuf, p),
- " (%.100s)",
- anynet_ntoa(&RealHostAddr));
- }
- }
-
- /* some versions of syslog only take 5 printf args */
-#if (SYSLOG_BUFSIZE) >= 256
- sbp = sbuf;
- (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
- "from=%.200s, size=%ld, class=%d, nrcpts=%d",
- e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr,
- e->e_msgsize, e->e_class, e->e_nrcpts);
- sbp += strlen(sbp);
- if (msgid != NULL)
- {
- (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
- ", msgid=%.100s", mbuf);
- sbp += strlen(sbp);
- }
- if (e->e_bodytype != NULL)
- {
- (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
- ", bodytype=%.20s", e->e_bodytype);
- sbp += strlen(sbp);
- }
- p = macvalue('r', e);
- if (p != NULL)
- {
- (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
- ", proto=%.20s", p);
- sbp += strlen(sbp);
- }
- p = macvalue(macid("{daemon_name}"), e);
- if (p != NULL)
- {
- (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
- ", daemon=%.20s", p);
- sbp += strlen(sbp);
- }
- sm_syslog(LOG_INFO, e->e_id, "%.850s, relay=%s", sbuf, name);
-
-#else /* (SYSLOG_BUFSIZE) >= 256 */
-
- sm_syslog(LOG_INFO, e->e_id,
- "from=%s",
- e->e_from.q_paddr == NULL ? "<NONE>"
- : shortenstring(e->e_from.q_paddr,
- 83));
- sm_syslog(LOG_INFO, e->e_id,
- "size=%ld, class=%ld, nrcpts=%d",
- e->e_msgsize, e->e_class, e->e_nrcpts);
- if (msgid != NULL)
- sm_syslog(LOG_INFO, e->e_id,
- "msgid=%s",
- shortenstring(mbuf, 83));
- sbp = sbuf;
- *sbp = '\0';
- if (e->e_bodytype != NULL)
- {
- (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
- "bodytype=%.20s, ", e->e_bodytype);
- sbp += strlen(sbp);
- }
- p = macvalue('r', e);
- if (p != NULL)
- {
- (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp),
- "proto=%.20s, ", p);
- sbp += strlen(sbp);
- }
- sm_syslog(LOG_INFO, e->e_id,
- "%.400srelay=%s", sbuf, name);
-#endif /* (SYSLOG_BUFSIZE) >= 256 */
-}
-
-/*
-** PRIENCODE -- encode external priority names into internal values.
-**
-** Parameters:
-** p -- priority in ascii.
-**
-** Returns:
-** priority as a numeric level.
-**
-** Side Effects:
-** none.
-*/
-
-static int
-priencode(p)
- char *p;
-{
- register int i;
-
- for (i = 0; i < NumPriorities; i++)
- {
- if (sm_strcasecmp(p, Priorities[i].pri_name) == 0)
- return Priorities[i].pri_val;
- }
-
- /* unknown priority */
- return 0;
-}
-
-/*
-** CRACKADDR -- parse an address and turn it into a macro
-**
-** This doesn't actually parse the address -- it just extracts
-** it and replaces it with "$g". The parse is totally ad hoc
-** and isn't even guaranteed to leave something syntactically
-** identical to what it started with. However, it does leave
-** something semantically identical if possible, else at least
-** syntactically correct.
-**
-** For example, it changes "Real Name <real@example.com> (Comment)"
-** to "Real Name <$g> (Comment)".
-**
-** This algorithm has been cleaned up to handle a wider range
-** of cases -- notably quoted and backslash escaped strings.
-** This modification makes it substantially better at preserving
-** the original syntax.
-**
-** Parameters:
-** addr -- the address to be cracked.
-** e -- the current envelope.
-**
-** Returns:
-** a pointer to the new version.
-**
-** Side Effects:
-** none.
-**
-** Warning:
-** The return value is saved in local storage and should
-** be copied if it is to be reused.
-*/
-
-#define SM_HAVE_ROOM ((bp < buflim) && (buflim <= bufend))
-
-/*
-** Append a character to bp if we have room.
-** If not, punt and return $g.
-*/
-
-#define SM_APPEND_CHAR(c) \
- do \
- { \
- if (SM_HAVE_ROOM) \
- *bp++ = (c); \
- else \
- goto returng; \
- } while (0)
-
-#if MAXNAME < 10
-ERROR MAXNAME must be at least 10
-#endif /* MAXNAME < 10 */
-
-char *
-crackaddr(addr, e)
- register char *addr;
- ENVELOPE *e;
-{
- register char *p;
- register char c;
- int cmtlev; /* comment level in input string */
- int realcmtlev; /* comment level in output string */
- int anglelev; /* angle level in input string */
- int copylev; /* 0 == in address, >0 copying */
- int bracklev; /* bracket level for IPv6 addr check */
- bool addangle; /* put closing angle in output */
- bool qmode; /* quoting in original string? */
- bool realqmode; /* quoting in output string? */
- bool putgmac = false; /* already wrote $g */
- bool quoteit = false; /* need to quote next character */
- bool gotangle = false; /* found first '<' */
- bool gotcolon = false; /* found a ':' */
- register char *bp;
- char *buflim;
- char *bufhead;
- char *addrhead;
- char *bufend;
- static char buf[MAXNAME + 1];
-
- if (tTd(33, 1))
- sm_dprintf("crackaddr(%s)\n", addr);
-
- buflim = bufend = &buf[sizeof(buf) - 1];
- bp = bufhead = buf;
-
- /* skip over leading spaces but preserve them */
- while (*addr != '\0' && isascii(*addr) && isspace(*addr))
- {
- SM_APPEND_CHAR(*addr);
- addr++;
- }
- bufhead = bp;
-
- /*
- ** Start by assuming we have no angle brackets. This will be
- ** adjusted later if we find them.
- */
-
- p = addrhead = addr;
- copylev = anglelev = cmtlev = realcmtlev = 0;
- bracklev = 0;
- qmode = realqmode = addangle = false;
-
- while ((c = *p++) != '\0')
- {
- /*
- ** Try to keep legal syntax using spare buffer space
- ** (maintained by buflim).
- */
-
- if (copylev > 0)
- SM_APPEND_CHAR(c);
-
- /* check for backslash escapes */
- if (c == '\\')
- {
- /* arrange to quote the address */
- if (cmtlev <= 0 && !qmode)
- quoteit = true;
-
- if ((c = *p++) == '\0')
- {
- /* too far */
- p--;
- goto putg;
- }
- if (copylev > 0)
- SM_APPEND_CHAR(c);
- goto putg;
- }
-
- /* check for quoted strings */
- if (c == '"' && cmtlev <= 0)
- {
- qmode = !qmode;
- if (copylev > 0 && SM_HAVE_ROOM)
- {
- if (realqmode)
- buflim--;
- else
- buflim++;
- realqmode = !realqmode;
- }
- continue;
- }
- if (qmode)
- goto putg;
-
- /* check for comments */
- if (c == '(')
- {
- cmtlev++;
-
- /* allow space for closing paren */
- if (SM_HAVE_ROOM)
- {
- buflim--;
- realcmtlev++;
- if (copylev++ <= 0)
- {
- if (bp != bufhead)
- SM_APPEND_CHAR(' ');
- SM_APPEND_CHAR(c);
- }
- }
- }
- if (cmtlev > 0)
- {
- if (c == ')')
- {
- cmtlev--;
- copylev--;
- if (SM_HAVE_ROOM)
- {
- realcmtlev--;
- buflim++;
- }
- }
- continue;
- }
- else if (c == ')')
- {
- /* syntax error: unmatched ) */
- if (copylev > 0 && SM_HAVE_ROOM && bp > bufhead)
- bp--;
- }
-
- /* count nesting on [ ... ] (for IPv6 domain literals) */
- if (c == '[')
- bracklev++;
- else if (c == ']')
- bracklev--;
-
- /* check for group: list; syntax */
- if (c == ':' && anglelev <= 0 && bracklev <= 0 &&
- !gotcolon && !ColonOkInAddr)
- {
- register char *q;
-
- /*
- ** Check for DECnet phase IV ``::'' (host::user)
- ** or DECnet phase V ``:.'' syntaxes. The latter
- ** covers ``user@DEC:.tay.myhost'' and
- ** ``DEC:.tay.myhost::user'' syntaxes (bletch).
- */
-
- if (*p == ':' || *p == '.')
- {
- if (cmtlev <= 0 && !qmode)
- quoteit = true;
- if (copylev > 0)
- {
- SM_APPEND_CHAR(c);
- SM_APPEND_CHAR(*p);
- }
- p++;
- goto putg;
- }
-
- gotcolon = true;
-
- bp = bufhead;
- if (quoteit)
- {
- SM_APPEND_CHAR('"');
-
- /* back up over the ':' and any spaces */
- --p;
- while (p > addr &&
- isascii(*--p) && isspace(*p))
- continue;
- p++;
- }
- for (q = addrhead; q < p; )
- {
- c = *q++;
- if (quoteit && c == '"')
- SM_APPEND_CHAR('\\');
- SM_APPEND_CHAR(c);
- }
- if (quoteit)
- {
- if (bp == &bufhead[1])
- bp--;
- else
- SM_APPEND_CHAR('"');
- while ((c = *p++) != ':')
- SM_APPEND_CHAR(c);
- SM_APPEND_CHAR(c);
- }
-
- /* any trailing white space is part of group: */
- while (isascii(*p) && isspace(*p))
- {
- SM_APPEND_CHAR(*p);
- p++;
- }
- copylev = 0;
- putgmac = quoteit = false;
- bufhead = bp;
- addrhead = p;
- continue;
- }
-
- if (c == ';' && copylev <= 0 && !ColonOkInAddr)
- SM_APPEND_CHAR(c);
-
- /* check for characters that may have to be quoted */
- if (strchr(MustQuoteChars, c) != NULL)
- {
- /*
- ** If these occur as the phrase part of a <>
- ** construct, but are not inside of () or already
- ** quoted, they will have to be quoted. Note that
- ** now (but don't actually do the quoting).
- */
-
- if (cmtlev <= 0 && !qmode)
- quoteit = true;
- }
-
- /* check for angle brackets */
- if (c == '<')
- {
- register char *q;
-
- /* assume first of two angles is bogus */
- if (gotangle)
- quoteit = true;
- gotangle = true;
-
- /* oops -- have to change our mind */
- anglelev = 1;
- if (SM_HAVE_ROOM)
- {
- if (!addangle)
- buflim--;
- addangle = true;
- }
-
- bp = bufhead;
- if (quoteit)
- {
- SM_APPEND_CHAR('"');
-
- /* back up over the '<' and any spaces */
- --p;
- while (p > addr &&
- isascii(*--p) && isspace(*p))
- continue;
- p++;
- }
- for (q = addrhead; q < p; )
- {
- c = *q++;
- if (quoteit && c == '"')
- {
- SM_APPEND_CHAR('\\');
- SM_APPEND_CHAR(c);
- }
- else
- SM_APPEND_CHAR(c);
- }
- if (quoteit)
- {
- if (bp == &buf[1])
- bp--;
- else
- SM_APPEND_CHAR('"');
- while ((c = *p++) != '<')
- SM_APPEND_CHAR(c);
- SM_APPEND_CHAR(c);
- }
- copylev = 0;
- putgmac = quoteit = false;
- continue;
- }
-
- if (c == '>')
- {
- if (anglelev > 0)
- {
- anglelev--;
- if (SM_HAVE_ROOM)
- {
- if (addangle)
- buflim++;
- addangle = false;
- }
- }
- else if (SM_HAVE_ROOM)
- {
- /* syntax error: unmatched > */
- if (copylev > 0 && bp > bufhead)
- bp--;
- quoteit = true;
- continue;
- }
- if (copylev++ <= 0)
- SM_APPEND_CHAR(c);
- continue;
- }
-
- /* must be a real address character */
- putg:
- if (copylev <= 0 && !putgmac)
- {
- if (bp > buf && bp[-1] == ')')
- SM_APPEND_CHAR(' ');
- SM_APPEND_CHAR(MACROEXPAND);
- SM_APPEND_CHAR('g');
- putgmac = true;
- }
- }
-
- /* repair any syntactic damage */
- if (realqmode && bp < bufend)
- *bp++ = '"';
- while (realcmtlev-- > 0 && bp < bufend)
- *bp++ = ')';
- if (addangle && bp < bufend)
- *bp++ = '>';
- *bp = '\0';
- if (bp < bufend)
- goto success;
-
- returng:
- /* String too long, punt */
- buf[0] = '<';
- buf[1] = MACROEXPAND;
- buf[2]= 'g';
- buf[3] = '>';
- buf[4]= '\0';
- sm_syslog(LOG_ALERT, e->e_id,
- "Dropped invalid comments from header address");
-
- success:
- if (tTd(33, 1))
- {
- sm_dprintf("crackaddr=>`");
- xputs(sm_debug_file(), buf);
- sm_dprintf("'\n");
- }
- return buf;
-}
-
-/*
-** PUTHEADER -- put the header part of a message from the in-core copy
-**
-** Parameters:
-** mci -- the connection information.
-** hdr -- the header to put.
-** e -- envelope to use.
-** flags -- MIME conversion flags.
-**
-** Returns:
-** true iff header part was written successfully
-**
-** Side Effects:
-** none.
-*/
-
-bool
-putheader(mci, hdr, e, flags)
- register MCI *mci;
- HDR *hdr;
- register ENVELOPE *e;
- int flags;
-{
- register HDR *h;
- char buf[SM_MAX(MAXLINE,BUFSIZ)];
- char obuf[MAXLINE];
-
- if (tTd(34, 1))
- sm_dprintf("--- putheader, mailer = %s ---\n",
- mci->mci_mailer->m_name);
-
- /*
- ** If we're in MIME mode, we're not really in the header of the
- ** message, just the header of one of the parts of the body of
- ** the message. Therefore MCIF_INHEADER should not be turned on.
- */
-
- if (!bitset(MCIF_INMIME, mci->mci_flags))
- mci->mci_flags |= MCIF_INHEADER;
-
- for (h = hdr; h != NULL; h = h->h_link)
- {
- register char *p = h->h_value;
- char *q;
-
- if (tTd(34, 11))
- {
- sm_dprintf(" %s:", h->h_field);
- xputs(sm_debug_file(), p);
- }
-
- /* Skip empty headers */
- if (h->h_value == NULL)
- continue;
-
- /* heuristic shortening of MIME fields to avoid MUA overflows */
- if (MaxMimeFieldLength > 0 &&
- wordinclass(h->h_field,
- macid("{checkMIMEFieldHeaders}")))
- {
- size_t len;
-
- len = fix_mime_header(h, e);
- if (len > 0)
- {
- sm_syslog(LOG_ALERT, e->e_id,
- "Truncated MIME %s header due to field size (length = %ld) (possible attack)",
- h->h_field, (unsigned long) len);
- if (tTd(34, 11))
- sm_dprintf(" truncated MIME %s header due to field size (length = %ld) (possible attack)\n",
- h->h_field,
- (unsigned long) len);
- }
- }
-
- if (MaxMimeHeaderLength > 0 &&
- wordinclass(h->h_field,
- macid("{checkMIMETextHeaders}")))
- {
- size_t len;
-
- len = strlen(h->h_value);
- if (len > (size_t) MaxMimeHeaderLength)
- {
- h->h_value[MaxMimeHeaderLength - 1] = '\0';
- sm_syslog(LOG_ALERT, e->e_id,
- "Truncated long MIME %s header (length = %ld) (possible attack)",
- h->h_field, (unsigned long) len);
- if (tTd(34, 11))
- sm_dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n",
- h->h_field,
- (unsigned long) len);
- }
- }
-
- if (MaxMimeHeaderLength > 0 &&
- wordinclass(h->h_field,
- macid("{checkMIMEHeaders}")))
- {
- size_t len;
-
- len = strlen(h->h_value);
- if (shorten_rfc822_string(h->h_value,
- MaxMimeHeaderLength))
- {
- if (len < MaxMimeHeaderLength)
- {
- /* we only rebalanced a bogus header */
- sm_syslog(LOG_ALERT, e->e_id,
- "Fixed MIME %s header (possible attack)",
- h->h_field);
- if (tTd(34, 11))
- sm_dprintf(" fixed MIME %s header (possible attack)\n",
- h->h_field);
- }
- else
- {
- /* we actually shortened header */
- sm_syslog(LOG_ALERT, e->e_id,
- "Truncated long MIME %s header (length = %ld) (possible attack)",
- h->h_field,
- (unsigned long) len);
- if (tTd(34, 11))
- sm_dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n",
- h->h_field,
- (unsigned long) len);
- }
- }
- }
-
- /*
- ** Suppress Content-Transfer-Encoding: if we are MIMEing
- ** and we are potentially converting from 8 bit to 7 bit
- ** MIME. If converting, add a new CTE header in
- ** mime8to7().
- */
-
- if (bitset(H_CTE, h->h_flags) &&
- bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME,
- mci->mci_flags) &&
- !bitset(M87F_NO8TO7, flags))
- {
- if (tTd(34, 11))
- sm_dprintf(" (skipped (content-transfer-encoding))\n");
- continue;
- }
-
- if (bitset(MCIF_INMIME, mci->mci_flags))
- {
- if (tTd(34, 11))
- sm_dprintf("\n");
- if (!put_vanilla_header(h, p, mci))
- goto writeerr;
- continue;
- }
-
- if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
- !bitintersect(h->h_mflags, mci->mci_mailer->m_flags) &&
- (h->h_macro == '\0' ||
- (q = macvalue(bitidx(h->h_macro), e)) == NULL ||
- *q == '\0'))
- {
- if (tTd(34, 11))
- sm_dprintf(" (skipped)\n");
- continue;
- }
-
- /* handle Resent-... headers specially */
- if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
- {
- if (tTd(34, 11))
- sm_dprintf(" (skipped (resent))\n");
- continue;
- }
-
- /* suppress return receipts if requested */
- if (bitset(H_RECEIPTTO, h->h_flags) &&
- (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags)))
- {
- if (tTd(34, 11))
- sm_dprintf(" (skipped (receipt))\n");
- continue;
- }
-
- /* macro expand value if generated internally */
- if (bitset(H_DEFAULT, h->h_flags) ||
- bitset(H_BINDLATE, h->h_flags))
- {
- expand(p, buf, sizeof(buf), e);
- p = buf;
- if (*p == '\0')
- {
- if (tTd(34, 11))
- sm_dprintf(" (skipped -- null value)\n");
- continue;
- }
- }
-
- if (bitset(H_BCC, h->h_flags))
- {
- /* Bcc: field -- either truncate or delete */
- if (bitset(EF_DELETE_BCC, e->e_flags))
- {
- if (tTd(34, 11))
- sm_dprintf(" (skipped -- bcc)\n");
- }
- else
- {
- /* no other recipient headers: truncate value */
- (void) sm_strlcpyn(obuf, sizeof(obuf), 2,
- h->h_field, ":");
- if (!putline(obuf, mci))
- goto writeerr;
- }
- continue;
- }
-
- if (tTd(34, 11))
- sm_dprintf("\n");
-
- if (bitset(H_FROM|H_RCPT, h->h_flags))
- {
- /* address field */
- bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
-
- if (bitset(H_FROM, h->h_flags))
- oldstyle = false;
- commaize(h, p, oldstyle, mci, e,
- PXLF_HEADER | PXLF_STRIPMQUOTE);
- }
- else
- {
- if (!put_vanilla_header(h, p, mci))
- goto writeerr;
- }
- }
-
- /*
- ** If we are converting this to a MIME message, add the
- ** MIME headers (but not in MIME mode!).
- */
-
-#if MIME8TO7
- if (bitset(MM_MIME8BIT, MimeMode) &&
- bitset(EF_HAS8BIT, e->e_flags) &&
- !bitset(EF_DONT_MIME, e->e_flags) &&
- !bitnset(M_8BITS, mci->mci_mailer->m_flags) &&
- !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags) &&
- hvalue("MIME-Version", e->e_header) == NULL)
- {
- if (!putline("MIME-Version: 1.0", mci))
- goto writeerr;
- if (hvalue("Content-Type", e->e_header) == NULL)
- {
- (void) sm_snprintf(obuf, sizeof(obuf),
- "Content-Type: text/plain; charset=%s",
- defcharset(e));
- if (!putline(obuf, mci))
- goto writeerr;
- }
- if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL
- && !putline("Content-Transfer-Encoding: 8bit", mci))
- goto writeerr;
- }
-#endif /* MIME8TO7 */
- return true;
-
- writeerr:
- return false;
-}
-
-/*
-** PUT_VANILLA_HEADER -- output a fairly ordinary header
-**
-** Parameters:
-** h -- the structure describing this header
-** v -- the value of this header
-** mci -- the connection info for output
-**
-** Returns:
-** true iff header was written successfully
-*/
-
-static bool
-put_vanilla_header(h, v, mci)
- HDR *h;
- char *v;
- MCI *mci;
-{
- register char *nlp;
- register char *obp;
- int putflags;
- char obuf[MAXLINE + 256]; /* additional length for h_field */
-
- putflags = PXLF_HEADER | PXLF_STRIPMQUOTE;
- if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
- putflags |= PXLF_STRIP8BIT;
- (void) sm_snprintf(obuf, sizeof(obuf), "%.200s:", h->h_field);
- obp = obuf + strlen(obuf);
- while ((nlp = strchr(v, '\n')) != NULL)
- {
- int l;
-
- l = nlp - v;
-
- /*
- ** XXX This is broken for SPACELEFT()==0
- ** However, SPACELEFT() is always > 0 unless MAXLINE==1.
- */
-
- if (SPACELEFT(obuf, obp) - 1 < (size_t) l)
- l = SPACELEFT(obuf, obp) - 1;
-
- (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v);
- if (!putxline(obuf, strlen(obuf), mci, putflags))
- goto writeerr;
- v += l + 1;
- obp = obuf;
- if (*v != ' ' && *v != '\t')
- *obp++ = ' ';
- }
-
- /* XXX This is broken for SPACELEFT()==0 */
- (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s",
- (int) (SPACELEFT(obuf, obp) - 1), v);
- return putxline(obuf, strlen(obuf), mci, putflags);
-
- writeerr:
- return false;
-}
-
-/*
-** COMMAIZE -- output a header field, making a comma-translated list.
-**
-** Parameters:
-** h -- the header field to output.
-** p -- the value to put in it.
-** oldstyle -- true if this is an old style header.
-** mci -- the connection information.
-** e -- the envelope containing the message.
-** putflags -- flags for putxline()
-**
-** Returns:
-** true iff header field was written successfully
-**
-** Side Effects:
-** outputs "p" to "mci".
-*/
-
-bool
-commaize(h, p, oldstyle, mci, e, putflags)
- register HDR *h;
- register char *p;
- bool oldstyle;
- register MCI *mci;
- register ENVELOPE *e;
- int putflags;
-{
- register char *obp;
- int opos, omax, spaces;
- bool firstone = true;
- char **res;
- char obuf[MAXLINE + 3];
-
- /*
- ** Output the address list translated by the
- ** mailer and with commas.
- */
-
- if (tTd(14, 2))
- sm_dprintf("commaize(%s:%s)\n", h->h_field, p);
-
- if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
- putflags |= PXLF_STRIP8BIT;
-
- obp = obuf;
- (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.200s:", h->h_field);
- /* opos = strlen(obp); instead of the next 3 lines? */
- opos = strlen(h->h_field) + 1;
- if (opos > 201)
- opos = 201;
- obp += opos;
-
- spaces = 0;
- while (*p != '\0' && isascii(*p) && isspace(*p))
- {
- ++spaces;
- ++p;
- }
- if (spaces > 0)
- {
- SM_ASSERT(sizeof(obuf) > opos * 2);
-
- /*
- ** Restrict number of spaces to half the length of buffer
- ** so the header field body can be put in here too.
- ** Note: this is a hack...
- */
-
- if (spaces > sizeof(obuf) / 2)
- spaces = sizeof(obuf) / 2;
- (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%*s", spaces,
- "");
- opos += spaces;
- obp += spaces;
- SM_ASSERT(obp < &obuf[MAXLINE]);
- }
-
- omax = mci->mci_mailer->m_linelimit - 2;
- if (omax < 0 || omax > 78)
- omax = 78;
-
- /*
- ** Run through the list of values.
- */
-
- while (*p != '\0')
- {
- register char *name;
- register int c;
- char savechar;
- int flags;
- auto int status;
-
- /*
- ** Find the end of the name. New style names
- ** end with a comma, old style names end with
- ** a space character. However, spaces do not
- ** necessarily delimit an old-style name -- at
- ** signs mean keep going.
- */
-
- /* find end of name */
- while ((isascii(*p) && isspace(*p)) || *p == ',')
- p++;
- name = p;
- res = NULL;
- for (;;)
- {
- auto char *oldp;
- char pvpbuf[PSBUFSIZE];
-
- res = prescan(p, oldstyle ? ' ' : ',', pvpbuf,
- sizeof(pvpbuf), &oldp, ExtTokenTab, false);
- p = oldp;
-#if _FFR_IGNORE_BOGUS_ADDR
- /* ignore addresses that can't be parsed */
- if (res == NULL)
- {
- name = p;
- continue;
- }
-#endif /* _FFR_IGNORE_BOGUS_ADDR */
-
- /* look to see if we have an at sign */
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
-
- if (*p != '@')
- {
- p = oldp;
- break;
- }
- ++p;
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
- }
- /* at the end of one complete name */
-
- /* strip off trailing white space */
- while (p >= name &&
- ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0'))
- p--;
- if (++p == name)
- continue;
-
- /*
- ** if prescan() failed go a bit backwards; this is a hack,
- ** there should be some better error recovery.
- */
-
- if (res == NULL && p > name &&
- !((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0'))
- --p;
- savechar = *p;
- *p = '\0';
-
- /* translate the name to be relative */
- flags = RF_HEADERADDR|RF_ADDDOMAIN;
- if (bitset(H_FROM, h->h_flags))
- flags |= RF_SENDERADDR;
-#if USERDB
- else if (e->e_from.q_mailer != NULL &&
- bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags))
- {
- char *q;
-
- q = udbsender(name, e->e_rpool);
- if (q != NULL)
- name = q;
- }
-#endif /* USERDB */
- status = EX_OK;
- name = remotename(name, mci->mci_mailer, flags, &status, e);
- if (*name == '\0')
- {
- *p = savechar;
- continue;
- }
- name = denlstring(name, false, true);
-
- /* output the name with nice formatting */
- opos += strlen(name);
- if (!firstone)
- opos += 2;
- if (opos > omax && !firstone)
- {
- (void) sm_strlcpy(obp, ",\n", SPACELEFT(obuf, obp));
- if (!putxline(obuf, strlen(obuf), mci, putflags))
- goto writeerr;
- obp = obuf;
- (void) sm_strlcpy(obp, " ", sizeof(obuf));
- opos = strlen(obp);
- obp += opos;
- opos += strlen(name);
- }
- else if (!firstone)
- {
- (void) sm_strlcpy(obp, ", ", SPACELEFT(obuf, obp));
- obp += 2;
- }
-
- while ((c = *name++) != '\0' && obp < &obuf[MAXLINE])
- *obp++ = c;
- firstone = false;
- *p = savechar;
- }
- if (obp < &obuf[sizeof(obuf)])
- *obp = '\0';
- else
- obuf[sizeof(obuf) - 1] = '\0';
- return putxline(obuf, strlen(obuf), mci, putflags);
-
- writeerr:
- return false;
-}
-
-/*
-** COPYHEADER -- copy header list
-**
-** This routine is the equivalent of newstr for header lists
-**
-** Parameters:
-** header -- list of header structures to copy.
-** rpool -- resource pool, or NULL
-**
-** Returns:
-** a copy of 'header'.
-**
-** Side Effects:
-** none.
-*/
-
-HDR *
-copyheader(header, rpool)
- register HDR *header;
- SM_RPOOL_T *rpool;
-{
- register HDR *newhdr;
- HDR *ret;
- register HDR **tail = &ret;
-
- while (header != NULL)
- {
- newhdr = (HDR *) sm_rpool_malloc_x(rpool, sizeof(*newhdr));
- STRUCTCOPY(*header, *newhdr);
- *tail = newhdr;
- tail = &newhdr->h_link;
- header = header->h_link;
- }
- *tail = NULL;
-
- return ret;
-}
-
-/*
-** FIX_MIME_HEADER -- possibly truncate/rebalance parameters in a MIME header
-**
-** Run through all of the parameters of a MIME header and
-** possibly truncate and rebalance the parameter according
-** to MaxMimeFieldLength.
-**
-** Parameters:
-** h -- the header to truncate/rebalance
-** e -- the current envelope
-**
-** Returns:
-** length of last offending field, 0 if all ok.
-**
-** Side Effects:
-** string modified in place
-*/
-
-static size_t
-fix_mime_header(h, e)
- HDR *h;
- ENVELOPE *e;
-{
- char *begin = h->h_value;
- char *end;
- size_t len = 0;
- size_t retlen = 0;
-
- if (begin == NULL || *begin == '\0')
- return 0;
-
- /* Split on each ';' */
- /* find_character() never returns NULL */
- while ((end = find_character(begin, ';')) != NULL)
- {
- char save = *end;
- char *bp;
-
- *end = '\0';
-
- len = strlen(begin);
-
- /* Shorten individual parameter */
- if (shorten_rfc822_string(begin, MaxMimeFieldLength))
- {
- if (len < MaxMimeFieldLength)
- {
- /* we only rebalanced a bogus field */
- sm_syslog(LOG_ALERT, e->e_id,
- "Fixed MIME %s header field (possible attack)",
- h->h_field);
- if (tTd(34, 11))
- sm_dprintf(" fixed MIME %s header field (possible attack)\n",
- h->h_field);
- }
- else
- {
- /* we actually shortened the header */
- retlen = len;
- }
- }
-
- /* Collapse the possibly shortened string with rest */
- bp = begin + strlen(begin);
- if (bp != end)
- {
- char *ep = end;
-
- *end = save;
- end = bp;
-
- /* copy character by character due to overlap */
- while (*ep != '\0')
- *bp++ = *ep++;
- *bp = '\0';
- }
- else
- *end = save;
- if (*end == '\0')
- break;
-
- /* Move past ';' */
- begin = end + 1;
- }
- return retlen;
-}
diff --git a/contrib/sendmail/src/helpfile b/contrib/sendmail/src/helpfile
deleted file mode 100644
index 941dc2a..0000000
--- a/contrib/sendmail/src/helpfile
+++ /dev/null
@@ -1,137 +0,0 @@
-#vers 2
-cpyr
-cpyr Copyright (c) 1998-2000, 2002, 2004-2007 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.48 2007/02/01 18:29:44 ca Exp $$
-cpyr
-smtp This is sendmail version $v
-smtp Topics:
-smtp HELO EHLO MAIL RCPT DATA
-smtp RSET NOOP QUIT HELP VRFY
-smtp EXPN VERB ETRN DSN AUTH
-smtp STARTTLS
-smtp For more info use "HELP <topic>".
-smtp To report bugs in the implementation see
-smtp http://www.sendmail.org/email-addresses.html
-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 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 ENHANCEDSTATUSCODES Enhanced status codes [RFC2034]
-ehlo DELIVERBY Deliver By [RFC2852]
-mail MAIL From:<sender> [ <parameters> ]
-mail Specifies the sender. Parameters are ESMTP extensions.
-mail See "HELP DSN" for details.
-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.
-control mstat Show sendmail status (machine readable format).
-control memdump Dump allocated memory list (for debugging only).
diff --git a/contrib/sendmail/src/macro.c b/contrib/sendmail/src/macro.c
deleted file mode 100644
index cdde4d2..0000000
--- a/contrib/sendmail/src/macro.c
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * Copyright (c) 1998-2001, 2003, 2006, 2007 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: macro.c,v 8.107 2007/08/06 22:29:02 ca Exp $")
-
-#include <sm/sendmail.h>
-#if MAXMACROID != (BITMAPBITS - 1)
- ERROR Read the comment in conf.h
-#endif /* MAXMACROID != (BITMAPBITS - 1) */
-
-static char *MacroName[MAXMACROID + 1]; /* macro id to name table */
-
-/*
-** Codes for long named macros.
-** See also macname():
- * if not ASCII printable, look up the name *
- if (n <= 0x20 || n > 0x7f)
-** First use 1 to NEXTMACROID_L, then use NEXTMACROID_H to MAXMACROID.
-*/
-
-#define NEXTMACROID_L 037
-#define NEXTMACROID_H 0240
-
-#if _FFR_MORE_MACROS
-/* table for next id in non-printable ASCII range: disallow some value */
-static int NextMIdTable[] =
-{
- /* 0 nul */ 1,
- /* 1 soh */ 2,
- /* 2 stx */ 3,
- /* 3 etx */ 4,
- /* 4 eot */ 5,
- /* 5 enq */ 6,
- /* 6 ack */ 7,
- /* 7 bel */ 8,
- /* 8 bs */ 14,
- /* 9 ht */ -1,
- /* 10 nl */ -1,
- /* 11 vt */ -1,
- /* 12 np */ -1,
- /* 13 cr */ -1,
- /* 14 so */ 15,
- /* 15 si */ 16,
- /* 16 dle */ 17,
- /* 17 dc1 */ 18,
- /* 18 dc2 */ 19,
- /* 19 dc3 */ 20,
- /* 20 dc4 */ 21,
- /* 21 nak */ 22,
- /* 22 syn */ 23,
- /* 23 etb */ 24,
- /* 24 can */ 25,
- /* 25 em */ 26,
- /* 26 sub */ 27,
- /* 27 esc */ 28,
- /* 28 fs */ 29,
- /* 29 gs */ 30,
- /* 30 rs */ 31,
- /* 31 us */ 32,
- /* 32 sp */ -1,
-};
-
-#define NEXTMACROID(mid) ( \
- (mid < NEXTMACROID_L) ? (NextMIdTable[mid]) : \
- ((mid < NEXTMACROID_H) ? NEXTMACROID_H : (mid + 1)))
-
-int NextMacroId = 1; /* codes for long named macros */
-/* see sendmail.h: Special characters in rewriting rules. */
-#else /* _FFR_MORE_MACROS */
-int NextMacroId = 0240; /* codes for long named macros */
-#define NEXTMACROID(mid) ((mid) + 1)
-#endif /* _FFR_MORE_MACROS */
-
-
-/*
-** INITMACROS -- initialize the macro system
-**
-** This just involves defining some macros that are actually
-** used internally as metasymbols to be themselves.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** initializes several macros to be themselves.
-*/
-
-struct metamac MetaMacros[] =
-{
- /* LHS pattern matching characters */
- { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE },
- { '=', MATCHCLASS }, { '~', MATCHNCLASS },
-
- /* these are RHS metasymbols */
- { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER },
- { '>', CALLSUBR },
-
- /* the conditional operations */
- { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI },
-
- /* the hostname lookup characters */
- { '[', HOSTBEGIN }, { ']', HOSTEND },
- { '(', LOOKUPBEGIN }, { ')', LOOKUPEND },
-
- /* miscellaneous control characters */
- { '&', MACRODEXPAND },
-
- { '\0', '\0' }
-};
-
-#define MACBINDING(name, mid) \
- stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
- MacroName[mid] = name;
-
-void
-initmacros(e)
- ENVELOPE *e;
-{
- struct metamac *m;
- int c;
- char buf[5];
-
- for (m = MetaMacros; m->metaname != '\0'; m++)
- {
- buf[0] = m->metaval;
- buf[1] = '\0';
- macdefine(&e->e_macro, A_TEMP, m->metaname, buf);
- }
- buf[0] = MATCHREPL;
- buf[2] = '\0';
- for (c = '0'; c <= '9'; c++)
- {
- buf[1] = c;
- macdefine(&e->e_macro, A_TEMP, c, buf);
- }
-
- /* set defaults for some macros sendmail will use later */
- macdefine(&e->e_macro, A_PERM, 'n', "MAILER-DAEMON");
-
- /* set up external names for some internal macros */
- MACBINDING("opMode", MID_OPMODE);
- /*XXX should probably add equivalents for all short macros here XXX*/
-}
-
-/*
-** EXPAND/DOEXPAND -- macro expand a string using $x escapes.
-**
-** After expansion, the expansion will be in external form (that is,
-** there will be no sendmail metacharacters and METAQUOTEs will have
-** been stripped out).
-**
-** Parameters:
-** s -- the string to expand.
-** buf -- the place to put the expansion.
-** bufsize -- the size of the buffer.
-** explevel -- the depth of expansion (doexpand only)
-** e -- envelope in which to work.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** none.
-*/
-
-static void doexpand __P(( char *, char *, size_t, int, ENVELOPE *));
-
-static void
-doexpand(s, buf, bufsize, explevel, e)
- char *s;
- char *buf;
- size_t bufsize;
- int explevel;
- ENVELOPE *e;
-{
- char *xp;
- char *q;
- bool skipping; /* set if conditionally skipping output */
- bool recurse; /* set if recursion required */
- size_t i;
- int skiplev; /* skipping nesting level */
- int iflev; /* if nesting level */
- bool quotenext; /* quote the following character */
- char xbuf[MACBUFSIZE];
-
- if (tTd(35, 24))
- {
- sm_dprintf("expand(");
- xputs(sm_debug_file(), s);
- sm_dprintf(")\n");
- }
-
- recurse = false;
- skipping = false;
- skiplev = 0;
- iflev = 0;
- quotenext = false;
- if (s == NULL)
- s = "";
- for (xp = xbuf; *s != '\0'; s++)
- {
- int c;
-
- /*
- ** Check for non-ordinary (special?) character.
- ** 'q' will be the interpolated quantity.
- */
-
- q = NULL;
- c = *s & 0377;
-
- if (quotenext)
- {
- quotenext = false;
- goto simpleinterpolate;
- }
-
- switch (c)
- {
- case CONDIF: /* see if var set */
- iflev++;
- c = *++s & 0377;
- if (skipping)
- skiplev++;
- else
- {
- char *mv;
-
- mv = macvalue(c, e);
- skipping = (mv == NULL || *mv == '\0');
- }
- continue;
-
- case CONDELSE: /* change state of skipping */
- if (iflev == 0)
- break; /* XXX: error */
- if (skiplev == 0)
- skipping = !skipping;
- continue;
-
- case CONDFI: /* stop skipping */
- if (iflev == 0)
- break; /* XXX: error */
- iflev--;
- if (skiplev == 0)
- skipping = false;
- if (skipping)
- skiplev--;
- continue;
-
- case MACROEXPAND: /* macro interpolation */
- c = bitidx(*++s);
- if (c != '\0')
- q = macvalue(c, e);
- else
- {
- s--;
- q = NULL;
- }
- if (q == NULL)
- continue;
- break;
-
- case METAQUOTE:
- /* next octet completely quoted */
- quotenext = true;
- break;
- }
-
- /*
- ** Interpolate q or output one character
- */
-
- simpleinterpolate:
- if (skipping || xp >= &xbuf[sizeof(xbuf) - 1])
- continue;
- if (q == NULL)
- *xp++ = c;
- else
- {
- /* copy to end of q or max space remaining in buf */
- bool hiderecurse = false;
-
- while ((c = *q++) != '\0' &&
- xp < &xbuf[sizeof(xbuf) - 1])
- {
- /* check for any sendmail metacharacters */
- if (!hiderecurse && (c & 0340) == 0200)
- recurse = true;
- *xp++ = c;
-
- /* give quoted characters a free ride */
- hiderecurse = (c & 0377) == METAQUOTE;
- }
- }
- }
- *xp = '\0';
-
- if (tTd(35, 28))
- {
- sm_dprintf("expand(%d) ==> ", explevel);
- xputs(sm_debug_file(), xbuf);
- sm_dprintf("\n");
- }
-
- /* recurse as appropriate */
- if (recurse)
- {
- if (explevel < MaxMacroRecursion)
- {
- doexpand(xbuf, buf, bufsize, explevel + 1, e);
- return;
- }
- syserr("expand: recursion too deep (%d max)",
- MaxMacroRecursion);
- }
-
- /* copy results out */
- if (explevel == 0)
- (void) sm_strlcpy(buf, xbuf, bufsize);
- else
- {
- /* leave in internal form */
- i = xp - xbuf;
- if (i >= bufsize)
- i = bufsize - 1;
- memmove(buf, xbuf, i);
- buf[i] = '\0';
- }
-
- if (tTd(35, 24))
- {
- sm_dprintf("expand ==> ");
- xputs(sm_debug_file(), buf);
- sm_dprintf("\n");
- }
-}
-
-void
-expand(s, buf, bufsize, e)
- char *s;
- char *buf;
- size_t bufsize;
- ENVELOPE *e;
-{
- doexpand(s, buf, bufsize, 0, e);
-}
-
-/*
-** MACDEFINE -- bind a macro name to a value
-**
-** Set a macro to a value, with fancy storage management.
-** macdefine will make a copy of the value, if required,
-** and will ensure that the storage for the previous value
-** is not leaked.
-**
-** Parameters:
-** mac -- Macro table.
-** vclass -- storage class of 'value', ignored if value==NULL.
-** A_HEAP means that the value was allocated by
-** malloc, and that macdefine owns the storage.
-** A_TEMP means that value points to temporary storage,
-** and thus macdefine needs to make a copy.
-** A_PERM means that value points to storage that
-** will remain allocated and unchanged for
-** at least the lifetime of mac. Use A_PERM if:
-** -- value == NULL,
-** -- value points to a string literal,
-** -- value was allocated from mac->mac_rpool
-** or (in the case of an envelope macro)
-** from e->e_rpool,
-** -- in the case of an envelope macro,
-** value is a string member of the envelope
-** such as e->e_sender.
-** id -- Macro id. This is a single character macro name
-** such as 'g', or a value returned by macid().
-** value -- Macro value: either NULL, or a string.
-*/
-
-void
-#if SM_HEAP_CHECK
-macdefine_tagged(mac, vclass, id, value, file, line, grp)
-#else /* SM_HEAP_CHECK */
-macdefine(mac, vclass, id, value)
-#endif /* SM_HEAP_CHECK */
- MACROS_T *mac;
- ARGCLASS_T vclass;
- int id;
- char *value;
-#if SM_HEAP_CHECK
- char *file;
- int line;
- int grp;
-#endif /* SM_HEAP_CHECK */
-{
- char *newvalue;
-
- if (id < 0 || id > MAXMACROID)
- return;
-
- if (tTd(35, 9))
- {
- sm_dprintf("%sdefine(%s as ",
- mac->mac_table[id] == NULL ? "" : "re", macname(id));
- xputs(sm_debug_file(), value);
- sm_dprintf(")\n");
- }
-
- if (mac->mac_rpool == NULL)
- {
- char *freeit = NULL;
-
- if (mac->mac_table[id] != NULL &&
- bitnset(id, mac->mac_allocated))
- freeit = mac->mac_table[id];
-
- if (value == NULL || vclass == A_HEAP)
- {
- sm_heap_checkptr_tagged(value, file, line);
- newvalue = value;
- clrbitn(id, mac->mac_allocated);
- }
- else
- {
-#if SM_HEAP_CHECK
- newvalue = sm_strdup_tagged_x(value, file, line, 0);
-#else /* SM_HEAP_CHECK */
- newvalue = sm_strdup_x(value);
-#endif /* SM_HEAP_CHECK */
- setbitn(id, mac->mac_allocated);
- }
- mac->mac_table[id] = newvalue;
- if (freeit != NULL)
- sm_free(freeit);
- }
- else
- {
- if (value == NULL || vclass == A_PERM)
- newvalue = value;
- else
- newvalue = sm_rpool_strdup_x(mac->mac_rpool, value);
- mac->mac_table[id] = newvalue;
- if (vclass == A_HEAP)
- sm_free(value);
- }
-
-#if _FFR_RESET_MACRO_GLOBALS
- switch (id)
- {
- case 'j':
- PSTRSET(MyHostName, value);
- break;
- }
-#endif /* _FFR_RESET_MACRO_GLOBALS */
-}
-
-/*
-** MACSET -- set a named macro to a value (low level)
-**
-** No fancy storage management; the caller takes full responsibility.
-** Often used with macget; see also macdefine.
-**
-** Parameters:
-** mac -- Macro table.
-** i -- Macro name, specified as an integer offset.
-** value -- Macro value: either NULL, or a string.
-*/
-
-void
-macset(mac, i, value)
- MACROS_T *mac;
- int i;
- char *value;
-{
- if (i < 0 || i > MAXMACROID)
- return;
-
- if (tTd(35, 9))
- {
- sm_dprintf("macset(%s as ", macname(i));
- xputs(sm_debug_file(), value);
- sm_dprintf(")\n");
- }
- mac->mac_table[i] = value;
-}
-
-/*
-** MACVALUE -- return uninterpreted value of a macro.
-**
-** Does fancy path searching.
-** The low level counterpart is macget.
-**
-** Parameters:
-** n -- the name of the macro.
-** e -- envelope in which to start looking for the macro.
-**
-** Returns:
-** The value of n.
-**
-** Side Effects:
-** none.
-*/
-
-char *
-macvalue(n, e)
- int n;
- ENVELOPE *e;
-{
- n = bitidx(n);
- if (e != NULL && e->e_mci != NULL)
- {
- char *p = e->e_mci->mci_macro.mac_table[n];
-
- if (p != NULL)
- return p;
- }
- while (e != NULL)
- {
- char *p = e->e_macro.mac_table[n];
-
- if (p != NULL)
- return p;
- if (e == e->e_parent)
- break;
- e = e->e_parent;
- }
- return GlobalMacros.mac_table[n];
-}
-
-/*
-** MACNAME -- return the name of a macro given its internal id
-**
-** Parameter:
-** n -- the id of the macro
-**
-** Returns:
-** The name of n.
-**
-** Side Effects:
-** none.
-**
-** WARNING:
-** Not thread-safe.
-*/
-
-char *
-macname(n)
- int n;
-{
- static char mbuf[2];
-
- n = (int)(unsigned char)n;
- if (n > MAXMACROID)
- return "***OUT OF RANGE MACRO***";
-
- /* if not ASCII printable, look up the name */
- if (n <= 0x20 || n > 0x7f)
- {
- char *p = MacroName[n];
-
- if (p != NULL)
- return p;
- return "***UNDEFINED MACRO***";
- }
-
- /* if in the ASCII graphic range, just return the id directly */
- mbuf[0] = n;
- mbuf[1] = '\0';
- return mbuf;
-}
-
-/*
-** MACID_PARSE -- return id of macro identified by its name
-**
-** Parameters:
-** p -- pointer to name string -- either a single
-** character or {name}.
-** ep -- filled in with the pointer to the byte
-** after the name.
-**
-** Returns:
-** 0 -- An error was detected.
-** 1..MAXMACROID -- The internal id code for this macro.
-**
-** Side Effects:
-** If this is a new macro name, a new id is allocated.
-** On error, syserr is called.
-*/
-
-int
-macid_parse(p, ep)
- char *p;
- char **ep;
-{
- int mid;
- char *bp;
- char mbuf[MAXMACNAMELEN + 1];
-
- if (tTd(35, 14))
- {
- sm_dprintf("macid(");
- xputs(sm_debug_file(), p);
- sm_dprintf(") => ");
- }
-
- if (*p == '\0' || (p[0] == '{' && p[1] == '}'))
- {
- syserr("Name required for macro/class");
- if (ep != NULL)
- *ep = p;
- if (tTd(35, 14))
- sm_dprintf("NULL\n");
- return 0;
- }
- if (*p != '{')
- {
- /* the macro is its own code */
- if (ep != NULL)
- *ep = p + 1;
- if (tTd(35, 14))
- {
- char buf[2];
-
- buf[0] = *p;
- buf[1] = '\0';
- xputs(sm_debug_file(), buf);
- sm_dprintf("\n");
- }
- return bitidx(*p);
- }
- bp = mbuf;
- while (*++p != '\0' && *p != '}' && bp < &mbuf[sizeof(mbuf) - 1])
- {
- if (isascii(*p) && (isalnum(*p) || *p == '_'))
- *bp++ = *p;
- else
- syserr("Invalid macro/class character %c", *p);
- }
- *bp = '\0';
- mid = -1;
- if (*p == '\0')
- {
- syserr("Unbalanced { on %s", mbuf); /* missing } */
- }
- else if (*p != '}')
- {
- syserr("Macro/class name ({%s}) too long (%d chars max)",
- mbuf, (int) (sizeof(mbuf) - 1));
- }
- else if (mbuf[1] == '\0' && mbuf[0] >= 0x20)
- {
- /* ${x} == $x */
- mid = bitidx(mbuf[0]);
- p++;
- }
- else
- {
- STAB *s;
-
- s = stab(mbuf, ST_MACRO, ST_ENTER);
- if (s->s_macro != 0)
- mid = s->s_macro;
- else
- {
- if (NextMacroId > MAXMACROID)
- {
- syserr("Macro/class {%s}: too many long names",
- mbuf);
- s->s_macro = -1;
- }
- else
- {
- MacroName[NextMacroId] = s->s_name;
- s->s_macro = mid = NextMacroId;
- NextMacroId = NEXTMACROID(NextMacroId);
- }
- }
- p++;
- }
- if (ep != NULL)
- *ep = p;
- if (mid < 0 || mid > MAXMACROID)
- {
- syserr("Unable to assign macro/class ID (mid = 0x%x)", mid);
- if (tTd(35, 14))
- sm_dprintf("NULL\n");
- return 0;
- }
- if (tTd(35, 14))
- sm_dprintf("0x%x\n", mid);
- return mid;
-}
-
-/*
-** WORDINCLASS -- tell if a word is in a specific class
-**
-** Parameters:
-** str -- the name of the word to look up.
-** cl -- the class name.
-**
-** Returns:
-** true if str can be found in cl.
-** false otherwise.
-*/
-
-bool
-wordinclass(str, cl)
- char *str;
- int cl;
-{
- STAB *s;
-
- s = stab(str, ST_CLASS, ST_FIND);
- return s != NULL && bitnset(bitidx(cl), s->s_class);
-}
diff --git a/contrib/sendmail/src/mailq.1 b/contrib/sendmail/src/mailq.1
deleted file mode 100644
index 62f123c..0000000
--- a/contrib/sendmail/src/mailq.1
+++ /dev/null
@@ -1,135 +0,0 @@
-.\" Copyright (c) 1998-2000, 2002, 2007 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.
-.\"
-.\" 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: mailq.1,v 8.21 2007/03/22 18:21:27 ca Exp $
-.\"
-.TH MAILQ 1 "$Date: 2007/03/22 18:21:27 $"
-.SH NAME
-mailq
-\- print the mail queue
-.SH SYNOPSIS
-.B mailq
-.RB [ \-Ac ]
-.RB [ \-q... ]
-.RB [ \-v ]
-.SH DESCRIPTION
-.B Mailq
-prints a summary of the mail messages queued for future delivery.
-.PP
-The first line printed for each message
-shows the internal identifier used on this host
-for the message with a possible status character,
-the size of the message in bytes,
-the date and time the message was accepted into the queue,
-and the envelope sender of the message.
-The second line shows the error message that caused this message
-to be retained in the queue;
-it will not be present if the message is being processed
-for the first time.
-The status characters are either
-.B *
-to indicate the job is being processed;
-.B X
-to indicate that the load is too high to process the job; and
-.B -
-to indicate that the job is too young to process.
-The following lines show message recipients,
-one per line.
-.PP
-.B Mailq
-is identical to ``sendmail -bp''.
-.PP
-The relevant options are as follows:
-.TP
-.B \-Ac
-Show the mail submission queue specified in
-.I /etc/mail/submit.cf
-instead of the MTA queue specified in
-.IR /etc/mail/sendmail.cf .
-.TP
-.B \-qL
-Show the "lost" items in the mail queue instead of the normal queue items.
-.TP
-.B \-qQ
-Show the quarantined items in the mail queue instead of the normal queue
-items.
-.TP
-\fB\-q\fR[\fI!\fR]I substr
-Limit processed jobs to those containing
-.I substr
-as a substring of the queue id or not when
-.I !
-is specified.
-.TP
-\fB\-q\fR[\fI!\fR]Q substr
-Limit processed jobs to quarantined jobs containing
-.I substr
-as a substring of the quarantine reason or not when
-.I !
-is specified.
-.TP
-\fB\-q\fR[\fI!\fR]R substr
-Limit processed jobs to those containing
-.I substr
-as a substring of one of the recipients or not when
-.I !
-is specified.
-.TP
-\fB\-q\fR[\fI!\fR]S substr
-Limit processed jobs to those containing
-.I substr
-as a substring of the sender or not when
-.I !
-is specified.
-.TP
-.B \-v
-Print verbose information.
-This adds the priority of the message and
-a single character indicator (``+'' or blank)
-indicating whether a warning message has been sent
-on the first line of the message.
-Additionally, extra lines may be intermixed with the recipients
-indicating the ``controlling user'' information;
-this shows who will own any programs that are executed
-on behalf of this message
-and the name of the alias this command expanded from, if any.
-Moreover, status messages for each recipient are printed
-if available.
-.PP
-Several sendmail.cf options influence the behavior of the
-.B mailq
-utility:
-The number of items printed per queue group is restricted by
-.B MaxQueueRunSize
-if that value is set.
-The status character
-.B *
-is not printed for some values of
-.B QueueSortOrder,
-e.g.,
-filename,
-random,
-modification, and
-none,
-unless a
-.B -q
-option is used to limit the processed jobs.
-.PP
-The
-.B mailq
-utility exits 0 on success, and >0 if an error occurs.
-.SH SEE ALSO
-sendmail(8)
-.SH HISTORY
-The
-.B mailq
-command appeared in
-4.0BSD.
diff --git a/contrib/sendmail/src/main.c b/contrib/sendmail/src/main.c
deleted file mode 100644
index 8680add..0000000
--- a/contrib/sendmail/src/main.c
+++ /dev/null
@@ -1,4554 +0,0 @@
-/*
- * Copyright (c) 1998-2006 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.
- *
- */
-
-#define _DEFINE
-#include <sendmail.h>
-#include <sm/sendmail.h>
-#include <sm/xtrap.h>
-#include <sm/signal.h>
-
-#ifndef lint
-SM_UNUSED(static char copyright[]) =
-"@(#) Copyright (c) 1998-2003 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 /* ! lint */
-
-SM_RCSID("@(#)$Id: main.c,v 8.963 2007/06/29 20:07:37 ca Exp $")
-
-
-#if NETINET || NETINET6
-# include <arpa/inet.h>
-#endif /* NETINET || NETINET6 */
-
-/* for getcfname() */
-#include <sendmail/pathnames.h>
-
-static SM_DEBUG_T
-DebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart",
- "@(#)$Debug: no_persistent_restart - don't restart, log only $");
-
-static void dump_class __P((STAB *, int));
-static void obsolete __P((char **));
-static void testmodeline __P((char *, ENVELOPE *));
-static char *getextenv __P((const char *));
-static void sm_printoptions __P((char **));
-static SIGFUNC_DECL intindebug __P((int));
-static SIGFUNC_DECL sighup __P((int));
-static SIGFUNC_DECL sigpipe __P((int));
-static SIGFUNC_DECL sigterm __P((int));
-#ifdef SIGUSR1
-static SIGFUNC_DECL sigusr1 __P((int));
-#endif /* SIGUSR1 */
-
-/*
-** SENDMAIL -- Post mail to a set of destinations.
-**
-** This is the basic mail router. All user mail programs should
-** call this routine to actually deliver mail. Sendmail in
-** 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/mail/sendmail.cf
-** (read by readcf.c).
-**
-** Usage:
-** /usr/lib/sendmail [flags] addr ...
-**
-** See the associated documentation for details.
-**
-** Authors:
-** Eric Allman, UCB/INGRES (until 10/81).
-** Britton-Lee, Inc., purveyors of fine
-** database computers (11/81 - 10/88).
-** International Computer Science Institute
-** (11/88 - 9/89).
-** UCB/Mammoth Project (10/89 - 7/95).
-** InReference, Inc. (8/95 - 1/97).
-** Sendmail, Inc. (1/98 - present).
-** The support of my employers is gratefully acknowledged.
-** Few of them (Britton-Lee in particular) have had
-** anything to gain from my involvement in this project.
-**
-** Gregory Neil Shapiro,
-** Worcester Polytechnic Institute (until 3/98).
-** Sendmail, Inc. (3/98 - present).
-**
-** Claus Assmann,
-** Sendmail, Inc. (12/98 - present).
-*/
-
-char *FullName; /* sender's full name */
-ENVELOPE BlankEnvelope; /* a "blank" envelope */
-static ENVELOPE MainEnvelope; /* the envelope around the basic letter */
-ADDRESS NullAddress = /* a null address */
- { "", "", NULL, "" };
-char *CommandLineArgs; /* command line args for pid file */
-bool Warn_Q_option = false; /* warn about Q option use */
-static int MissingFds = 0; /* bit map of fds missing on startup */
-char *Mbdb = "pw"; /* mailbox database defaults to /etc/passwd */
-
-#ifdef NGROUPS_MAX
-GIDSET_T InitialGidSet[NGROUPS_MAX];
-#endif /* NGROUPS_MAX */
-
-#define MAXCONFIGLEVEL 10 /* 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 */
-
-unsigned int SubmitMode;
-int SyslogPrefixLen; /* estimated length of syslog prefix */
-#define PIDLEN 6 /* pid length for computing SyslogPrefixLen */
-#ifndef SL_FUDGE
-# define SL_FUDGE 10 /* fudge offset for SyslogPrefixLen */
-#endif /* ! SL_FUDGE */
-#define SLDLL 8 /* est. length of default syslog label */
-
-
-/* Some options are dangerous to allow users to use in non-submit mode */
-#define CHECK_AGAINST_OPMODE(cmd) \
-{ \
- if (extraprivs && \
- OpMode != MD_DELIVER && OpMode != MD_SMTP && \
- OpMode != MD_ARPAFTP && \
- OpMode != MD_VERIFY && OpMode != MD_TEST) \
- { \
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \
- "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
- (cmd)); \
- break; \
- } \
- if (extraprivs && queuerun) \
- { \
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \
- "WARNING: Ignoring submission mode -%c option with -q\n", \
- (cmd)); \
- break; \
- } \
-}
-
-int
-main(argc, argv, envp)
- int argc;
- char **argv;
- char **envp;
-{
- register char *p;
- char **av;
- extern char Version[];
- char *ep, *from;
- STAB *st;
- register int i;
- int j;
- int dp;
- int fill_errno;
- int qgrp = NOQGRP; /* queue group to process */
- bool safecf = true;
- BITMAP256 *p_flags = NULL; /* daemon flags */
- bool warn_C_flag = false;
- bool auth = true; /* whether to set e_auth_param */
- char warn_f_flag = '\0';
- bool run_in_foreground = false; /* -bD mode */
- bool queuerun = false, debug = false;
- struct passwd *pw;
- struct hostent *hp;
- char *nullserver = NULL;
- char *authinfo = NULL;
- char *sysloglabel = NULL; /* label for syslog */
- char *conffile = NULL; /* name of .cf file */
- char *queuegroup = NULL; /* queue group to process */
- char *quarantining = NULL; /* quarantine queue items? */
- bool extraprivs;
- bool forged, negate;
- bool queuepersistent = false; /* queue runner process runs forever */
- bool foregroundqueue = false; /* queue run in foreground */
- bool save_val; /* to save some bool var. */
- int cftype; /* which cf file to use? */
- SM_FILE_T *smdebug;
- static time_t starttime = 0; /* when was process started */
- struct stat traf_st; /* for TrafficLog FIFO check */
- char buf[MAXLINE];
- char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
- static char rnamebuf[MAXNAME]; /* holds RealUserName */
- char *emptyenviron[1];
-#if STARTTLS
- bool tls_ok;
-#endif /* STARTTLS */
- QUEUE_CHAR *new;
- ENVELOPE *e;
- extern int DtableSize;
- extern int optind;
- extern int opterr;
- extern char *optarg;
- extern char **environ;
-#if SASL
- extern void sm_sasl_init __P((void));
-#endif /* SASL */
-
-#if USE_ENVIRON
- envp = environ;
-#endif /* USE_ENVIRON */
-
- /* turn off profiling */
- SM_PROF(0);
-
- /* install default exception handler */
- sm_exc_newthread(fatal_error);
-
- /* set the default in/out channel so errors reported to screen */
- InChannel = smioin;
- OutChannel = smioout;
-
- /*
- ** Check to see if we reentered.
- ** This would normally happen if e_putheader or e_putbody
- ** were NULL when invoked.
- */
-
- if (starttime != 0)
- {
- syserr("main: reentered!");
- abort();
- }
- starttime = curtime();
-
- /* avoid null pointer dereferences */
- TermEscape.te_rv_on = TermEscape.te_under_on = TermEscape.te_normal = "";
-
- RealUid = getuid();
- RealGid = getgid();
-
- /* Check if sendmail is running with extra privs */
- extraprivs = (RealUid != 0 &&
- (geteuid() != getuid() || getegid() != getgid()));
-
- CurrentPid = getpid();
-
- /* get whatever .cf file is right for the opmode */
- cftype = SM_GET_RIGHT_CF;
-
- /* in 4.4BSD, the table can be huge; impose a reasonable limit */
- DtableSize = getdtsize();
- if (DtableSize > 256)
- DtableSize = 256;
-
- /*
- ** Be sure we have enough file descriptors.
- ** But also be sure that 0, 1, & 2 are open.
- */
-
- /* reset errno and fill_errno; the latter is used way down below */
- errno = fill_errno = 0;
- fill_fd(STDIN_FILENO, NULL);
- if (errno != 0)
- fill_errno = errno;
- fill_fd(STDOUT_FILENO, NULL);
- if (errno != 0)
- fill_errno = errno;
- fill_fd(STDERR_FILENO, NULL);
- if (errno != 0)
- fill_errno = errno;
-
- sm_closefrom(STDERR_FILENO + 1, DtableSize);
- errno = 0;
- smdebug = NULL;
-
-#if LOG
-# ifndef SM_LOG_STR
-# define SM_LOG_STR "sendmail"
-# endif /* ! SM_LOG_STR */
-# ifdef LOG_MAIL
- openlog(SM_LOG_STR, LOG_PID, LOG_MAIL);
-# else /* LOG_MAIL */
- openlog(SM_LOG_STR, LOG_PID);
-# endif /* LOG_MAIL */
-#endif /* LOG */
-
- /*
- ** Seed the random number generator.
- ** Used for queue file names, picking a queue directory, and
- ** MX randomization.
- */
-
- seed_random();
-
- /* do machine-dependent initializations */
- init_md(argc, argv);
-
-
- SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL;
-
- /* reset status from syserr() calls for missing file descriptors */
- Errors = 0;
- ExitStat = EX_OK;
-
- SubmitMode = SUBMIT_UNKNOWN;
-#if XDEBUG
- checkfd012("after openlog");
-#endif /* XDEBUG */
-
- tTsetup(tTdvect, sizeof(tTdvect), "0-99.1,*_trace_*.1");
-
-#ifdef NGROUPS_MAX
- /* save initial group set for future checks */
- i = getgroups(NGROUPS_MAX, InitialGidSet);
- if (i <= 0)
- {
- InitialGidSet[0] = (GID_T) -1;
- i = 0;
- }
- while (i < NGROUPS_MAX)
- InitialGidSet[i++] = InitialGidSet[0];
-#endif /* NGROUPS_MAX */
-
- /* drop group id privileges (RunAsUser not yet set) */
- dp = drop_privileges(false);
- setstat(dp);
-
-#ifdef SIGUSR1
- /* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
- if (!extraprivs)
- {
- /* arrange to dump state on user-1 signal */
- (void) sm_signal(SIGUSR1, sigusr1);
- }
- else
- {
- /* ignore user-1 signal */
- (void) sm_signal(SIGUSR1, SIG_IGN);
- }
-#endif /* SIGUSR1 */
-
- /* initialize for setproctitle */
- initsetproctitle(argc, argv, envp);
-
- /* Handle any non-getoptable constructions. */
- obsolete(argv);
-
- /*
- ** Do a quick prescan of the argument list.
- */
-
-
- /* find initial opMode */
- OpMode = MD_DELIVER;
- av = argv;
- p = strrchr(*av, '/');
- if (p++ == NULL)
- p = *av;
- if (strcmp(p, "newaliases") == 0)
- OpMode = MD_INITALIAS;
- else if (strcmp(p, "mailq") == 0)
- OpMode = MD_PRINT;
- else if (strcmp(p, "smtpd") == 0)
- OpMode = MD_DAEMON;
- else if (strcmp(p, "hoststat") == 0)
- OpMode = MD_HOSTSTAT;
- else if (strcmp(p, "purgestat") == 0)
- OpMode = MD_PURGESTAT;
-
-#if defined(__osf__) || defined(_AIX3)
-# define OPTIONS "A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:x"
-#endif /* defined(__osf__) || defined(_AIX3) */
-#if defined(sony_news)
-# define OPTIONS "A:B:b:C:cD:d:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
-#endif /* defined(sony_news) */
-#ifndef OPTIONS
-# define OPTIONS "A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
-#endif /* ! OPTIONS */
-
- /* Set to 0 to allow -b; need to check optarg before using it! */
- opterr = 0;
- while ((j = getopt(argc, argv, OPTIONS)) != -1)
- {
- switch (j)
- {
- case 'b': /* operations mode */
- j = (optarg == NULL) ? ' ' : *optarg;
- switch (j)
- {
- case MD_DAEMON:
- case MD_FGDAEMON:
- case MD_SMTP:
- case MD_INITALIAS:
- case MD_DELIVER:
- case MD_VERIFY:
- case MD_TEST:
- case MD_PRINT:
- case MD_PRINTNQE:
- case MD_HOSTSTAT:
- case MD_PURGESTAT:
- case MD_ARPAFTP:
- OpMode = j;
- break;
-
- case MD_FREEZE:
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Frozen configurations unsupported\n");
- return EX_USAGE;
-
- default:
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Invalid operation mode %c\n",
- j);
- return EX_USAGE;
- }
- break;
-
- case 'D':
- if (debug)
- {
- errno = 0;
- syserr("-D file must be before -d");
- ExitStat = EX_USAGE;
- break;
- }
- dp = drop_privileges(true);
- setstat(dp);
- smdebug = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
- optarg, SM_IO_APPEND, NULL);
- if (smdebug == NULL)
- {
- syserr("cannot open %s", optarg);
- ExitStat = EX_CANTCREAT;
- break;
- }
- sm_debug_setfile(smdebug);
- break;
-
- case 'd':
- debug = true;
- tTflag(optarg);
- (void) sm_io_setvbuf(sm_debug_file(), SM_TIME_DEFAULT,
- (char *) NULL, SM_IO_NBF,
- SM_IO_BUFSIZ);
- break;
-
- case 'G': /* relay (gateway) submission */
- SubmitMode = SUBMIT_MTA;
- break;
-
- case 'L':
- if (optarg == NULL)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "option requires an argument -- '%c'",
- (char) j);
- return EX_USAGE;
- }
- j = SM_MIN(strlen(optarg), 32) + 1;
- sysloglabel = xalloc(j);
- (void) sm_strlcpy(sysloglabel, optarg, j);
- SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
- SL_FUDGE + j;
- break;
-
- case 'Q':
- case 'q':
- /* just check if it is there */
- queuerun = true;
- break;
- }
- }
- opterr = 1;
-
- /* Don't leak queue information via debug flags */
- if (extraprivs && queuerun && debug)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "WARNING: Can not use -d with -q. Disabling debugging.\n");
- sm_debug_close();
- sm_debug_setfile(NULL);
- (void) memset(tTdvect, '\0', sizeof(tTdvect));
- }
-
-#if LOG
- if (sysloglabel != NULL)
- {
- /* Sanitize the string */
- for (p = sysloglabel; *p != '\0'; p++)
- {
- if (!isascii(*p) || !isprint(*p) || *p == '%')
- *p = '*';
- }
- 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;
- BlankEnvelope.e_xfp = NULL;
- STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
- CurEnv = &BlankEnvelope;
- STRUCTCOPY(NullAddress, MainEnvelope.e_from);
-
- /*
- ** Set default values for variables.
- ** These cannot be in initialized data space.
- */
-
- setdefaults(&BlankEnvelope);
- initmacros(&BlankEnvelope);
-
- /* reset macro */
- set_op_mode(OpMode);
- if (OpMode == MD_DAEMON)
- DaemonPid = CurrentPid; /* needed for finis() to work */
-
- pw = sm_getpwuid(RealUid);
- if (pw != NULL)
- (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof(rnamebuf));
- else
- (void) sm_snprintf(rnamebuf, sizeof(rnamebuf), "Unknown UID %d",
- (int) RealUid);
-
- RealUserName = rnamebuf;
-
- if (tTd(0, 101))
- {
- sm_dprintf("Version %s\n", Version);
- finis(false, true, EX_OK);
- /* NOTREACHED */
- }
-
- /*
- ** if running non-set-user-ID binary as non-root, pretend
- ** we are the RunAsUid
- */
-
- if (RealUid != 0 && geteuid() == RealUid)
- {
- if (tTd(47, 1))
- sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
- (int) RealUid);
- RunAsUid = RealUid;
- }
- else if (geteuid() != 0)
- RunAsUid = geteuid();
-
- EffGid = getegid();
- if (RealUid != 0 && EffGid == RealGid)
- RunAsGid = RealGid;
-
- if (tTd(47, 5))
- {
- sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
- (int) geteuid(), (int) getuid(),
- (int) getegid(), (int) getgid());
- sm_dprintf("main: RunAsUser = %d:%d\n",
- (int) RunAsUid, (int) RunAsGid);
- }
-
- /* save command line arguments */
- j = 0;
- for (av = argv; *av != NULL; )
- j += strlen(*av++) + 1;
- SaveArgv = (char **) xalloc(sizeof(char *) * (argc + 1));
- CommandLineArgs = xalloc(j);
- p = CommandLineArgs;
- for (av = argv, i = 0; *av != NULL; )
- {
- int h;
-
- SaveArgv[i++] = newstr(*av);
- if (av != argv)
- *p++ = ' ';
- (void) sm_strlcpy(p, *av++, j);
- h = strlen(p);
- p += h;
- j -= h + 1;
- }
- SaveArgv[i] = NULL;
-
- if (tTd(0, 1))
- {
- extern char *CompileOptions[];
-
- sm_dprintf("Version %s\n Compiled with:", Version);
- sm_printoptions(CompileOptions);
- }
- if (tTd(0, 10))
- {
- extern char *OsCompileOptions[];
-
- sm_dprintf(" OS Defines:");
- sm_printoptions(OsCompileOptions);
-#ifdef _PATH_UNIX
- sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
-#endif /* _PATH_UNIX */
-
- sm_dprintf(" Conf file:\t%s (default for MSP)\n",
- getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
- conffile));
- sm_dprintf(" Conf file:\t%s (default for MTA)\n",
- getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
- conffile));
- sm_dprintf(" Pid file:\t%s (default)\n", PidFile);
- }
-
- if (tTd(0, 12))
- {
- extern char *SmCompileOptions[];
-
- sm_dprintf(" libsm Defines:");
- sm_printoptions(SmCompileOptions);
- }
-
- if (tTd(0, 13))
- {
- extern char *FFRCompileOptions[];
-
- sm_dprintf(" FFR Defines:");
- sm_printoptions(FFRCompileOptions);
- }
-
- /* clear sendmail's environment */
- ExternalEnviron = environ;
- emptyenviron[0] = NULL;
- environ = emptyenviron;
-
- /*
- ** restore any original TZ setting until TimeZoneSpec has been
- ** determined - or early log messages may get bogus time stamps
- */
-
- if ((p = getextenv("TZ")) != NULL)
- {
- char *tz;
- int tzlen;
-
- /* XXX check for reasonable length? */
- tzlen = strlen(p) + 4;
- tz = xalloc(tzlen);
- (void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
-
- /* XXX check return code? */
- (void) putenv(tz);
- }
-
- /* prime the child environment */
- sm_setuserenv("AGENT", "sendmail");
-
- (void) sm_signal(SIGPIPE, SIG_IGN);
- OldUmask = umask(022);
- FullName = getextenv("NAME");
- if (FullName != NULL)
- FullName = newstr(FullName);
-
- /*
- ** Initialize name server if it is going to be used.
- */
-
-#if NAMED_BIND
- if (!bitset(RES_INIT, _res.options))
- (void) res_init();
- if (tTd(8, 8))
- _res.options |= RES_DEBUG;
- else
- _res.options &= ~RES_DEBUG;
-# ifdef RES_NOALIASES
- _res.options |= RES_NOALIASES;
-# 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;
-
- /* initialize some macros, etc. */
- init_vendor_macros(&BlankEnvelope);
-
- /* version */
- macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
-
- /* hostname */
- hp = myhostname(jbuf, sizeof(jbuf));
- if (jbuf[0] != '\0')
- {
- struct utsname utsname;
-
- if (tTd(0, 4))
- sm_dprintf("Canonical name: %s\n", jbuf);
- macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
- macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
- setclass('w', jbuf);
-
- p = strchr(jbuf, '.');
- if (p != NULL && p[1] != '\0')
- macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm', &p[1]);
-
- if (uname(&utsname) >= 0)
- p = utsname.nodename;
- else
- {
- if (tTd(0, 22))
- sm_dprintf("uname failed (%s)\n",
- sm_errstring(errno));
- makelower(jbuf);
- p = jbuf;
- }
- if (tTd(0, 4))
- sm_dprintf(" UUCP nodename: %s\n", p);
- macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
- setclass('k', p);
- setclass('w', p);
- }
- if (hp != NULL)
- {
- for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
- {
- if (tTd(0, 4))
- sm_dprintf("\ta.k.a.: %s\n", *av);
- setclass('w', *av);
- }
-#if NETINET || NETINET6
- for (i = 0; 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)
- {
-# if NETINET
- case AF_INET:
- if (hp->h_length != INADDRSZ)
- break;
-
- memmove(&ia, hp->h_addr_list[i], INADDRSZ);
- (void) sm_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) sm_snprintf(ipbuf, sizeof(ipbuf),
- "[%.100s]", addr);
- break;
-# endif /* NETINET6 */
- }
- if (ipbuf[0] == '\0')
- break;
-
- if (tTd(0, 4))
- sm_dprintf("\ta.k.a.: %s\n", ipbuf);
- setclass('w', ipbuf);
- }
-#endif /* NETINET || NETINET6 */
-#if NETINET6
- freehostent(hp);
- hp = NULL;
-#endif /* NETINET6 */
- }
-
- /* current time */
- macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
-
- /* current load average */
- sm_getla();
-
- QueueLimitRecipient = (QUEUE_CHAR *) NULL;
- QueueLimitSender = (QUEUE_CHAR *) NULL;
- QueueLimitId = (QUEUE_CHAR *) NULL;
- QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
-
- /*
- ** Crack argv.
- */
-
- optind = 1;
- while ((j = getopt(argc, argv, OPTIONS)) != -1)
- {
- switch (j)
- {
- case 'b': /* operations mode */
- /* already done */
- break;
-
- case 'A': /* use Alternate sendmail/submit.cf */
- cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
- : SM_GET_SENDMAIL_CF;
- break;
-
- case 'B': /* body type */
- CHECK_AGAINST_OPMODE(j);
- BlankEnvelope.e_bodytype = newstr(optarg);
- break;
-
- case 'C': /* select configuration file (already done) */
- if (RealUid != 0)
- warn_C_flag = true;
- conffile = newstr(optarg);
- dp = drop_privileges(true);
- setstat(dp);
- safecf = false;
- break;
-
- case 'D':
- case 'd': /* debugging */
- /* already done */
- break;
-
- case 'f': /* from address */
- case 'r': /* obsolete -f flag */
- CHECK_AGAINST_OPMODE(j);
- if (from != NULL)
- {
- usrerr("More than one \"from\" person");
- ExitStat = EX_USAGE;
- break;
- }
- if (optarg[0] == '\0')
- from = newstr("<>");
- else
- from = newstr(denlstring(optarg, true, true));
- if (strcmp(RealUserName, from) != 0)
- warn_f_flag = j;
- break;
-
- case 'F': /* set full name */
- CHECK_AGAINST_OPMODE(j);
- FullName = newstr(optarg);
- break;
-
- case 'G': /* relay (gateway) submission */
- /* already set */
- CHECK_AGAINST_OPMODE(j);
- break;
-
- case 'h': /* hop count */
- CHECK_AGAINST_OPMODE(j);
- BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
- 10);
- (void) sm_snprintf(buf, sizeof(buf), "%d",
- BlankEnvelope.e_hopcount);
- macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
-
- if (*ep)
- {
- usrerr("Bad hop count (%s)", optarg);
- ExitStat = EX_USAGE;
- }
- break;
-
- case 'L': /* program label */
- /* already set */
- break;
-
- case 'n': /* don't alias */
- CHECK_AGAINST_OPMODE(j);
- NoAlias = true;
- break;
-
- case 'N': /* delivery status notifications */
- CHECK_AGAINST_OPMODE(j);
- DefaultNotify |= QHASNOTIFY;
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{dsn_notify}"), optarg);
- if (sm_strcasecmp(optarg, "never") == 0)
- break;
- for (p = optarg; p != NULL; optarg = p)
- {
- p = strchr(p, ',');
- if (p != NULL)
- *p++ = '\0';
- if (sm_strcasecmp(optarg, "success") == 0)
- DefaultNotify |= QPINGONSUCCESS;
- else if (sm_strcasecmp(optarg, "failure") == 0)
- DefaultNotify |= QPINGONFAILURE;
- else if (sm_strcasecmp(optarg, "delay") == 0)
- DefaultNotify |= QPINGONDELAY;
- else
- {
- usrerr("Invalid -N argument");
- ExitStat = EX_USAGE;
- }
- }
- break;
-
- case 'o': /* set option */
- setoption(*optarg, optarg + 1, false, true,
- &BlankEnvelope);
- break;
-
- case 'O': /* set option (long form) */
- setoption(' ', optarg, false, true, &BlankEnvelope);
- break;
-
- case 'p': /* set protocol */
- CHECK_AGAINST_OPMODE(j);
- p = strchr(optarg, ':');
- if (p != NULL)
- {
- *p++ = '\0';
- if (*p != '\0')
- {
- i = strlen(p) + 1;
- ep = sm_malloc_x(i);
- cleanstrcpy(ep, p, i);
- macdefine(&BlankEnvelope.e_macro,
- A_HEAP, 's', ep);
- }
- }
- if (*optarg != '\0')
- {
- i = strlen(optarg) + 1;
- ep = sm_malloc_x(i);
- cleanstrcpy(ep, optarg, i);
- macdefine(&BlankEnvelope.e_macro, A_HEAP,
- 'r', ep);
- }
- break;
-
- case 'Q': /* change quarantining on queued items */
- /* sanity check */
- if (OpMode != MD_DELIVER &&
- OpMode != MD_QUEUERUN)
- {
- usrerr("Can not use -Q with -b%c", OpMode);
- ExitStat = EX_USAGE;
- break;
- }
-
- if (OpMode == MD_DELIVER)
- set_op_mode(MD_QUEUERUN);
-
- FullName = NULL;
-
- quarantining = newstr(optarg);
- break;
-
- case 'q': /* run queue files at intervals */
- /* sanity check */
- if (OpMode != MD_DELIVER &&
- OpMode != MD_DAEMON &&
- OpMode != MD_FGDAEMON &&
- OpMode != MD_PRINT &&
- OpMode != MD_PRINTNQE &&
- OpMode != MD_QUEUERUN)
- {
- usrerr("Can not use -q with -b%c", OpMode);
- ExitStat = EX_USAGE;
- break;
- }
-
- /* don't override -bd, -bD or -bp */
- if (OpMode == MD_DELIVER)
- set_op_mode(MD_QUEUERUN);
-
- FullName = NULL;
- negate = optarg[0] == '!';
- if (negate)
- {
- /* negate meaning of pattern match */
- optarg++; /* skip '!' for next switch */
- }
-
- switch (optarg[0])
- {
- case 'G': /* Limit by queue group name */
- if (negate)
- {
- usrerr("Can not use -q!G");
- ExitStat = EX_USAGE;
- break;
- }
- if (queuegroup != NULL)
- {
- usrerr("Can not use multiple -qG options");
- ExitStat = EX_USAGE;
- break;
- }
- queuegroup = newstr(&optarg[1]);
- break;
-
- case 'I': /* Limit by ID */
- new = (QUEUE_CHAR *) xalloc(sizeof(*new));
- new->queue_match = newstr(&optarg[1]);
- new->queue_negate = negate;
- new->queue_next = QueueLimitId;
- QueueLimitId = new;
- break;
-
- case 'R': /* Limit by recipient */
- new = (QUEUE_CHAR *) xalloc(sizeof(*new));
- new->queue_match = newstr(&optarg[1]);
- new->queue_negate = negate;
- new->queue_next = QueueLimitRecipient;
- QueueLimitRecipient = new;
- break;
-
- case 'S': /* Limit by sender */
- new = (QUEUE_CHAR *) xalloc(sizeof(*new));
- new->queue_match = newstr(&optarg[1]);
- new->queue_negate = negate;
- new->queue_next = QueueLimitSender;
- QueueLimitSender = new;
- break;
-
- case 'f': /* foreground queue run */
- foregroundqueue = true;
- break;
-
- case 'Q': /* Limit by quarantine message */
- if (optarg[1] != '\0')
- {
- new = (QUEUE_CHAR *) xalloc(sizeof(*new));
- new->queue_match = newstr(&optarg[1]);
- new->queue_negate = negate;
- new->queue_next = QueueLimitQuarantine;
- QueueLimitQuarantine = new;
- }
- QueueMode = QM_QUARANTINE;
- break;
-
- case 'L': /* act on lost items */
- QueueMode = QM_LOST;
- break;
-
- case 'p': /* Persistent queue */
- queuepersistent = true;
- if (QueueIntvl == 0)
- QueueIntvl = 1;
- if (optarg[1] == '\0')
- break;
- ++optarg;
- /* FALLTHROUGH */
-
- default:
- i = Errors;
- QueueIntvl = convtime(optarg, 'm');
- if (QueueIntvl < 0)
- {
- usrerr("Invalid -q value");
- ExitStat = EX_USAGE;
- }
-
- /* check for bad conversion */
- if (i < Errors)
- ExitStat = EX_USAGE;
- break;
- }
- break;
-
- case 'R': /* DSN RET: what to return */
- CHECK_AGAINST_OPMODE(j);
- if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags))
- {
- usrerr("Duplicate -R flag");
- ExitStat = EX_USAGE;
- break;
- }
- BlankEnvelope.e_flags |= EF_RET_PARAM;
- if (sm_strcasecmp(optarg, "hdrs") == 0)
- BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
- else if (sm_strcasecmp(optarg, "full") != 0)
- {
- usrerr("Invalid -R value");
- ExitStat = EX_USAGE;
- }
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{dsn_ret}"), optarg);
- break;
-
- case 't': /* read recipients from message */
- CHECK_AGAINST_OPMODE(j);
- GrabTo = true;
- break;
-
- case 'V': /* DSN ENVID: set "original" envelope id */
- CHECK_AGAINST_OPMODE(j);
- if (!xtextok(optarg))
- {
- usrerr("Invalid syntax in -V flag");
- ExitStat = EX_USAGE;
- }
- else
- {
- BlankEnvelope.e_envid = newstr(optarg);
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{dsn_envid}"), optarg);
- }
- break;
-
- case 'X': /* traffic log file */
- dp = drop_privileges(true);
- setstat(dp);
- if (stat(optarg, &traf_st) == 0 &&
- S_ISFIFO(traf_st.st_mode))
- TrafficLogFile = sm_io_open(SmFtStdio,
- SM_TIME_DEFAULT,
- optarg,
- SM_IO_WRONLY, NULL);
- else
- TrafficLogFile = sm_io_open(SmFtStdio,
- SM_TIME_DEFAULT,
- optarg,
- SM_IO_APPEND, NULL);
- if (TrafficLogFile == NULL)
- {
- syserr("cannot open %s", optarg);
- ExitStat = EX_CANTCREAT;
- break;
- }
- (void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT,
- NULL, SM_IO_LBF, 0);
- break;
-
- /* compatibility flags */
- case 'c': /* connect to non-local mailers */
- case 'i': /* don't let dot stop me */
- case 'm': /* send to me too */
- case 'T': /* set timeout interval */
- case 'v': /* give blow-by-blow description */
- setoption(j, "T", false, true, &BlankEnvelope);
- break;
-
- case 'e': /* error message disposition */
- case 'M': /* define macro */
- setoption(j, optarg, false, true, &BlankEnvelope);
- break;
-
- case 's': /* save From lines in headers */
- setoption('f', "T", false, true, &BlankEnvelope);
- break;
-
-#ifdef DBM
- case 'I': /* initialize alias DBM file */
- set_op_mode(MD_INITALIAS);
- break;
-#endif /* DBM */
-
-#if defined(__osf__) || defined(_AIX3)
- case 'x': /* random flag that OSF/1 & AIX mailx passes */
- break;
-#endif /* defined(__osf__) || defined(_AIX3) */
-#if defined(sony_news)
- case 'E':
- case 'J': /* ignore flags for Japanese code conversion
- implemented on Sony NEWS */
- break;
-#endif /* defined(sony_news) */
-
- default:
- finis(true, true, EX_USAGE);
- /* NOTREACHED */
- break;
- }
- }
-
- /* if we've had errors so far, exit now */
- if ((ExitStat != EX_OK && OpMode != MD_TEST) ||
- ExitStat == EX_OSERR)
- {
- finis(false, true, ExitStat);
- /* NOTREACHED */
- }
-
- if (bitset(SUBMIT_MTA, SubmitMode))
- {
- /* If set daemon_flags on command line, don't reset it */
- if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_flags}"), "CC f");
- }
- else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
- {
- SubmitMode = SUBMIT_MSA;
-
- /* If set daemon_flags on command line, don't reset it */
- if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_flags}"), "c u");
- }
-
- /*
- ** Do basic initialization.
- ** Read system control file.
- ** Extract special fields for local use.
- */
-
-#if XDEBUG
- checkfd012("before readcf");
-#endif /* XDEBUG */
- vendor_pre_defaults(&BlankEnvelope);
-
- readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
- safecf, &BlankEnvelope);
-#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
- ConfigFileRead = true;
-#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
- vendor_post_defaults(&BlankEnvelope);
-
- /* now we can complain about missing fds */
- if (MissingFds != 0 && LogLevel > 8)
- {
- char mbuf[MAXLINE];
-
- mbuf[0] = '\0';
- if (bitset(1 << STDIN_FILENO, MissingFds))
- (void) sm_strlcat(mbuf, ", stdin", sizeof(mbuf));
- if (bitset(1 << STDOUT_FILENO, MissingFds))
- (void) sm_strlcat(mbuf, ", stdout", sizeof(mbuf));
- if (bitset(1 << STDERR_FILENO, MissingFds))
- (void) sm_strlcat(mbuf, ", stderr", sizeof(mbuf));
-
- /* Notice: fill_errno is from high above: fill_fd() */
- sm_syslog(LOG_WARNING, NOQID,
- "File descriptors missing on startup: %s; %s",
- &mbuf[2], sm_errstring(fill_errno));
- }
-
- /* Remove the ability for a normal user to send signals */
- if (RealUid != 0 && RealUid != geteuid())
- {
- uid_t new_uid = geteuid();
-
-#if HASSETREUID
- /*
- ** Since we can differentiate between uid and euid,
- ** make the uid a different user so the real user
- ** can't send signals. However, it doesn't need to be
- ** root (euid has root).
- */
-
- if (new_uid == 0)
- new_uid = DefUid;
- if (tTd(47, 5))
- sm_dprintf("Changing real uid to %d\n", (int) new_uid);
- if (setreuid(new_uid, geteuid()) < 0)
- {
- syserr("main: setreuid(%d, %d) failed",
- (int) new_uid, (int) geteuid());
- finis(false, true, EX_OSERR);
- /* NOTREACHED */
- }
- if (tTd(47, 10))
- sm_dprintf("Now running as e/ruid %d:%d\n",
- (int) geteuid(), (int) getuid());
-#else /* HASSETREUID */
- /*
- ** Have to change both effective and real so need to
- ** change them both to effective to keep privs.
- */
-
- if (tTd(47, 5))
- sm_dprintf("Changing uid to %d\n", (int) new_uid);
- if (setuid(new_uid) < 0)
- {
- syserr("main: setuid(%d) failed", (int) new_uid);
- finis(false, true, EX_OSERR);
- /* NOTREACHED */
- }
- if (tTd(47, 10))
- sm_dprintf("Now running as e/ruid %d:%d\n",
- (int) geteuid(), (int) getuid());
-#endif /* HASSETREUID */
- }
-
-#if NAMED_BIND
- if (FallbackMX != NULL)
- (void) getfallbackmxrr(FallbackMX);
-#endif /* NAMED_BIND */
-
- if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "WARNING: SuperSafe=interactive should only be used with\n DeliveryMode=interactive\n");
- }
-
- if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
- {
- usrerr("Mail submission program cannot be used as daemon");
- finis(false, true, EX_USAGE);
- }
-
- if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
- OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
- OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
- makeworkgroups();
-
- /* set up the basic signal handlers */
- if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
- (void) sm_signal(SIGINT, intsig);
- (void) sm_signal(SIGTERM, intsig);
-
- /* Enforce use of local time (null string overrides this) */
- if (TimeZoneSpec == NULL)
- unsetenv("TZ");
- else if (TimeZoneSpec[0] != '\0')
- sm_setuserenv("TZ", TimeZoneSpec);
- else
- sm_setuserenv("TZ", NULL);
- tzset();
-
- /* initialize mailbox database */
- i = sm_mbdb_initialize(Mbdb);
- if (i != EX_OK)
- {
- usrerr("Can't initialize mailbox database \"%s\": %s",
- Mbdb, sm_strexit(i));
- ExitStat = i;
- }
-
- /* avoid denial-of-service attacks */
- resetlimits();
-
- if (OpMode == MD_TEST)
- {
- /* can't be done after readcf if RunAs* is used */
- dp = drop_privileges(true);
- if (dp != EX_OK)
- {
- finis(false, true, dp);
- /* NOTREACHED */
- }
- }
- else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
- {
- /* drop privileges -- daemon mode done after socket/bind */
- dp = drop_privileges(false);
- setstat(dp);
- if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
- {
- usrerr("Mail submission program must have RunAsUser set to non root user");
- finis(false, true, EX_CONFIG);
- /* NOTREACHED */
- }
- }
-
-#if NAMED_BIND
- _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.
- */
-
- authinfo = getauthinfo(STDIN_FILENO, &forged);
- macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
-
- /* suppress error printing if errors mailed back or whatever */
- if (BlankEnvelope.e_errormode != EM_PRINT)
- HoldErrs = true;
-
- /* set up the $=m class now, after .cf has a chance to redefine $m */
- expand("\201m", jbuf, sizeof(jbuf), &BlankEnvelope);
- if (jbuf[0] != '\0')
- setclass('m', jbuf);
-
- /* probe interfaces and locate any additional names */
- if (DontProbeInterfaces != DPI_PROBENONE)
- load_if_names();
-
- if (tTd(0, 10))
- {
- char pidpath[MAXPATHLEN];
-
- /* Now we know which .cf file we use */
- sm_dprintf(" Conf file:\t%s (selected)\n",
- getcfname(OpMode, SubmitMode, cftype, conffile));
- expand(PidFile, pidpath, sizeof(pidpath), &BlankEnvelope);
- sm_dprintf(" Pid file:\t%s (selected)\n", pidpath);
- }
-
- if (tTd(0, 1))
- {
- sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
- sm_dprintf("\n (short domain name) $w = ");
- xputs(sm_debug_file(), macvalue('w', &BlankEnvelope));
- sm_dprintf("\n (canonical domain name) $j = ");
- xputs(sm_debug_file(), macvalue('j', &BlankEnvelope));
- sm_dprintf("\n (subdomain name) $m = ");
- xputs(sm_debug_file(), macvalue('m', &BlankEnvelope));
- sm_dprintf("\n (node name) $k = ");
- xputs(sm_debug_file(), macvalue('k', &BlankEnvelope));
- sm_dprintf("\n========================================================\n\n");
- }
-
- /*
- ** Do more command line checking -- these are things that
- ** have to modify the results of reading the config file.
- */
-
- /* process authorization warnings from command line */
- if (warn_C_flag)
- auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
- RealUserName, conffile);
- if (Warn_Q_option && !wordinclass(RealUserName, 't'))
- auth_warning(&BlankEnvelope, "Processed from queue %s",
- QueueDir);
- if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
- RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
- sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
- (int) RealUid);
-
- /* check body type for legality */
- i = check_bodytype(BlankEnvelope.e_bodytype);
- if (i == BODYTYPE_ILLEGAL)
- {
- usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
- BlankEnvelope.e_bodytype = NULL;
- }
- else if (i != BODYTYPE_NONE)
- SevenBitInput = (i == BODYTYPE_7BIT);
-
- /* tweak default DSN notifications */
- if (DefaultNotify == 0)
- DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
-
- /* check for sane configuration level */
- if (ConfigLevel > MAXCONFIGLEVEL)
- {
- syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
- ConfigLevel, Version, MAXCONFIGLEVEL);
- }
-
- /* need MCI cache to have persistence */
- if (HostStatDir != NULL && MaxMciCache == 0)
- {
- HostStatDir = NULL;
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
- }
-
- /* need HostStatusDir in order to have SingleThreadDelivery */
- if (SingleThreadDelivery && HostStatDir == NULL)
- {
- SingleThreadDelivery = false;
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
- }
-
-#if _FFR_MEMSTAT
- j = sm_memstat_open();
- if (j < 0 && (RefuseLowMem > 0 || QueueLowMem > 0) && LogLevel > 4)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "cannot get memory statistics, settings ignored, error=%d"
- , j);
- }
-#endif /* _FFR_MEMSTAT */
-
- /* check for permissions */
- if (RealUid != 0 &&
- RealUid != TrustedUid)
- {
- char *action = NULL;
-
- switch (OpMode)
- {
- case MD_QUEUERUN:
- if (quarantining != NULL)
- action = "quarantine jobs";
- else
- {
- /* Normal users can do a single queue run */
- if (QueueIntvl == 0)
- break;
- }
-
- /* but not persistent queue runners */
- if (action == NULL)
- action = "start a queue runner daemon";
- /* FALLTHROUGH */
-
- case MD_PURGESTAT:
- if (action == NULL)
- action = "purge host status";
- /* FALLTHROUGH */
-
- case MD_DAEMON:
- case MD_FGDAEMON:
- if (action == NULL)
- action = "run daemon";
-
- if (tTd(65, 1))
- sm_dprintf("Deny user %d attempt to %s\n",
- (int) RealUid, action);
-
- if (LogLevel > 1)
- sm_syslog(LOG_ALERT, NOQID,
- "user %d attempted to %s",
- (int) RealUid, action);
- HoldErrs = false;
- usrerr("Permission denied (real uid not trusted)");
- finis(false, true, EX_USAGE);
- /* NOTREACHED */
- break;
-
- case MD_VERIFY:
- if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
- {
- /*
- ** If -bv and RestrictExpand,
- ** drop privs to prevent normal
- ** users from reading private
- ** aliases/forwards/:include:s
- */
-
- if (tTd(65, 1))
- sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
- (int) RealUid);
-
- dp = drop_privileges(true);
-
- /* Fake address safety */
- if (tTd(65, 1))
- sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
- setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
-
- if (dp != EX_OK)
- {
- if (tTd(65, 1))
- sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
- (int) RealUid);
- CurEnv->e_id = NULL;
- finis(true, true, dp);
- /* NOTREACHED */
- }
- }
- break;
-
- case MD_TEST:
- case MD_PRINT:
- case MD_PRINTNQE:
- case MD_FREEZE:
- case MD_HOSTSTAT:
- /* Nothing special to check */
- break;
-
- case MD_INITALIAS:
- if (!wordinclass(RealUserName, 't'))
- {
- if (tTd(65, 1))
- sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
- (int) RealUid);
- if (LogLevel > 1)
- sm_syslog(LOG_ALERT, NOQID,
- "user %d attempted to rebuild the alias map",
- (int) RealUid);
- HoldErrs = false;
- usrerr("Permission denied (real uid not trusted)");
- finis(false, true, EX_USAGE);
- /* NOTREACHED */
- }
- if (UseMSP)
- {
- HoldErrs = false;
- usrerr("User %d cannot rebuild aliases in mail submission program",
- (int) RealUid);
- finis(false, true, EX_USAGE);
- /* NOTREACHED */
- }
- /* FALLTHROUGH */
-
- default:
- if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
- Verbose != 0)
- {
- /*
- ** If -v and RestrictExpand, reset
- ** Verbose to prevent normal users
- ** from seeing the expansion of
- ** aliases/forwards/:include:s
- */
-
- if (tTd(65, 1))
- sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
- (int) RealUid);
- Verbose = 0;
- }
- break;
- }
- }
-
- if (MeToo)
- BlankEnvelope.e_flags |= EF_METOO;
-
- switch (OpMode)
- {
- case MD_TEST:
- /* don't have persistent host status in test mode */
- HostStatDir = NULL;
- if (Verbose == 0)
- Verbose = 2;
- BlankEnvelope.e_errormode = EM_PRINT;
- HoldErrs = false;
- break;
-
- case MD_VERIFY:
- BlankEnvelope.e_errormode = EM_PRINT;
- HoldErrs = false;
- /* arrange to exit cleanly on hangup signal */
- if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
- (void) sm_signal(SIGHUP, intsig);
- if (geteuid() != 0)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Notice: -bv may give misleading output for non-privileged user\n");
- break;
-
- case MD_FGDAEMON:
- run_in_foreground = true;
- set_op_mode(MD_DAEMON);
- /* FALLTHROUGH */
-
- case MD_DAEMON:
- vendor_daemon_setup(&BlankEnvelope);
-
- /* remove things that don't make sense in daemon mode */
- FullName = NULL;
- GrabTo = false;
-
- /* arrange to restart on hangup signal */
- if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
- sm_syslog(LOG_WARNING, NOQID,
- "daemon invoked without full pathname; kill -1 won't work");
- break;
-
- case MD_INITALIAS:
- Verbose = 2;
- BlankEnvelope.e_errormode = EM_PRINT;
- HoldErrs = false;
- /* FALLTHROUGH */
-
- default:
- /* arrange to exit cleanly on hangup signal */
- if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
- (void) sm_signal(SIGHUP, intsig);
- break;
- }
-
- /* special considerations for FullName */
- if (FullName != NULL)
- {
- char *full = NULL;
-
- /* full names can't have newlines */
- if (strchr(FullName, '\n') != NULL)
- {
- full = newstr(denlstring(FullName, true, true));
- FullName = full;
- }
-
- /* check for characters that may have to be quoted */
- if (!rfc822_string(FullName))
- {
- /*
- ** Quote a full name with special characters
- ** as a comment so crackaddr() doesn't destroy
- ** the name portion of the address.
- */
-
- FullName = addquotes(FullName, NULL);
- if (full != NULL)
- sm_free(full); /* XXX */
- }
- }
-
- /* do heuristic mode adjustment */
- if (Verbose)
- {
- /* turn off noconnect option */
- setoption('c', "F", true, false, &BlankEnvelope);
-
- /* turn on interactive delivery */
- setoption('d', "", true, false, &BlankEnvelope);
- }
-
-#ifdef VENDOR_CODE
- /* check for vendor mismatch */
- if (VendorCode != VENDOR_CODE)
- {
- message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
- getvendor(VENDOR_CODE), getvendor(VendorCode));
- }
-#endif /* VENDOR_CODE */
-
- /* check for out of date configuration level */
- if (ConfigLevel < MAXCONFIGLEVEL)
- {
- message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
- Version, MAXCONFIGLEVEL, ConfigLevel);
- }
-
- if (ConfigLevel < 3)
- UseErrorsTo = true;
-
- /* set options that were previous macros */
- if (SmtpGreeting == NULL)
- {
- if (ConfigLevel < 7 &&
- (p = macvalue('e', &BlankEnvelope)) != NULL)
- SmtpGreeting = newstr(p);
- else
- SmtpGreeting = "\201j Sendmail \201v ready at \201b";
- }
- if (UnixFromLine == NULL)
- {
- if (ConfigLevel < 7 &&
- (p = macvalue('l', &BlankEnvelope)) != NULL)
- UnixFromLine = newstr(p);
- else
- UnixFromLine = "From \201g \201d";
- }
- SmtpError[0] = '\0';
-
- /* our name for SMTP codes */
- expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
- if (jbuf[0] == '\0')
- PSTRSET(MyHostName, "localhost");
- else
- PSTRSET(MyHostName, jbuf);
- if (strchr(MyHostName, '.') == NULL)
- message("WARNING: local host name (%s) is not qualified; see cf/README: WHO AM I?",
- MyHostName);
-
- /* make certain that this name is part of the $=w class */
- setclass('w', MyHostName);
-
- /* fill in the structure of the *default* queue */
- st = stab("mqueue", ST_QUEUE, ST_FIND);
- if (st == NULL)
- syserr("No default queue (mqueue) defined");
- else
- set_def_queueval(st->s_quegrp, true);
-
- /* the indices of built-in mailers */
- st = stab("local", ST_MAILER, ST_FIND);
- if (st != NULL)
- LocalMailer = st->s_mailer;
- else if (OpMode != MD_TEST || !warn_C_flag)
- syserr("No local mailer defined");
-
- st = stab("prog", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No prog mailer defined");
- else
- {
- ProgMailer = st->s_mailer;
- clrbitn(M_MUSER, ProgMailer->m_flags);
- }
-
- st = stab("*file*", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No *file* mailer defined");
- else
- {
- FileMailer = st->s_mailer;
- clrbitn(M_MUSER, FileMailer->m_flags);
- }
-
- st = stab("*include*", ST_MAILER, ST_FIND);
- if (st == NULL)
- syserr("No *include* mailer defined");
- else
- InclMailer = st->s_mailer;
-
- if (ConfigLevel < 6)
- {
- /* heuristic tweaking of local mailer for back compat */
- if (LocalMailer != NULL)
- {
- setbitn(M_ALIASABLE, LocalMailer->m_flags);
- setbitn(M_HASPWENT, LocalMailer->m_flags);
- setbitn(M_TRYRULESET5, LocalMailer->m_flags);
- setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
- setbitn(M_CHECKPROG, LocalMailer->m_flags);
- setbitn(M_CHECKFILE, LocalMailer->m_flags);
- setbitn(M_CHECKUDB, LocalMailer->m_flags);
- }
- if (ProgMailer != NULL)
- setbitn(M_RUNASRCPT, ProgMailer->m_flags);
- if (FileMailer != NULL)
- setbitn(M_RUNASRCPT, FileMailer->m_flags);
- }
- if (ConfigLevel < 7)
- {
- if (LocalMailer != NULL)
- setbitn(M_VRFY250, LocalMailer->m_flags);
- if (ProgMailer != NULL)
- setbitn(M_VRFY250, ProgMailer->m_flags);
- if (FileMailer != NULL)
- setbitn(M_VRFY250, FileMailer->m_flags);
- }
-
- /* MIME Content-Types that cannot be transfer encoded */
- setclass('n', "multipart/signed");
-
- /* MIME message/xxx subtypes that can be treated as messages */
- setclass('s', "rfc822");
-
- /* MIME Content-Transfer-Encodings that can be encoded */
- setclass('e', "7bit");
- setclass('e', "8bit");
- setclass('e', "binary");
-
-#ifdef USE_B_CLASS
- /* MIME Content-Types that should be treated as binary */
- setclass('b', "image");
- setclass('b', "audio");
- setclass('b', "video");
- setclass('b', "application/octet-stream");
-#endif /* USE_B_CLASS */
-
- /* MIME headers which have fields to check for overflow */
- setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
- setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
-
- /* MIME headers to check for length overflow */
- setclass(macid("{checkMIMETextHeaders}"), "content-description");
-
- /* MIME headers to check for overflow and rebalance */
- setclass(macid("{checkMIMEHeaders}"), "content-disposition");
- setclass(macid("{checkMIMEHeaders}"), "content-id");
- setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
- setclass(macid("{checkMIMEHeaders}"), "content-type");
- setclass(macid("{checkMIMEHeaders}"), "mime-version");
-
- /* Macros to save in the queue file -- don't remove any */
- setclass(macid("{persistentMacros}"), "r");
- setclass(macid("{persistentMacros}"), "s");
- setclass(macid("{persistentMacros}"), "_");
- setclass(macid("{persistentMacros}"), "{if_addr}");
- setclass(macid("{persistentMacros}"), "{daemon_flags}");
-
- /* operate in queue directory */
- if (QueueDir == NULL || *QueueDir == '\0')
- {
- if (OpMode != MD_TEST)
- {
- syserr("QueueDirectory (Q) option must be set");
- ExitStat = EX_CONFIG;
- }
- }
- else
- {
- if (OpMode != MD_TEST)
- setup_queues(OpMode == MD_DAEMON);
- }
-
- /* check host status directory for validity */
- if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
- {
- /* cannot use this value */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Cannot use HostStatusDirectory = %s: %s\n",
- HostStatDir, sm_errstring(errno));
- HostStatDir = NULL;
- }
-
- if (OpMode == MD_QUEUERUN &&
- RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
- {
- struct stat stbuf;
-
- /* check to see if we own the queue directory */
- if (stat(".", &stbuf) < 0)
- syserr("main: cannot stat %s", QueueDir);
- if (stbuf.st_uid != RealUid)
- {
- /* nope, really a botch */
- HoldErrs = false;
- usrerr("You do not have permission to process the queue");
- finis(false, true, EX_NOPERM);
- /* NOTREACHED */
- }
- }
-
-#if MILTER
- /* sanity checks on milter filters */
- if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
- {
- milter_config(InputFilterList, InputFilters, MAXFILTERS);
- setup_daemon_milters();
- }
-#endif /* MILTER */
-
- /* Convert queuegroup string to qgrp number */
- if (queuegroup != NULL)
- {
- qgrp = name2qid(queuegroup);
- if (qgrp == NOQGRP)
- {
- HoldErrs = false;
- usrerr("Queue group %s unknown", queuegroup);
- finis(false, true, ExitStat);
- /* NOTREACHED */
- }
- }
-
- /* if we've had errors so far, exit now */
- if (ExitStat != EX_OK && OpMode != MD_TEST)
- {
- finis(false, true, ExitStat);
- /* NOTREACHED */
- }
-
-#if SASL
- /* sendmail specific SASL initialization */
- sm_sasl_init();
-#endif /* SASL */
-
-#if XDEBUG
- checkfd012("before main() initmaps");
-#endif /* XDEBUG */
-
- /*
- ** Do operation-mode-dependent initialization.
- */
-
- switch (OpMode)
- {
- case MD_PRINT:
- /* print the queue */
- HoldErrs = false;
- dropenvelope(&BlankEnvelope, true, false);
- (void) sm_signal(SIGPIPE, sigpipe);
- if (qgrp != NOQGRP)
- {
- int j;
-
- /* Selecting a particular queue group to run */
- for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
- {
- if (StopRequest)
- stop_sendmail();
- (void) print_single_queue(qgrp, j);
- }
- finis(false, true, EX_OK);
- /* NOTREACHED */
- }
- printqueue();
- finis(false, true, EX_OK);
- /* NOTREACHED */
- break;
-
- case MD_PRINTNQE:
- /* print number of entries in queue */
- dropenvelope(&BlankEnvelope, true, false);
- (void) sm_signal(SIGPIPE, sigpipe);
- printnqe(smioout, NULL);
- finis(false, true, EX_OK);
- /* NOTREACHED */
- break;
-
- case MD_QUEUERUN:
- /* only handle quarantining here */
- if (quarantining == NULL)
- break;
-
- if (QueueMode != QM_QUARANTINE &&
- QueueMode != QM_NORMAL)
- {
- HoldErrs = false;
- usrerr("Can not use -Q with -q%c", QueueMode);
- ExitStat = EX_USAGE;
- finis(false, true, ExitStat);
- /* NOTREACHED */
- }
- quarantine_queue(quarantining, qgrp);
- finis(false, true, EX_OK);
- break;
-
- case MD_HOSTSTAT:
- (void) sm_signal(SIGPIPE, sigpipe);
- (void) mci_traverse_persistent(mci_print_persistent, NULL);
- finis(false, true, EX_OK);
- /* NOTREACHED */
- break;
-
- case MD_PURGESTAT:
- (void) mci_traverse_persistent(mci_purge_persistent, NULL);
- finis(false, true, EX_OK);
- /* NOTREACHED */
- break;
-
- case MD_INITALIAS:
- /* initialize maps */
- initmaps();
- finis(false, true, ExitStat);
- /* NOTREACHED */
- break;
-
- case MD_SMTP:
- case MD_DAEMON:
- /* reset DSN parameters */
- DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{dsn_notify}"), NULL);
- BlankEnvelope.e_envid = NULL;
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{dsn_envid}"), NULL);
- BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{dsn_ret}"), NULL);
-
- /* don't open maps for daemon -- done below in child */
- break;
- }
-
- if (tTd(0, 15))
- {
- /* print configuration table (or at least part of it) */
- if (tTd(0, 90))
- printrules();
- for (i = 0; i < MAXMAILERS; i++)
- {
- if (Mailer[i] != NULL)
- printmailer(sm_debug_file(), Mailer[i]);
- }
- }
-
- /*
- ** Switch to the main envelope.
- */
-
- CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope,
- sm_rpool_new_x(NULL));
- MainEnvelope.e_flags = BlankEnvelope.e_flags;
-
- /*
- ** If test mode, read addresses from stdin and process.
- */
-
- if (OpMode == MD_TEST)
- {
- if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL)))
- Verbose = 2;
-
- if (Verbose)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Enter <ruleset> <address>\n");
- }
- macdefine(&(MainEnvelope.e_macro), A_PERM,
- macid("{addr_type}"), "e r");
- for (;;)
- {
- SM_TRY
- {
- (void) sm_signal(SIGINT, intindebug);
- (void) sm_releasesignal(SIGINT);
- if (Verbose == 2)
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "> ");
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
- if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf,
- sizeof(buf)) == NULL)
- testmodeline("/quit", &MainEnvelope);
- p = strchr(buf, '\n');
- if (p != NULL)
- *p = '\0';
- if (Verbose < 2)
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "> %s\n", buf);
- testmodeline(buf, &MainEnvelope);
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /*
- ** 8.10 just prints \n on interrupt.
- ** I'm printing the exception here in case
- ** sendmail is extended to raise additional
- ** exceptions in this context.
- */
-
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "\n");
- sm_exc_print(exc, smioout);
- }
- SM_END_TRY
- }
- }
-
-#if STARTTLS
- tls_ok = true;
- if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER ||
- OpMode == MD_ARPAFTP)
- {
- /* check whether STARTTLS is turned off for the client */
- if (chkclientmodifiers(D_NOTLS))
- tls_ok = false;
- }
- else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
- OpMode == MD_SMTP)
- {
- /* check whether STARTTLS is turned off for the server */
- if (chkdaemonmodifiers(D_NOTLS))
- tls_ok = false;
- }
- else /* other modes don't need STARTTLS */
- tls_ok = false;
-
- if (tls_ok)
- {
- /* basic TLS initialization */
- tls_ok = init_tls_library();
- }
-
- if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER))
- {
- /* disable TLS for client */
- setclttls(false);
- }
-#endif /* STARTTLS */
-
- /*
- ** If collecting stuff from the queue, go start doing that.
- */
-
- if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
- {
- pid_t pid = -1;
-
-#if STARTTLS
- /* init TLS for client, ignore result for now */
- (void) initclttls(tls_ok);
-#endif /* STARTTLS */
-
- /*
- ** The parent process of the caller of runqueue() needs
- ** to stay around for a possible SIGTERM. The SIGTERM will
- ** tell this process that all of the queue runners children
- ** need to be sent SIGTERM as well. At the same time, we
- ** want to return control to the command line. So we do an
- ** extra fork().
- */
-
- if (Verbose || foregroundqueue || (pid = fork()) <= 0)
- {
- /*
- ** If the fork() failed we should still try to do
- ** the queue run. If it succeeded then the child
- ** is going to start the run and wait for all
- ** of the children to finish.
- */
-
- if (pid == 0)
- {
- /* Reset global flags */
- RestartRequest = NULL;
- ShutdownRequest = NULL;
- PendingSignal = 0;
-
- /* disconnect from terminal */
- disconnect(2, CurEnv);
- }
-
- CurrentPid = getpid();
- if (qgrp != NOQGRP)
- {
- int rwgflags = RWG_NONE;
-
- /*
- ** To run a specific queue group mark it to
- ** be run, select the work group it's in and
- ** increment the work counter.
- */
-
- for (i = 0; i < NumQueue && Queue[i] != NULL;
- i++)
- Queue[i]->qg_nextrun = (time_t) -1;
- Queue[qgrp]->qg_nextrun = 0;
- if (Verbose)
- rwgflags |= RWG_VERBOSE;
- if (queuepersistent)
- rwgflags |= RWG_PERSISTENT;
- rwgflags |= RWG_FORCE;
- (void) run_work_group(Queue[qgrp]->qg_wgrp,
- rwgflags);
- }
- else
- (void) runqueue(false, Verbose,
- queuepersistent, true);
-
- /* set the title to make it easier to find */
- sm_setproctitle(true, CurEnv, "Queue control");
- (void) sm_signal(SIGCHLD, SIG_DFL);
- while (CurChildren > 0)
- {
- int status;
- pid_t ret;
-
- errno = 0;
- while ((ret = sm_wait(&status)) <= 0)
- {
- if (errno == ECHILD)
- {
- /*
- ** Oops... something got messed
- ** up really bad. Waiting for
- ** non-existent children
- ** shouldn't happen. Let's get
- ** out of here.
- */
-
- CurChildren = 0;
- break;
- }
- continue;
- }
-
- /* something is really really wrong */
- if (errno == ECHILD)
- {
- sm_syslog(LOG_ERR, NOQID,
- "queue control process: lost all children: wait returned ECHILD");
- break;
- }
-
- /* Only drop when a child gives status */
- if (WIFSTOPPED(status))
- continue;
-
- proc_list_drop(ret, status, NULL);
- }
- }
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
-
-# if SASL
- if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
- {
- /* check whether AUTH is turned off for the server */
- if (!chkdaemonmodifiers(D_NOAUTH) &&
- (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
- syserr("!sasl_server_init failed! [%s]",
- sasl_errstring(i, NULL, NULL));
- }
-# endif /* SASL */
-
- if (OpMode == MD_SMTP)
- {
- proc_list_add(CurrentPid, "Sendmail SMTP Agent",
- PROC_DAEMON, 0, -1, NULL);
-
- /* clean up background delivery children */
- (void) sm_signal(SIGCHLD, reapchild);
- }
-
- /*
- ** If a daemon, wait for a request.
- ** getrequests will always return in a child.
- ** If we should also be processing the queue, start
- ** doing it in background.
- ** We check for any errors that might have happened
- ** during startup.
- */
-
- if (OpMode == MD_DAEMON || QueueIntvl > 0)
- {
- char dtype[200];
-
- /* avoid cleanup in finis(), DaemonPid will be set below */
- DaemonPid = 0;
- if (!run_in_foreground && !tTd(99, 100))
- {
- /* put us in background */
- i = fork();
- if (i < 0)
- syserr("daemon: cannot fork");
- if (i != 0)
- {
- finis(false, true, EX_OK);
- /* NOTREACHED */
- }
-
- /*
- ** Initialize exception stack and default exception
- ** handler for child process.
- */
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- CurrentPid = getpid();
-
- sm_exc_newthread(fatal_error);
-
- /* disconnect from our controlling tty */
- disconnect(2, &MainEnvelope);
- }
-
- dtype[0] = '\0';
- if (OpMode == MD_DAEMON)
- {
- (void) sm_strlcat(dtype, "+SMTP", sizeof(dtype));
- DaemonPid = CurrentPid;
- }
- if (QueueIntvl > 0)
- {
- (void) sm_strlcat2(dtype,
- queuepersistent
- ? "+persistent-queueing@"
- : "+queueing@",
- pintvl(QueueIntvl, true),
- sizeof(dtype));
- }
- if (tTd(0, 1))
- (void) sm_strlcat(dtype, "+debugging", sizeof(dtype));
-
- sm_syslog(LOG_INFO, NOQID,
- "starting daemon (%s): %s", Version, dtype + 1);
-#if XLA
- xla_create_file();
-#endif /* XLA */
-
- /* save daemon type in a macro for possible PidFile use */
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{daemon_info}"), dtype + 1);
-
- /* save queue interval in a macro for possible PidFile use */
- macdefine(&MainEnvelope.e_macro, A_TEMP,
- macid("{queue_interval}"), pintvl(QueueIntvl, true));
-
- /* workaround: can't seem to release the signal in the parent */
- (void) sm_signal(SIGHUP, sighup);
- (void) sm_releasesignal(SIGHUP);
- (void) sm_signal(SIGTERM, sigterm);
-
- if (QueueIntvl > 0)
- {
-#if _FFR_RUNPQG
- if (qgrp != NOQGRP)
- {
- int rwgflags = RWG_NONE;
-
- /*
- ** To run a specific queue group mark it to
- ** be run, select the work group it's in and
- ** increment the work counter.
- */
-
- for (i = 0; i < NumQueue && Queue[i] != NULL;
- i++)
- Queue[i]->qg_nextrun = (time_t) -1;
- Queue[qgrp]->qg_nextrun = 0;
- if (Verbose)
- rwgflags |= RWG_VERBOSE;
- if (queuepersistent)
- rwgflags |= RWG_PERSISTENT;
- rwgflags |= RWG_FORCE;
- (void) run_work_group(Queue[qgrp]->qg_wgrp,
- rwgflags);
- }
- else
-#endif /* _FFR_RUNPQG */
- (void) runqueue(true, false, queuepersistent,
- true);
-
- /*
- ** If queuepersistent but not in daemon mode then
- ** we're going to do the queue runner monitoring here.
- ** If in daemon mode then the monitoring will happen
- ** elsewhere.
- */
-
- if (OpMode != MD_DAEMON && queuepersistent)
- {
- /*
- ** Write the pid to file
- ** XXX Overwrites sendmail.pid
- */
-
- log_sendmail_pid(&MainEnvelope);
-
- /* set the title to make it easier to find */
- sm_setproctitle(true, CurEnv, "Queue control");
- (void) sm_signal(SIGCHLD, SIG_DFL);
- while (CurChildren > 0)
- {
- int status;
- pid_t ret;
- int group;
-
- CHECK_RESTART;
- errno = 0;
- while ((ret = sm_wait(&status)) <= 0)
- {
- /*
- ** Waiting for non-existent
- ** children shouldn't happen.
- ** Let's get out of here if
- ** it occurs.
- */
-
- if (errno == ECHILD)
- {
- CurChildren = 0;
- break;
- }
- continue;
- }
-
- /* something is really really wrong */
- if (errno == ECHILD)
- {
- sm_syslog(LOG_ERR, NOQID,
- "persistent queue runner control process: lost all children: wait returned ECHILD");
- break;
- }
-
- if (WIFSTOPPED(status))
- continue;
-
- /* Probe only on a child status */
- proc_list_drop(ret, status, &group);
-
- if (WIFSIGNALED(status))
- {
- if (WCOREDUMP(status))
- {
- sm_syslog(LOG_ERR, NOQID,
- "persistent queue runner=%d core dumped, signal=%d",
- group, WTERMSIG(status));
-
- /* don't restart this */
- mark_work_group_restart(
- group, -1);
- continue;
- }
-
- sm_syslog(LOG_ERR, NOQID,
- "persistent queue runner=%d died, pid=%ld, signal=%d",
- group, (long) ret,
- WTERMSIG(status));
- }
-
- /*
- ** When debugging active, don't
- ** restart the persistent queues.
- ** But do log this as info.
- */
-
- if (sm_debug_active(&DebugNoPRestart,
- 1))
- {
- sm_syslog(LOG_DEBUG, NOQID,
- "persistent queue runner=%d, exited",
- group);
- mark_work_group_restart(group,
- -1);
- }
- CHECK_RESTART;
- }
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
-
- if (OpMode != MD_DAEMON)
- {
- char qtype[200];
-
- /*
- ** Write the pid to file
- ** XXX Overwrites sendmail.pid
- */
-
- log_sendmail_pid(&MainEnvelope);
-
- /* set the title to make it easier to find */
- qtype[0] = '\0';
- (void) sm_strlcpyn(qtype, sizeof(qtype), 4,
- "Queue runner@",
- pintvl(QueueIntvl, true),
- " for ",
- QueueDir);
- sm_setproctitle(true, CurEnv, qtype);
- for (;;)
- {
- (void) pause();
-
- CHECK_RESTART;
-
- if (doqueuerun())
- (void) runqueue(true, false,
- false, false);
- }
- }
- }
- dropenvelope(&MainEnvelope, true, false);
-
-#if STARTTLS
- /* init TLS for server, ignore result for now */
- (void) initsrvtls(tls_ok);
-#endif /* STARTTLS */
-
- nextreq:
- p_flags = getrequests(&MainEnvelope);
-
- /* drop privileges */
- (void) drop_privileges(false);
-
- /*
- ** Get authentication data
- ** Set _ macro in BlankEnvelope before calling newenvelope().
- */
-
- authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
- NULL), &forged);
- macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
-
- /* at this point we are in a child: reset state */
- sm_rpool_free(MainEnvelope.e_rpool);
- (void) newenvelope(&MainEnvelope, &MainEnvelope,
- sm_rpool_new_x(NULL));
- }
-
- if (LogLevel > 9)
- {
- /* log connection information */
- sm_syslog(LOG_INFO, NULL, "connect from %s", authinfo);
- }
-
- /*
- ** If running SMTP protocol, start collecting and executing
- ** commands. This will never return.
- */
-
- if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
- {
- char pbuf[20];
-
- /*
- ** Save some macros for check_* rulesets.
- */
-
- if (forged)
- {
- char ipbuf[103];
-
- (void) sm_snprintf(ipbuf, sizeof(ipbuf), "[%.100s]",
- anynet_ntoa(&RealHostAddr));
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{client_name}"), ipbuf);
- }
- else
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{client_name}"), RealHostName);
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{client_ptr}"), RealHostName);
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
- sm_getla();
-
- switch (RealHostAddr.sa.sa_family)
- {
-#if NETINET
- case AF_INET:
- (void) sm_snprintf(pbuf, sizeof(pbuf), "%d",
- RealHostAddr.sin.sin_port);
- break;
-#endif /* NETINET */
-#if NETINET6
- case AF_INET6:
- (void) sm_snprintf(pbuf, sizeof(pbuf), "%d",
- RealHostAddr.sin6.sin6_port);
- break;
-#endif /* NETINET6 */
- default:
- (void) sm_snprintf(pbuf, sizeof(pbuf), "0");
- break;
- }
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{client_port}"), pbuf);
-
- if (OpMode == MD_DAEMON)
- {
- ENVELOPE *saved_env;
-
- /* validate the connection */
- HoldErrs = true;
- saved_env = CurEnv;
- CurEnv = &BlankEnvelope;
- nullserver = validate_connection(&RealHostAddr,
- macvalue(macid("{client_name}"),
- &BlankEnvelope),
- &BlankEnvelope);
- if (bitset(EF_DISCARD, BlankEnvelope.e_flags))
- MainEnvelope.e_flags |= EF_DISCARD;
- CurEnv = saved_env;
- HoldErrs = false;
- }
- else if (p_flags == NULL)
- {
- p_flags = (BITMAP256 *) xalloc(sizeof(*p_flags));
- clrbitmap(p_flags);
- }
-#if STARTTLS
- if (OpMode == MD_SMTP)
- (void) initsrvtls(tls_ok);
-#endif /* STARTTLS */
-
- /* turn off profiling */
- SM_PROF(1);
- smtp(nullserver, *p_flags, &MainEnvelope);
-
- if (tTd(93, 100))
- {
- /* turn off profiling */
- SM_PROF(0);
- if (OpMode == MD_DAEMON)
- goto nextreq;
- }
- }
-
- sm_rpool_free(MainEnvelope.e_rpool);
- clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
- if (OpMode == MD_VERIFY)
- {
- set_delivery_mode(SM_VERIFY, &MainEnvelope);
- PostMasterCopy = NULL;
- }
- else
- {
- /* interactive -- all errors are global */
- MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
- }
-
- /*
- ** Do basic system initialization and set the sender
- */
-
- initsys(&MainEnvelope);
- macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
- macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
- setsender(from, &MainEnvelope, NULL, '\0', false);
- if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
- (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
- strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
- {
- auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
- RealUserName, from, warn_f_flag);
-#if SASL
- auth = false;
-#endif /* SASL */
- }
- if (auth)
- {
- char *fv;
-
- /* set the initial sender for AUTH= to $f@$j */
- fv = macvalue('f', &MainEnvelope);
- if (fv == NULL || *fv == '\0')
- MainEnvelope.e_auth_param = NULL;
- else
- {
- if (strchr(fv, '@') == NULL)
- {
- i = strlen(fv) + strlen(macvalue('j',
- &MainEnvelope)) + 2;
- p = sm_malloc_x(i);
- (void) sm_strlcpyn(p, i, 3, fv, "@",
- macvalue('j',
- &MainEnvelope));
- }
- else
- p = sm_strdup_x(fv);
- MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
- xtextify(p, "="));
- sm_free(p); /* XXX */
- }
- }
- if (macvalue('s', &MainEnvelope) == NULL)
- macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
-
- av = argv + optind;
- if (*av == NULL && !GrabTo)
- {
- MainEnvelope.e_to = NULL;
- MainEnvelope.e_flags |= EF_GLOBALERRS;
- HoldErrs = false;
- SuperSafe = SAFE_NO;
- usrerr("Recipient names must be specified");
-
- /* collect body for UUCP return */
- if (OpMode != MD_VERIFY)
- collect(InChannel, false, NULL, &MainEnvelope, true);
- finis(true, true, EX_USAGE);
- /* NOTREACHED */
- }
-
- /*
- ** Scan argv and deliver the message to everyone.
- */
-
- save_val = LogUsrErrs;
- LogUsrErrs = true;
- sendtoargv(av, &MainEnvelope);
- LogUsrErrs = save_val;
-
- /* if we have had errors sofar, arrange a meaningful exit stat */
- if (Errors > 0 && ExitStat == EX_OK)
- ExitStat = EX_USAGE;
-
-#if _FFR_FIX_DASHT
- /*
- ** If using -t, force not sending to argv recipients, even
- ** if they are mentioned in the headers.
- */
-
- if (GrabTo)
- {
- ADDRESS *q;
-
- for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
- q->q_state = QS_REMOVED;
- }
-#endif /* _FFR_FIX_DASHT */
-
- /*
- ** Read the input mail.
- */
-
- MainEnvelope.e_to = NULL;
- if (OpMode != MD_VERIFY || GrabTo)
- {
- int savederrors;
- unsigned long savedflags;
-
- /*
- ** workaround for compiler warning on Irix:
- ** do not initialize variable in the definition, but
- ** later on:
- ** warning(1548): transfer of control bypasses
- ** initialization of:
- ** variable "savederrors" (declared at line 2570)
- ** variable "savedflags" (declared at line 2571)
- ** goto giveup;
- */
-
- savederrors = Errors;
- savedflags = MainEnvelope.e_flags & EF_FATALERRS;
- MainEnvelope.e_flags |= EF_GLOBALERRS;
- MainEnvelope.e_flags &= ~EF_FATALERRS;
- Errors = 0;
- buffer_errors();
- collect(InChannel, false, NULL, &MainEnvelope, true);
-
- /* header checks failed */
- if (Errors > 0)
- {
- giveup:
- if (!GrabTo)
- {
- /* Log who the mail would have gone to */
- logundelrcpts(&MainEnvelope,
- MainEnvelope.e_message,
- 8, false);
- }
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- return -1;
- }
-
- /* bail out if message too large */
- if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
- {
- finis(true, true, ExitStat != EX_OK ? ExitStat
- : EX_DATAERR);
- /* NOTREACHED */
- return -1;
- }
-
- /* set message size */
- (void) sm_snprintf(buf, sizeof(buf), "%ld",
- MainEnvelope.e_msgsize);
- macdefine(&MainEnvelope.e_macro, A_TEMP,
- macid("{msg_size}"), buf);
-
- Errors = savederrors;
- MainEnvelope.e_flags |= savedflags;
- }
- errno = 0;
-
- if (tTd(1, 1))
- sm_dprintf("From person = \"%s\"\n",
- MainEnvelope.e_from.q_paddr);
-
- /* Check if quarantining stats should be updated */
- if (MainEnvelope.e_quarmsg != NULL)
- markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
-
- /*
- ** Actually send everything.
- ** If verifying, just ack.
- */
-
- if (Errors == 0)
- {
- if (!split_by_recipient(&MainEnvelope) &&
- bitset(EF_FATALERRS, MainEnvelope.e_flags))
- goto giveup;
- }
-
- /* make sure we deliver at least the first envelope */
- i = FastSplit > 0 ? 0 : -1;
- for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
- {
- ENVELOPE *next;
-
- e->e_from.q_state = QS_SENDER;
- if (tTd(1, 5))
- {
- sm_dprintf("main[%d]: QS_SENDER ", i);
- printaddr(sm_debug_file(), &e->e_from, false);
- }
- e->e_to = NULL;
- sm_getla();
- GrabTo = false;
-#if NAMED_BIND
- _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
- _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
-#endif /* NAMED_BIND */
- next = e->e_sibling;
- e->e_sibling = NULL;
-
- /* after FastSplit envelopes: queue up */
- sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
- e->e_sibling = next;
- }
-
- /*
- ** All done.
- ** Don't send return error message if in VERIFY mode.
- */
-
- finis(true, true, ExitStat);
- /* NOTREACHED */
- return ExitStat;
-}
-/*
-** STOP_SENDMAIL -- Stop the running program
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** exits.
-*/
-
-void
-stop_sendmail()
-{
- /* reset uid for process accounting */
- endpwent();
- (void) setuid(RealUid);
- exit(EX_OK);
-}
-/*
-** FINIS -- Clean up and exit.
-**
-** Parameters:
-** drop -- whether or not to drop CurEnv envelope
-** cleanup -- call exit() or _exit()?
-** exitstat -- exit status to use for exit() call
-**
-** Returns:
-** never
-**
-** Side Effects:
-** exits sendmail
-*/
-
-void
-finis(drop, cleanup, exitstat)
- bool drop;
- bool cleanup;
- volatile int exitstat;
-{
- char pidpath[MAXPATHLEN];
- pid_t pid;
-
- /* Still want to process new timeouts added below */
- sm_clear_events();
- (void) sm_releasesignal(SIGALRM);
-
- if (tTd(2, 1))
- {
- sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
- exitstat,
- CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
- printenvflags(CurEnv);
- }
- if (tTd(2, 9))
- printopenfds(false);
-
- SM_TRY
- /*
- ** Clean up. This might raise E:mta.quickabort
- */
-
- /* clean up temp files */
- CurEnv->e_to = NULL;
- if (drop)
- {
- if (CurEnv->e_id != NULL)
- {
- dropenvelope(CurEnv, true, false);
- sm_rpool_free(CurEnv->e_rpool);
- CurEnv->e_rpool = NULL;
-
- /* these may have pointed to the rpool */
- CurEnv->e_to = NULL;
- CurEnv->e_message = NULL;
- CurEnv->e_statmsg = NULL;
- CurEnv->e_quarmsg = NULL;
- CurEnv->e_bodytype = NULL;
- CurEnv->e_id = NULL;
- CurEnv->e_envid = NULL;
- CurEnv->e_auth_param = NULL;
- }
- else
- poststats(StatFile);
- }
-
- /* flush any cached connections */
- mci_flush(true, NULL);
-
- /* close maps belonging to this pid */
- closemaps(false);
-
-#if USERDB
- /* close UserDatabase */
- _udbx_close();
-#endif /* USERDB */
-
-#if SASL
- stop_sasl_client();
-#endif /* SASL */
-
-#if XLA
- /* clean up extended load average stuff */
- xla_all_end();
-#endif /* XLA */
-
- SM_FINALLY
- /*
- ** And exit.
- */
-
- if (LogLevel > 78)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
- (int) CurrentPid);
- if (exitstat == EX_TEMPFAIL ||
- CurEnv->e_errormode == EM_BERKNET)
- exitstat = EX_OK;
-
- /* XXX clean up queues and related data structures */
- cleanup_queues();
- pid = getpid();
-#if SM_CONF_SHM
- cleanup_shm(DaemonPid == pid);
-#endif /* SM_CONF_SHM */
-
- /* close locked pid file */
- close_sendmail_pid();
-
- if (DaemonPid == pid || PidFilePid == pid)
- {
- /* blow away the pid file */
- expand(PidFile, pidpath, sizeof(pidpath), CurEnv);
- (void) unlink(pidpath);
- }
-
- /* reset uid for process accounting */
- endpwent();
- sm_mbdb_terminate();
-#if _FFR_MEMSTAT
- (void) sm_memstat_close();
-#endif /* _FFR_MEMSTAT */
- (void) setuid(RealUid);
-#if SM_HEAP_CHECK
- /* dump the heap, if we are checking for memory leaks */
- if (sm_debug_active(&SmHeapCheck, 2))
- sm_heap_report(smioout,
- sm_debug_level(&SmHeapCheck) - 1);
-#endif /* SM_HEAP_CHECK */
- if (sm_debug_active(&SmXtrapReport, 1))
- sm_dprintf("xtrap count = %d\n", SmXtrapCount);
- if (cleanup)
- exit(exitstat);
- else
- _exit(exitstat);
- SM_END_TRY
-}
-/*
-** INTINDEBUG -- signal handler for SIGINT in -bt mode
-**
-** Parameters:
-** sig -- incoming signal.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** longjmps back to test mode loop.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-/* Type of an exception generated on SIGINT during address test mode. */
-static const SM_EXC_TYPE_T EtypeInterrupt =
-{
- SmExcTypeMagic,
- "S:mta.interrupt",
- "",
- sm_etype_printf,
- "interrupt",
-};
-
-/* ARGSUSED */
-static SIGFUNC_DECL
-intindebug(sig)
- int sig;
-{
- int save_errno = errno;
-
- FIX_SYSV_SIGNAL(sig, intindebug);
- errno = save_errno;
- CHECK_CRITICAL(sig);
- errno = save_errno;
- sm_exc_raisenew_x(&EtypeInterrupt);
- errno = save_errno;
- return SIGFUNC_RETURN;
-}
-/*
-** SIGTERM -- SIGTERM handler for the daemon
-**
-** Parameters:
-** sig -- signal number.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets ShutdownRequest which will hopefully trigger
-** the daemon to exit.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-/* ARGSUSED */
-static SIGFUNC_DECL
-sigterm(sig)
- int sig;
-{
- int save_errno = errno;
-
- FIX_SYSV_SIGNAL(sig, sigterm);
- ShutdownRequest = "signal";
- errno = save_errno;
- return SIGFUNC_RETURN;
-}
-/*
-** SIGHUP -- handle a SIGHUP signal
-**
-** Parameters:
-** sig -- incoming signal.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets RestartRequest which should cause the daemon
-** to restart.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-/* ARGSUSED */
-static SIGFUNC_DECL
-sighup(sig)
- int sig;
-{
- int save_errno = errno;
-
- FIX_SYSV_SIGNAL(sig, sighup);
- RestartRequest = "signal";
- errno = save_errno;
- return SIGFUNC_RETURN;
-}
-/*
-** SIGPIPE -- signal handler for SIGPIPE
-**
-** Parameters:
-** sig -- incoming signal.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets StopRequest which should cause the mailq/hoststatus
-** display to stop.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-/* ARGSUSED */
-static SIGFUNC_DECL
-sigpipe(sig)
- int sig;
-{
- int save_errno = errno;
-
- FIX_SYSV_SIGNAL(sig, sigpipe);
- StopRequest = true;
- errno = save_errno;
- return SIGFUNC_RETURN;
-}
-/*
-** INTSIG -- clean up on interrupt
-**
-** This just arranges to exit. It pessimizes in that it
-** may resend a message.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Unlocks the current job.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-**
-** XXX: More work is needed for this signal handler.
-*/
-
-/* ARGSUSED */
-SIGFUNC_DECL
-intsig(sig)
- int sig;
-{
- bool drop = false;
- int save_errno = errno;
-
- FIX_SYSV_SIGNAL(sig, intsig);
- errno = save_errno;
- CHECK_CRITICAL(sig);
- sm_allsignals(true);
-
- if (sig != 0 && LogLevel > 79)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
- FileName = NULL;
-
- /* 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;
-
- drop = true;
- }
- else if (OpMode != MD_TEST)
- {
- unlockqueue(CurEnv);
- }
-
- finis(drop, false, EX_OK);
- /* NOTREACHED */
-}
-/*
-** DISCONNECT -- remove our connection with any foreground process
-**
-** Parameters:
-** 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 /dev/null.
-** 2 -- also, disconnect from controlling terminal
-** (only for daemon mode).
-** e -- the current envelope.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** Trys to insure that we are immune to vagaries of
-** the controlling tty.
-*/
-
-void
-disconnect(droplev, e)
- int droplev;
- register ENVELOPE *e;
-{
- int fd;
-
- if (tTd(52, 1))
- sm_dprintf("disconnect: In %d Out %d, e=%p\n",
- sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
- sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e);
- if (tTd(52, 100))
- {
- sm_dprintf("don't\n");
- return;
- }
- if (LogLevel > 93)
- sm_syslog(LOG_DEBUG, e->e_id,
- "disconnect level %d",
- droplev);
-
- /* be sure we don't get nasty signals */
- (void) sm_signal(SIGINT, SIG_IGN);
- (void) sm_signal(SIGQUIT, SIG_IGN);
-
- /* we can't communicate with our caller, so.... */
- HoldErrs = true;
- CurEnv->e_errormode = EM_MAIL;
- Verbose = 0;
- DisConnected = true;
-
- /* all input from /dev/null */
- if (InChannel != smioin)
- {
- (void) sm_io_close(InChannel, SM_TIME_DEFAULT);
- InChannel = smioin;
- }
- if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
- SM_IO_RDONLY, NULL, smioin) == NULL)
- sm_syslog(LOG_ERR, e->e_id,
- "disconnect: sm_io_reopen(\"%s\") failed: %s",
- SM_PATH_DEVNULL, sm_errstring(errno));
-
- /*
- ** output to the transcript
- ** We also compare the fd numbers here since OutChannel
- ** might be a layer on top of smioout due to encryption
- ** (see sfsasl.c).
- */
-
- if (OutChannel != smioout &&
- sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
- sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
- {
- (void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
- OutChannel = smioout;
-
-#if 0
- /*
- ** Has smioout been closed? Reopen it.
- ** This shouldn't happen anymore, the code is here
- ** just as a reminder.
- */
-
- if (smioout->sm_magic == NULL &&
- sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
- SM_IO_WRONLY, NULL, smioout) == NULL)
- sm_syslog(LOG_ERR, e->e_id,
- "disconnect: sm_io_reopen(\"%s\") failed: %s",
- SM_PATH_DEVNULL, sm_errstring(errno));
-#endif /* 0 */
- }
- if (droplev > 0)
- {
- fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
- if (fd == -1)
- {
- sm_syslog(LOG_ERR, e->e_id,
- "disconnect: open(\"%s\") failed: %s",
- SM_PATH_DEVNULL, sm_errstring(errno));
- }
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
- if (fd >= 0)
- {
- (void) dup2(fd, STDOUT_FILENO);
- (void) dup2(fd, STDERR_FILENO);
- (void) close(fd);
- }
- }
-
- /* drop our controlling TTY completely if possible */
- if (droplev > 1)
- {
- (void) setsid();
- errno = 0;
- }
-
-#if XDEBUG
- checkfd012("disconnect");
-#endif /* XDEBUG */
-
- if (LogLevel > 71)
- sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
- (int) CurrentPid);
-
- errno = 0;
-}
-
-static void
-obsolete(argv)
- char *argv[];
-{
- register char *ap;
- register char *op;
-
- while ((ap = *++argv) != NULL)
- {
- /* Return if "--" or not an option of any form. */
- if (ap[0] != '-' || ap[1] == '-')
- return;
-
- /* Don't allow users to use "-Q." or "-Q ." */
- if ((ap[1] == 'Q' && ap[2] == '.') ||
- (ap[1] == 'Q' && argv[1] != NULL &&
- argv[1][0] == '.' && argv[1][1] == '\0'))
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Can not use -Q.\n");
- exit(EX_USAGE);
- }
-
- /* skip over options that do have a value */
- op = strchr(OPTIONS, ap[1]);
- if (op != NULL && *++op == ':' && ap[2] == '\0' &&
- ap[1] != 'd' &&
-#if defined(sony_news)
- ap[1] != 'E' && ap[1] != 'J' &&
-#endif /* defined(sony_news) */
- argv[1] != NULL && argv[1][0] != '-')
- {
- argv++;
- continue;
- }
-
- /* If -C doesn't have an argument, use sendmail.cf. */
-#define __DEFPATH "sendmail.cf"
- if (ap[1] == 'C' && ap[2] == '\0')
- {
- *argv = xalloc(sizeof(__DEFPATH) + 2);
- (void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
- "-C", __DEFPATH);
- }
-
- /* If -q doesn't have an argument, run it once. */
- if (ap[1] == 'q' && ap[2] == '\0')
- *argv = "-q0";
-
- /* If -Q doesn't have an argument, disable quarantining */
- if (ap[1] == 'Q' && ap[2] == '\0')
- *argv = "-Q.";
-
- /* if -d doesn't have an argument, use 0-99.1 */
- if (ap[1] == 'd' && ap[2] == '\0')
- *argv = "-d0-99.1";
-
-#if defined(sony_news)
- /* if -E doesn't have an argument, use -EC */
- if (ap[1] == 'E' && ap[2] == '\0')
- *argv = "-EC";
-
- /* if -J doesn't have an argument, use -JJ */
- if (ap[1] == 'J' && ap[2] == '\0')
- *argv = "-JJ";
-#endif /* defined(sony_news) */
- }
-}
-/*
-** AUTH_WARNING -- specify authorization warning
-**
-** Parameters:
-** e -- the current envelope.
-** msg -- the text of the message.
-** args -- arguments to the message.
-**
-** Returns:
-** none.
-*/
-
-void
-#ifdef __STDC__
-auth_warning(register ENVELOPE *e, const char *msg, ...)
-#else /* __STDC__ */
-auth_warning(e, msg, va_alist)
- register ENVELOPE *e;
- const char *msg;
- va_dcl
-#endif /* __STDC__ */
-{
- char buf[MAXLINE];
- SM_VA_LOCAL_DECL
-
- if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
- {
- register char *p;
- static char hostbuf[48];
-
- if (hostbuf[0] == '\0')
- {
- struct hostent *hp;
-
- hp = myhostname(hostbuf, sizeof(hostbuf));
-#if NETINET6
- if (hp != NULL)
- {
- freehostent(hp);
- hp = NULL;
- }
-#endif /* NETINET6 */
- }
-
- (void) sm_strlcpyn(buf, sizeof(buf), 2, hostbuf, ": ");
- p = &buf[strlen(buf)];
- SM_VA_START(ap, msg);
- (void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
- SM_VA_END(ap);
- addheader("X-Authentication-Warning", buf, 0, e, true);
- if (LogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Authentication-Warning: %.400s",
- buf);
- }
-}
-/*
-** GETEXTENV -- get from external environment
-**
-** Parameters:
-** envar -- the name of the variable to retrieve
-**
-** Returns:
-** The value, if any.
-*/
-
-static char *
-getextenv(envar)
- const char *envar;
-{
- char **envp;
- int l;
-
- l = strlen(envar);
- for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++)
- {
- if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
- return &(*envp)[l + 1];
- }
- return NULL;
-}
-/*
-** SM_SETUSERENV -- set an environment variable in the propagated environment
-**
-** Parameters:
-** envar -- the name of the environment variable.
-** value -- the value to which it should be set. If
-** null, this is extracted from the incoming
-** environment. If that is not set, the call
-** to sm_setuserenv is ignored.
-**
-** Returns:
-** none.
-*/
-
-void
-sm_setuserenv(envar, value)
- const char *envar;
- const char *value;
-{
- int i, l;
- char **evp = UserEnviron;
- char *p;
-
- if (value == NULL)
- {
- value = getextenv(envar);
- if (value == NULL)
- return;
- }
-
- /* XXX enforce reasonable size? */
- i = strlen(envar) + 1;
- l = strlen(value) + i + 1;
- p = (char *) xalloc(l);
- (void) sm_strlcpyn(p, l, 3, envar, "=", value);
-
- while (*evp != NULL && strncmp(*evp, p, i) != 0)
- evp++;
- if (*evp != NULL)
- {
- *evp++ = p;
- }
- else if (evp < &UserEnviron[MAXUSERENVIRON])
- {
- *evp++ = p;
- *evp = NULL;
- }
-
- /* make sure it is in our environment as well */
- if (putenv(p) < 0)
- syserr("sm_setuserenv: putenv(%s) failed", p);
-}
-/*
-** DUMPSTATE -- dump state
-**
-** For debugging.
-*/
-
-void
-dumpstate(when)
- char *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);
- if (j != NULL)
- {
- if (!wordinclass(j, 'w'))
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "*** $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)",
- NextMacroId, MAXMACROID);
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
- printopenfds(true);
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
- mci_dump_all(smioout, true);
- rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
- if (rs > 0)
- {
- int status;
- register char **pvp;
- char *pv[MAXATOM + 1];
-
- pv[0] = NULL;
- status = REWRITE(pv, rs, CurEnv);
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "--- ruleset debug_dumpstate returns stat %d, pv: ---",
- status);
- for (pvp = pv; *pvp != NULL; pvp++)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
- }
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
-}
-
-#ifdef SIGUSR1
-/*
-** SIGUSR1 -- Signal a request to dump state.
-**
-** Parameters:
-** sig -- calling signal.
-**
-** Returns:
-** none.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-**
-** XXX: More work is needed for this signal handler.
-*/
-
-/* ARGSUSED */
-static SIGFUNC_DECL
-sigusr1(sig)
- int sig;
-{
- int save_errno = errno;
-
- FIX_SYSV_SIGNAL(sig, sigusr1);
- errno = save_errno;
- CHECK_CRITICAL(sig);
- dumpstate("user signal");
-# if SM_HEAP_CHECK
- dumpstab();
-# endif /* SM_HEAP_CHECK */
- errno = save_errno;
- return SIGFUNC_RETURN;
-}
-#endif /* SIGUSR1 */
-
-/*
-** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
-**
-** Parameters:
-** to_real_uid -- if set, drop to the real uid instead
-** of the RunAsUser.
-**
-** Returns:
-** EX_OSERR if the setuid failed.
-** EX_OK otherwise.
-*/
-
-int
-drop_privileges(to_real_uid)
- bool to_real_uid;
-{
- int rval = EX_OK;
- GIDSET_T emptygidset[1];
-
- if (tTd(47, 1))
- sm_dprintf("drop_privileges(%d): Real[UG]id=%d:%d, get[ug]id=%d:%d, gete[ug]id=%d:%d, RunAs[UG]id=%d:%d\n",
- (int) to_real_uid,
- (int) RealUid, (int) RealGid,
- (int) getuid(), (int) getgid(),
- (int) geteuid(), (int) getegid(),
- (int) RunAsUid, (int) RunAsGid);
-
- if (to_real_uid)
- {
- RunAsUserName = RealUserName;
- RunAsUid = RealUid;
- RunAsGid = RealGid;
- EffGid = RunAsGid;
- }
-
- /* make sure no one can grab open descriptors for secret files */
- endpwent();
- sm_mbdb_terminate();
-
- /* reset group permissions; these can be set later */
- emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
-
- /*
- ** Notice: on some OS (Linux...) the setgroups() call causes
- ** a logfile entry if sendmail is not run by root.
- ** However, it is unclear (no POSIX standard) whether
- ** setgroups() can only succeed if executed by root.
- ** So for now we keep it as it is; if you want to change it, use
- ** if (geteuid() == 0 && setgroups(1, emptygidset) == -1)
- */
-
- if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
- {
- syserr("drop_privileges: setgroups(1, %d) failed",
- (int) emptygidset[0]);
- rval = EX_OSERR;
- }
-
- /* reset primary group id */
- if (to_real_uid)
- {
- /*
- ** Drop gid to real gid.
- ** On some OS we must reset the effective[/real[/saved]] gid,
- ** and then use setgid() to finally drop all group privileges.
- ** Later on we check whether we can get back the
- ** effective gid.
- */
-
-#if HASSETEGID
- if (setegid(RunAsGid) < 0)
- {
- syserr("drop_privileges: setegid(%d) failed",
- (int) RunAsGid);
- rval = EX_OSERR;
- }
-#else /* HASSETEGID */
-# if HASSETREGID
- if (setregid(RunAsGid, RunAsGid) < 0)
- {
- syserr("drop_privileges: setregid(%d, %d) failed",
- (int) RunAsGid, (int) RunAsGid);
- rval = EX_OSERR;
- }
-# else /* HASSETREGID */
-# if HASSETRESGID
- if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
- {
- syserr("drop_privileges: setresgid(%d, %d, %d) failed",
- (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
- rval = EX_OSERR;
- }
-# endif /* HASSETRESGID */
-# endif /* HASSETREGID */
-#endif /* HASSETEGID */
- }
- if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
- {
- if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
- {
- syserr("drop_privileges: setgid(%d) failed",
- (int) RunAsGid);
- rval = EX_OSERR;
- }
- errno = 0;
- if (rval == EX_OK && getegid() != RunAsGid)
- {
- syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d",
- (int) getegid(), (int) RunAsGid);
- rval = EX_OSERR;
- }
- }
-
- /* fiddle with uid */
- if (to_real_uid || RunAsUid != 0)
- {
- uid_t euid;
-
- /*
- ** Try to setuid(RunAsUid).
- ** euid must be RunAsUid,
- ** ruid must be RunAsUid unless (e|r)uid wasn't 0
- ** and we didn't have to drop privileges to the real uid.
- */
-
- if (setuid(RunAsUid) < 0 ||
- geteuid() != RunAsUid ||
- (getuid() != RunAsUid &&
- (to_real_uid || geteuid() == 0 || getuid() == 0)))
- {
-#if HASSETREUID
- /*
- ** if ruid != RunAsUid, euid == RunAsUid, then
- ** try resetting just the real uid, then using
- ** setuid() to drop the saved-uid as well.
- */
-
- if (geteuid() == RunAsUid)
- {
- if (setreuid(RunAsUid, -1) < 0)
- {
- syserr("drop_privileges: setreuid(%d, -1) failed",
- (int) RunAsUid);
- rval = EX_OSERR;
- }
- if (setuid(RunAsUid) < 0)
- {
- syserr("drop_privileges: second setuid(%d) attempt failed",
- (int) RunAsUid);
- rval = EX_OSERR;
- }
- }
- else
-#endif /* HASSETREUID */
- {
- syserr("drop_privileges: setuid(%d) failed",
- (int) RunAsUid);
- rval = EX_OSERR;
- }
- }
- euid = geteuid();
- 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 ((to_real_uid || RunAsGid != 0) &&
- rval == EX_OK && RunAsGid != EffGid &&
- getuid() != 0 && geteuid() != 0)
- {
- errno = 0;
- if (setgid(EffGid) == 0)
- {
- syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
- (int) EffGid);
- rval = EX_OSERR;
- }
- }
-
- if (tTd(47, 5))
- {
- sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
- (int) geteuid(), (int) getuid(),
- (int) getegid(), (int) getgid());
- sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
- (int) RunAsUid, (int) RunAsGid);
- if (tTd(47, 10))
- sm_dprintf("drop_privileges: rval = %d\n", rval);
- }
- return rval;
-}
-/*
-** FILL_FD -- make sure a file descriptor has been properly allocated
-**
-** Used to make sure that stdin/out/err are allocated on startup
-**
-** Parameters:
-** fd -- the file descriptor to be filled.
-** where -- a string used for logging. If NULL, this is
-** being called on startup, and logging should
-** not be done.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** possibly changes MissingFds
-*/
-
-void
-fill_fd(fd, where)
- int fd;
- char *where;
-{
- int i;
- struct stat stbuf;
-
- if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
- return;
-
- if (where != NULL)
- syserr("fill_fd: %s: fd %d not open", where, fd);
- else
- MissingFds |= 1 << fd;
- i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
- if (i < 0)
- {
- syserr("!fill_fd: %s: cannot open %s",
- where == NULL ? "startup" : where, SM_PATH_DEVNULL);
- }
- if (fd != i)
- {
- (void) dup2(i, fd);
- (void) close(i);
- }
-}
-/*
-** SM_PRINTOPTIONS -- print options
-**
-** Parameters:
-** options -- array of options.
-**
-** Returns:
-** none.
-*/
-
-static void
-sm_printoptions(options)
- char **options;
-{
- int ll;
- char **av;
-
- av = options;
- ll = 7;
- while (*av != NULL)
- {
- if (ll + strlen(*av) > 63)
- {
- sm_dprintf("\n");
- ll = 0;
- }
- if (ll == 0)
- sm_dprintf("\t\t");
- else
- sm_dprintf(" ");
- sm_dprintf("%s", *av);
- ll += strlen(*av++) + 1;
- }
- sm_dprintf("\n");
-}
-
-/*
-** TO8BIT -- convert \octal sequences in a test mode input line
-**
-** Parameters:
-** str -- the input line.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** replaces \0octal in str with octal value.
-*/
-
-static bool to8bit __P((char *));
-
-static bool
-to8bit(str)
- char *str;
-{
- int c, len;
- char *out, *in;
- bool changed;
-
- if (str == NULL)
- return false;
- in = out = str;
- changed = false;
- len = 0;
- while ((c = (*str++ & 0377)) != '\0')
- {
- int oct, nxtc;
-
- ++len;
- if (c == '\\' &&
- (nxtc = (*str & 0377)) == '0')
- {
- oct = 0;
- while ((nxtc = (*str & 0377)) != '\0' &&
- isascii(nxtc) && isdigit(nxtc))
- {
- oct <<= 3;
- oct += nxtc - '0';
- ++str;
- ++len;
- }
- changed = true;
- c = oct;
- }
- *out++ = c;
- }
- *out++ = c;
- if (changed)
- {
- char *q;
-
- q = quote_internal_chars(in, in, &len);
- if (q != in)
- sm_strlcpy(in, q, len);
- }
- return changed;
-}
-
-/*
-** TESTMODELINE -- process a test mode input line
-**
-** Parameters:
-** line -- the input line.
-** e -- the current environment.
-** Syntax:
-** # a comment
-** .X process X as a configuration line
-** =X dump a configuration item (such as mailers)
-** $X dump a macro or class
-** /X try an activity
-** X normal process through rule set X
-*/
-
-static void
-testmodeline(line, e)
- char *line;
- ENVELOPE *e;
-{
- register char *p;
- char *q;
- auto char *delimptr;
- int mid;
- int i, rs;
- STAB *map;
- char **s;
- struct rewrite *rw;
- ADDRESS a;
- char *lbp;
- auto int lbs;
- static int tryflags = RF_COPYNONE;
- char exbuf[MAXLINE];
- char lbuf[MAXLINE];
- extern unsigned char TokTypeNoC[];
- bool eightbit;
-
- /* skip leading spaces */
- while (*line == ' ')
- line++;
-
- lbp = NULL;
- eightbit = false;
- switch (line[0])
- {
- case '#':
- case '\0':
- return;
-
- case '?':
- help("-bt", e);
- return;
-
- case '.': /* config-style settings */
- switch (line[1])
- {
- case 'D':
- mid = macid_parse(&line[2], &delimptr);
- if (mid == 0)
- return;
- lbs = sizeof(lbuf);
- lbp = translate_dollars(delimptr, lbuf, &lbs);
- macdefine(&e->e_macro, A_TEMP, mid, lbp);
- if (lbp != lbuf)
- SM_FREE(lbp);
- break;
-
- case 'C':
- if (line[2] == '\0') /* not to call syserr() */
- return;
-
- mid = macid_parse(&line[2], &delimptr);
- if (mid == 0)
- return;
- lbs = sizeof(lbuf);
- lbp = translate_dollars(delimptr, lbuf, &lbs);
- expand(lbp, exbuf, sizeof(exbuf), e);
- if (lbp != lbuf)
- SM_FREE(lbp);
- p = exbuf;
- while (*p != '\0')
- {
- register char *wd;
- char delim;
-
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
- wd = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- delim = *p;
- *p = '\0';
- if (wd[0] != '\0')
- setclass(mid, wd);
- *p = delim;
- }
- break;
-
- case '\0':
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Usage: .[DC]macro value(s)\n");
- break;
-
- default:
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Unknown \".\" command %s\n", line);
- break;
- }
- return;
-
- case '=': /* config-style settings */
- switch (line[1])
- {
- case 'S': /* dump rule set */
- rs = strtorwset(&line[2], NULL, ST_FIND);
- if (rs < 0)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Undefined ruleset %s\n", &line[2]);
- return;
- }
- rw = RewriteRules[rs];
- if (rw == NULL)
- return;
- do
- {
- (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
- 'R');
- s = rw->r_lhs;
- while (*s != NULL)
- {
- xputs(smioout, *s++);
- (void) sm_io_putc(smioout,
- SM_TIME_DEFAULT, ' ');
- }
- (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
- '\t');
- (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
- '\t');
- s = rw->r_rhs;
- while (*s != NULL)
- {
- xputs(smioout, *s++);
- (void) sm_io_putc(smioout,
- SM_TIME_DEFAULT, ' ');
- }
- (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
- '\n');
- } while ((rw = rw->r_next) != NULL);
- break;
-
- case 'M':
- for (i = 0; i < MAXMAILERS; i++)
- {
- if (Mailer[i] != NULL)
- printmailer(smioout, Mailer[i]);
- }
- break;
-
- case '\0':
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Usage: =Sruleset or =M\n");
- break;
-
- default:
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Unknown \"=\" command %s\n", line);
- break;
- }
- return;
-
- case '-': /* set command-line-like opts */
- switch (line[1])
- {
- case 'd':
- tTflag(&line[2]);
- break;
-
- case '\0':
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Usage: -d{debug arguments}\n");
- break;
-
- default:
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Unknown \"-\" command %s\n", line);
- break;
- }
- return;
-
- case '$':
- if (line[1] == '=')
- {
- mid = macid(&line[2]);
- if (mid != 0)
- stabapply(dump_class, mid);
- return;
- }
- mid = macid(&line[1]);
- if (mid == 0)
- return;
- p = macvalue(mid, e);
- if (p == NULL)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Undefined\n");
- else
- {
- xputs(smioout, p);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "\n");
- }
- return;
-
- case '/': /* miscellaneous commands */
- p = &line[strlen(line)];
- while (--p >= line && isascii(*p) && isspace(*p))
- *p = '\0';
- p = strpbrk(line, " \t");
- if (p != NULL)
- {
- while (isascii(*p) && isspace(*p))
- *p++ = '\0';
- }
- else
- p = "";
- if (line[1] == '\0')
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Usage: /[canon|map|mx|parse|try|tryflags]\n");
- return;
- }
- if (sm_strcasecmp(&line[1], "quit") == 0)
- {
- CurEnv->e_id = NULL;
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- if (sm_strcasecmp(&line[1], "mx") == 0)
- {
-#if NAMED_BIND
- /* look up MX records */
- int nmx;
- auto int rcode;
- char *mxhosts[MAXMXHOSTS + 1];
-
- if (*p == '\0')
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Usage: /mx address\n");
- return;
- }
- nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true,
- NULL);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "getmxrr(%s) returns %d value(s):\n",
- p, nmx);
- for (i = 0; i < nmx; i++)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "\t%s\n", mxhosts[i]);
-#else /* NAMED_BIND */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "No MX code compiled in\n");
-#endif /* NAMED_BIND */
- }
- else if (sm_strcasecmp(&line[1], "canon") == 0)
- {
- char host[MAXHOSTNAMELEN];
-
- if (*p == '\0')
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Usage: /canon address\n");
- return;
- }
- else if (sm_strlcpy(host, p, sizeof(host)) >= sizeof(host))
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Name too long\n");
- return;
- }
- (void) getcanonname(host, sizeof(host), !HasWildcardMX,
- NULL);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "getcanonname(%s) returns %s\n",
- p, host);
- }
- else if (sm_strcasecmp(&line[1], "map") == 0)
- {
- auto int rcode = EX_OK;
- char *av[2];
-
- if (*p == '\0')
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Usage: /map mapname key\n");
- return;
- }
- for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
- continue;
- if (*q == '\0')
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "No key specified\n");
- return;
- }
- *q++ = '\0';
- map = stab(p, ST_MAP, ST_FIND);
- if (map == NULL)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Map named \"%s\" not found\n", p);
- return;
- }
- if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
- !openmap(&(map->s_map)))
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Map named \"%s\" not open\n", p);
- return;
- }
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "map_lookup: %s (%s) ", p, q);
- av[0] = q;
- av[1] = NULL;
- p = (*map->s_map.map_class->map_lookup)
- (&map->s_map, q, av, &rcode);
- if (p == NULL)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "no match (%d)\n",
- rcode);
- else
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "returns %s (%d)\n", p,
- rcode);
- }
- else if (sm_strcasecmp(&line[1], "try") == 0)
- {
- MAILER *m;
- STAB *st;
- auto int rcode = EX_OK;
-
- q = strpbrk(p, " \t");
- if (q != NULL)
- {
- while (isascii(*q) && isspace(*q))
- *q++ = '\0';
- }
- if (q == NULL || *q == '\0')
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Usage: /try mailer address\n");
- return;
- }
- st = stab(p, ST_MAILER, ST_FIND);
- if (st == NULL)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Unknown mailer %s\n", p);
- return;
- }
- m = st->s_mailer;
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Trying %s %s address %s for mailer %s\n",
- bitset(RF_HEADERADDR, tryflags) ? "header"
- : "envelope",
- bitset(RF_SENDERADDR, tryflags) ? "sender"
- : "recipient", q, p);
- p = remotename(q, m, tryflags, &rcode, CurEnv);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Rcode = %d, addr = %s\n",
- rcode, p == NULL ? "<NULL>" : p);
- e->e_to = NULL;
- }
- else if (sm_strcasecmp(&line[1], "tryflags") == 0)
- {
- if (*p == '\0')
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
- return;
- }
- for (; *p != '\0'; p++)
- {
- switch (*p)
- {
- case 'H':
- case 'h':
- tryflags |= RF_HEADERADDR;
- break;
-
- case 'E':
- case 'e':
- tryflags &= ~RF_HEADERADDR;
- break;
-
- case 'S':
- case 's':
- tryflags |= RF_SENDERADDR;
- break;
-
- case 'R':
- case 'r':
- tryflags &= ~RF_SENDERADDR;
- break;
- }
- }
- exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
- exbuf[1] = ' ';
- exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
- exbuf[3] = '\0';
- macdefine(&e->e_macro, A_TEMP,
- macid("{addr_type}"), exbuf);
- }
- else if (sm_strcasecmp(&line[1], "parse") == 0)
- {
- if (*p == '\0')
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Usage: /parse address\n");
- return;
- }
- q = crackaddr(p, e);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Cracked address = ");
- xputs(smioout, q);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "\nParsing %s %s address\n",
- bitset(RF_HEADERADDR, tryflags) ?
- "header" : "envelope",
- bitset(RF_SENDERADDR, tryflags) ?
- "sender" : "recipient");
- if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
- == NULL)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Cannot parse\n");
- else if (a.q_host != NULL && a.q_host[0] != '\0')
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "mailer %s, host %s, user %s\n",
- a.q_mailer->m_name,
- a.q_host,
- a.q_user);
- else
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "mailer %s, user %s\n",
- a.q_mailer->m_name,
- a.q_user);
- e->e_to = NULL;
- }
- else if (sm_strcasecmp(&line[1], "header") == 0)
- {
- unsigned long ul;
-
- ul = chompheader(p, CHHDR_CHECK|CHHDR_USER, NULL, e);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "ul = %lu\n", ul);
- }
- else
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Unknown \"/\" command %s\n",
- line);
- }
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
- return;
- }
-
- for (p = line; isascii(*p) && isspace(*p); p++)
- continue;
- q = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p == '\0')
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "No address!\n");
- return;
- }
- *p = '\0';
- if (tTd(23, 101))
- eightbit = to8bit(p + 1);
- if (invalidaddr(p + 1, NULL, true))
- return;
- do
- {
- register char **pvp;
- char pvpbuf[PSBUFSIZE];
-
- pvp = prescan(++p, ',', pvpbuf, sizeof(pvpbuf), &delimptr,
- ConfigLevel >= 9 ? TokTypeNoC : ExtTokenTab, false);
- if (pvp == NULL)
- continue;
- p = q;
- while (*p != '\0')
- {
- int status;
-
- rs = strtorwset(p, NULL, ST_FIND);
- if (rs < 0)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Undefined ruleset %s\n",
- p);
- break;
- }
- status = REWRITE(pvp, rs, e);
- if (status != EX_OK)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "== Ruleset %s (%d) status %d\n",
- p, rs, status);
- else if (eightbit)
- {
- cataddr(pvp, NULL, exbuf, sizeof(exbuf), '\0',
- true);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "cataddr: %s\n",
- str2prt(exbuf));
- }
- while (*p != '\0' && *p++ != ',')
- continue;
- }
- } while (*(p = delimptr) != '\0');
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
-}
-
-static void
-dump_class(s, id)
- register STAB *s;
- int id;
-{
- if (s->s_symtype != ST_CLASS)
- return;
- if (bitnset(bitidx(id), s->s_class))
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "%s\n", s->s_name);
-}
-
-/*
-** An exception type used to create QuickAbort exceptions.
-** This is my first cut at converting QuickAbort from longjmp to exceptions.
-** These exceptions have a single integer argument, which is the argument
-** to longjmp in the original code (either 1 or 2). I don't know the
-** significance of 1 vs 2: the calls to setjmp don't care.
-*/
-
-const SM_EXC_TYPE_T EtypeQuickAbort =
-{
- SmExcTypeMagic,
- "E:mta.quickabort",
- "i",
- sm_etype_printf,
- "quick abort %0",
-};
diff --git a/contrib/sendmail/src/map.c b/contrib/sendmail/src/map.c
deleted file mode 100644
index 4248fd9..0000000
--- a/contrib/sendmail/src/map.c
+++ /dev/null
@@ -1,7989 +0,0 @@
-/*
- * Copyright (c) 1998-2007 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.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: map.c,v 8.699 2007/10/10 00:06:45 ca Exp $")
-
-#if LDAPMAP
-# include <sm/ldap.h>
-#endif /* LDAPMAP */
-
-#if NDBM
-# include <ndbm.h>
-# ifdef R_FIRST
- ERROR README: You are running the Berkeley DB version of ndbm.h. See
- 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 /* R_FIRST */
-#endif /* NDBM */
-#if NEWDB
-# include "sm/bdb.h"
-#endif /* NEWDB */
-#if NIS
- struct dom_binding; /* forward reference needed on IRIX */
-# include <rpcsvc/ypclnt.h>
-# if NDBM
-# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
-# endif /* NDBM */
-#endif /* NIS */
-
-#include "map.h"
-
-#if 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 *, char[], int));
-static void map_close __P((STAB *, int));
-static void map_init __P((STAB *, int));
-#ifdef LDAPMAP
-static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
-#endif /* LDAPMAP */
-#if NISPLUS
-static bool nisplus_getcanonname __P((char *, int, int *));
-#endif /* NISPLUS */
-#if 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 *));
-#if SOCKETMAP
-static STAB *socket_map_findconn __P((const char*));
-
-/* XXX arbitrary limit for sanity */
-# define SOCKETMAP_MAXL 1000000
-#endif /* SOCKETMAP */
-
-/* default error message for trying to open a map in write mode */
-#ifdef ENOSYS
-# define SM_EMAPCANTWRITE ENOSYS
-#else /* ENOSYS */
-# ifdef EFTYPE
-# define SM_EMAPCANTWRITE EFTYPE
-# else /* EFTYPE */
-# define SM_EMAPCANTWRITE ENXIO
-# endif /* EFTYPE */
-#endif /* ENOSYS */
-
-/*
-** MAP.C -- implementations for various map classes.
-**
-** Each map class implements a series of functions:
-**
-** bool map_parse(MAP *map, char *args)
-** Parse the arguments from the config file. Return true
-** if they were ok, false otherwise. Fill in map with the
-** values.
-**
-** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
-** Look up the key in the given map. If found, do any
-** rewriting the map wants (including "args" if desired)
-** and return the value. Set *pstat to the appropriate status
-** on error and return NULL. Args will be NULL if called
-** from the alias routines, although this should probably
-** not be relied upon. It is suggested you call map_rewrite
-** to return the results -- it takes care of null termination
-** and uses a dynamically expanded buffer as needed.
-**
-** void map_store(MAP *map, char *key, char *value)
-** Store the key:value pair in the map.
-**
-** bool map_open(MAP *map, int mode)
-** Open the map for the indicated mode. Mode should
-** be either O_RDONLY or O_RDWR. Return true if it
-** was opened successfully, false otherwise. If the open
-** failed and the MF_OPTIONAL flag is not set, it should
-** also print an error. If the MF_ALIAS bit is set
-** and this map class understands the @:@ convention, it
-** should call aliaswait() before returning.
-**
-** void map_close(MAP *map)
-** Close the map.
-**
-** This file also includes the implementation for getcanonname.
-** It is currently implemented in a pretty ad-hoc manner; it ought
-** to be more properly integrated into the map structure.
-*/
-
-#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
-# define LOCK_ON_OPEN 1 /* we can open/create a locked file */
-#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
-# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
-#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
-
-/*
-** MAP_PARSEARGS -- parse config line arguments for database lookup
-**
-** This is a generic version of the map_parse method.
-**
-** Parameters:
-** map -- the map being initialized.
-** ap -- a pointer to the args on the config line.
-**
-** Returns:
-** true -- if everything parsed OK.
-** false -- otherwise.
-**
-** Side Effects:
-** null terminates the filename; stores it in map
-*/
-
-bool
-map_parseargs(map, ap)
- MAP *map;
- char *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))
- 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 'a':
- map->map_app = ++p;
- break;
-
- case 'T':
- map->map_tapp = ++p;
- break;
-
- case 'k':
- while (isascii(*++p) && isspace(*p))
- continue;
- map->map_keycolnm = p;
- break;
-
- case 'v':
- while (isascii(*++p) && isspace(*p))
- continue;
- map->map_valcolnm = p;
- 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;
-
- case 't':
- map->map_mflags |= MF_NODEFER;
- break;
-
-
- case 'S':
- map->map_spacesub = *++p;
- break;
-
- case 'D':
- map->map_mflags |= MF_DEFER;
- break;
-
- default:
- syserr("Illegal option %c map %s", *p, map->map_mname);
- break;
- }
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
- }
- if (map->map_app != NULL)
- map->map_app = newstr(map->map_app);
- if (map->map_tapp != NULL)
- map->map_tapp = newstr(map->map_tapp);
- if (map->map_keycolnm != NULL)
- map->map_keycolnm = newstr(map->map_keycolnm);
- if (map->map_valcolnm != NULL)
- map->map_valcolnm = newstr(map->map_valcolnm);
-
- if (*p != '\0')
- {
- map->map_file = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
- map->map_file = newstr(map->map_file);
- }
-
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
- if (*p != '\0')
- map->map_rebuild = newstr(p);
-
- if (map->map_file == NULL &&
- !bitset(MCF_OPTFILE, map->map_class->map_cflags))
- {
- syserr("No file name for %s map %s",
- map->map_class->map_cname, map->map_mname);
- return false;
- }
- return true;
-}
-/*
-** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
-**
-** It also adds the map_app string. It can be used as a utility
-** in the map_lookup method.
-**
-** Parameters:
-** map -- the map that causes this.
-** s -- the string to rewrite, NOT necessarily null terminated.
-** slen -- the length of s.
-** av -- arguments to interpolate into buf.
-**
-** Returns:
-** Pointer to rewritten result. This is static data that
-** should be copied if it is to be saved!
-*/
-
-char *
-map_rewrite(map, s, slen, av)
- register MAP *map;
- register const char *s;
- size_t slen;
- char **av;
-{
- register char *bp;
- register char c;
- char **avp;
- register char *ap;
- size_t l;
- size_t len;
- static size_t buflen = 0;
- static char *buf = NULL;
-
- if (tTd(39, 1))
- {
- sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
- if (av == NULL)
- sm_dprintf(" (nullv)");
- else
- {
- for (avp = av; *avp != NULL; avp++)
- sm_dprintf("\n\t%s", *avp);
- }
- sm_dprintf("\n");
- }
-
- /* count expected size of output (can safely overestimate) */
- l = len = slen;
- if (av != NULL)
- {
- const char *sp = s;
-
- while (l-- > 0 && (c = *sp++) != '\0')
- {
- if (c != '%')
- continue;
- if (l-- <= 0)
- break;
- c = *sp++;
- if (!(isascii(c) && isdigit(c)))
- continue;
- for (avp = av; --c >= '0' && *avp != NULL; avp++)
- continue;
- if (*avp == NULL)
- continue;
- len += strlen(*avp);
- }
- }
- if (map->map_app != NULL)
- len += strlen(map->map_app);
- if (buflen < ++len)
- {
- /* need to malloc additional space */
- buflen = len;
- if (buf != NULL)
- sm_free(buf);
- buf = sm_pmalloc_x(buflen);
- }
-
- bp = buf;
- if (av == NULL)
- {
- memmove(bp, s, slen);
- bp += slen;
-
- /* assert(len > slen); */
- len -= slen;
- }
- else
- {
- while (slen-- > 0 && (c = *s++) != '\0')
- {
- if (c != '%')
- {
- pushc:
- if (len-- <= 1)
- break;
- *bp++ = c;
- continue;
- }
- if (slen-- <= 0 || (c = *s++) == '\0')
- c = '%';
- if (c == '%')
- goto pushc;
- if (!(isascii(c) && isdigit(c)))
- {
- if (len-- <= 1)
- break;
- *bp++ = '%';
- goto pushc;
- }
- for (avp = av; --c >= '0' && *avp != NULL; avp++)
- continue;
- if (*avp == NULL)
- continue;
-
- /* transliterate argument into output string */
- for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
- *bp++ = c;
- }
- }
- if (map->map_app != NULL && len > 0)
- (void) sm_strlcpy(bp, map->map_app, len);
- else
- *bp = '\0';
- if (tTd(39, 1))
- sm_dprintf("map_rewrite => %s\n", buf);
- return buf;
-}
-/*
-** INITMAPS -- rebuild alias maps
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-*/
-
-void
-initmaps()
-{
-#if XDEBUG
- checkfd012("entering initmaps");
-#endif /* XDEBUG */
- stabapply(map_init, 0);
-#if XDEBUG
- checkfd012("exiting initmaps");
-#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.
-*/
-
-/* ARGSUSED1 */
-static void
-map_init(s, unused)
- register STAB *s;
- int unused;
-{
- register MAP *map;
-
- /* has to be a map */
- if (s->s_symtype != ST_MAP)
- return;
-
- map = &s->s_map;
- if (!bitset(MF_VALID, map->map_mflags))
- return;
-
- if (tTd(38, 2))
- sm_dprintf("map_init(%s:%s, %s)\n",
- map->map_class->map_cname == NULL ? "NULL" :
- map->map_class->map_cname,
- map->map_mname == NULL ? "NULL" : map->map_mname,
- map->map_file == NULL ? "NULL" : map->map_file);
-
- if (!bitset(MF_ALIAS, map->map_mflags) ||
- !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
- {
- if (tTd(38, 3))
- sm_dprintf("\tnot rebuildable\n");
- return;
- }
-
- /* if already open, close it (for nested open) */
- if (bitset(MF_OPEN, map->map_mflags))
- {
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
- }
-
- (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))
- {
- restore = true;
- HoldErrs = true;
- QuickAbort = false;
- }
-
- errno = 0;
- if (map->map_class->map_open(map, O_RDONLY))
- {
- if (tTd(38, 4))
- sm_dprintf("openmap()\t%s:%s %s: valid\n",
- map->map_class->map_cname == NULL ? "NULL" :
- map->map_class->map_cname,
- map->map_mname == NULL ? "NULL" :
- map->map_mname,
- map->map_file == NULL ? "NULL" :
- map->map_file);
- map->map_mflags |= MF_OPEN;
- map->map_pid = CurrentPid;
- }
- else
- {
- if (tTd(38, 4))
- sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
- map->map_class->map_cname == NULL ? "NULL" :
- map->map_class->map_cname,
- map->map_mname == NULL ? "NULL" :
- map->map_mname,
- map->map_file == NULL ? "NULL" :
- map->map_file,
- errno == 0 ? "" : ": ",
- errno == 0 ? "" : sm_errstring(errno));
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- {
- extern MAPCLASS BogusMapClass;
-
- map->map_orgclass = map->map_class;
- map->map_class = &BogusMapClass;
- map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
- map->map_pid = CurrentPid;
- }
- 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.
-**
-** Parameters:
-** bogus -- only close bogus maps.
-**
-** Returns:
-** none.
-*/
-
-void
-closemaps(bogus)
- bool bogus;
-{
- stabapply(map_close, bogus);
-}
-/*
-** MAP_CLOSE -- close a map opened by the current pid.
-**
-** Parameters:
-** s -- STAB entry: if map: try to close
-** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
-**
-** Returns:
-** none.
-*/
-
-/* ARGSUSED1 */
-static void
-map_close(s, bogus)
- register STAB *s;
- int bogus; /* int because of stabapply(), used as bool */
-{
- MAP *map;
- extern MAPCLASS BogusMapClass;
-
- if (s->s_symtype != ST_MAP)
- return;
-
- map = &s->s_map;
-
- /*
- ** close the map iff:
- ** it is valid and open and opened by this process
- ** and (!bogus or it's a bogus map or it is not persistent)
- ** negate this: return iff
- ** it is not valid or it is not open or not opened by this process
- ** or (bogus and it's not a bogus map and it's not not-persistent)
- */
-
- if (!bitset(MF_VALID, map->map_mflags) ||
- !bitset(MF_OPEN, map->map_mflags) ||
- bitset(MF_CLOSING, map->map_mflags) ||
- map->map_pid != CurrentPid ||
- (bogus && map->map_class != &BogusMapClass &&
- !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
- return;
-
- if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
- map->map_orgclass != &BogusMapClass)
- map->map_class = map->map_orgclass;
- if (tTd(38, 5))
- sm_dprintf("closemaps: closing %s (%s)\n",
- map->map_mname == NULL ? "NULL" : map->map_mname,
- map->map_file == NULL ? "NULL" : map->map_file);
-
- if (!bitset(MF_OPENBOGUS, map->map_mflags))
- {
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- }
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
-}
-
-#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
-extern int getdomainname();
-
-/* this is mainly for backward compatibility in Sun environment */
-static char *
-sun_init_domain()
-{
- /*
- ** Get the domain name from the kernel.
- ** If it does not start with a leading dot, then remove
- ** the first component. Since leading dots are funny Unix
- ** files, we treat a leading "+" the same as a leading dot.
- ** Finally, force there to be at least one dot in the domain name
- ** (i.e. top-level domains are not allowed, like "com", must be
- ** something like "sun.com").
- */
-
- char buf[MAXNAME];
- char *period, *autodomain;
-
- if (getdomainname(buf, sizeof buf) < 0)
- return NULL;
-
- if (buf[0] == '\0')
- return NULL;
-
- if (tTd(0, 20))
- printf("domainname = %s\n", buf);
-
- if (buf[0] == '+')
- buf[0] = '.';
- period = strchr(buf, '.');
- if (period == NULL)
- autodomain = buf;
- else
- autodomain = period + 1;
- if (strchr(autodomain, '.') == NULL)
- return newstr(buf);
- else
- return newstr(autodomain);
-}
-#endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
-
-/*
-** GETCANONNAME -- look up name using service switch
-**
-** Parameters:
-** host -- the host name to look up.
-** hbsize -- the size of the host buffer.
-** trymx -- if set, try MX records.
-** pttl -- pointer to return TTL (can be NULL).
-**
-** Returns:
-** true -- if the host was found.
-** false -- otherwise.
-*/
-
-bool
-getcanonname(host, hbsize, trymx, pttl)
- char *host;
- int hbsize;
- bool trymx;
- int *pttl;
-{
- int nmaps;
- int mapno;
- bool found = false;
- bool got_tempfail = false;
- auto int status;
- char *maptype[MAXMAPSTACK];
- short mapreturn[MAXMAPACTIONS];
-#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- bool should_try_nis_domain = false;
- static char *nis_domain = NULL;
-#endif
-
- nmaps = switch_map_find("hosts", maptype, mapreturn);
- if (pttl != 0)
- *pttl = SM_DEFAULT_TTL;
- for (mapno = 0; mapno < nmaps; mapno++)
- {
- int i;
-
- if (tTd(38, 20))
- sm_dprintf("getcanonname(%s), trying %s\n",
- host, maptype[mapno]);
- if (strcmp("files", maptype[mapno]) == 0)
- {
- found = text_getcanonname(host, hbsize, &status);
- }
-#if NIS
- else if (strcmp("nis", maptype[mapno]) == 0)
- {
- found = nis_getcanonname(host, hbsize, &status);
-# if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- if (nis_domain == NULL)
- nis_domain = sun_init_domain();
-# endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
- }
-#endif /* NIS */
-#if NISPLUS
- else if (strcmp("nisplus", maptype[mapno]) == 0)
- {
- found = nisplus_getcanonname(host, hbsize, &status);
-# if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- if (nis_domain == NULL)
- nis_domain = sun_init_domain();
-# endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
- }
-#endif /* NISPLUS */
-#if NAMED_BIND
- else if (strcmp("dns", maptype[mapno]) == 0)
- {
- found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
- }
-#endif /* NAMED_BIND */
-#if NETINFO
- else if (strcmp("netinfo", maptype[mapno]) == 0)
- {
- found = ni_getcanonname(host, hbsize, &status);
- }
-#endif /* NETINFO */
- else
- {
- found = false;
- status = EX_UNAVAILABLE;
- }
-
- /*
- ** Heuristic: if $m is not set, we are running during system
- ** startup. In this case, when a name is apparently found
- ** but has no dot, treat is as not found. This avoids
- ** problems if /etc/hosts has no FQDN but is listed first
- ** in the service switch.
- */
-
- if (found &&
- (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
- break;
-
-#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- if (found)
- should_try_nis_domain = true;
- /* but don't break, as we need to try all methods first */
-#endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
-
- /* see if we should continue */
- if (status == EX_TEMPFAIL)
- {
- i = MA_TRYAGAIN;
- got_tempfail = true;
- }
- else if (status == EX_NOTFOUND)
- i = MA_NOTFOUND;
- else
- i = MA_UNAVAIL;
- if (bitset(1 << mapno, mapreturn[i]))
- break;
- }
-
- if (found)
- {
- char *d;
-
- if (tTd(38, 20))
- sm_dprintf("getcanonname(%s), found\n", host);
-
- /*
- ** If returned name is still single token, compensate
- ** by tagging on $m. This is because some sites set
- ** up their DNS or NIS databases wrong.
- */
-
- if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
- {
- d = macvalue('m', CurEnv);
- if (d != NULL &&
- hbsize > (int) (strlen(host) + strlen(d) + 1))
- {
- if (host[strlen(host) - 1] != '.')
- (void) sm_strlcat2(host, ".", d,
- hbsize);
- else
- (void) sm_strlcat(host, d, hbsize);
- }
- else
- {
-#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- if (VendorCode == VENDOR_SUN &&
- should_try_nis_domain)
- {
- goto try_nis_domain;
- }
-#endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
- return false;
- }
- }
- return true;
- }
-
-#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- if (VendorCode == VENDOR_SUN && should_try_nis_domain)
- {
- try_nis_domain:
- if (nis_domain != NULL &&
- strlen(nis_domain) + strlen(host) + 1 < hbsize)
- {
- (void) sm_strlcat2(host, ".", nis_domain, hbsize);
- return true;
- }
- }
-#endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
-
- if (tTd(38, 20))
- sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
- status);
-
- if (got_tempfail)
- SM_SET_H_ERRNO(TRY_AGAIN);
- else
- SM_SET_H_ERRNO(HOST_NOT_FOUND);
-
- return false;
-}
-/*
-** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
-**
-** Parameters:
-** name -- the name against which to match.
-** dot -- where to reinsert '.' to get FQDN
-** line -- the /etc/hosts line.
-** cbuf -- the location to store the result.
-** cbuflen -- the size of cbuf.
-**
-** Returns:
-** true -- if the line matched the desired name.
-** false -- otherwise.
-*/
-
-static bool
-extract_canonname(name, dot, line, cbuf, cbuflen)
- char *name;
- char *dot;
- char *line;
- char cbuf[];
- int cbuflen;
-{
- int i;
- char *p;
- bool found = false;
-
- cbuf[0] = '\0';
- if (line[0] == '#')
- return false;
-
- for (i = 1; ; i++)
- {
- char nbuf[MAXNAME + 1];
-
- p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
- if (p == NULL)
- break;
- if (*p == '\0')
- continue;
- if (cbuf[0] == '\0' ||
- (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
- {
- (void) sm_strlcpy(cbuf, p, cbuflen);
- }
- if (sm_strcasecmp(name, p) == 0)
- found = true;
- else if (dot != NULL)
- {
- /* try looking for the FQDN as well */
- *dot = '.';
- if (sm_strcasecmp(name, p) == 0)
- found = true;
- *dot = '\0';
- }
- }
- if (found && strchr(cbuf, '.') == NULL)
- {
- /* try to add a domain on the end of the name */
- char *domain = macvalue('m', CurEnv);
-
- if (domain != NULL &&
- strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
- {
- p = &cbuf[i];
- *p++ = '.';
- (void) sm_strlcpy(p, domain, cbuflen - i - 1);
- }
- }
- return found;
-}
-
-/*
-** DNS modules
-*/
-
-#if NAMED_BIND
-# if DNSMAP
-
-# include "sm_resolve.h"
-# if NETINET || NETINET6
-# include <arpa/inet.h>
-# endif /* NETINET || NETINET6 */
-
-/*
-** DNS_MAP_OPEN -- stub to check proper value for dns map type
-*/
-
-bool
-dns_map_open(map, mode)
- MAP *map;
- int mode;
-{
- if (tTd(38,2))
- sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
-
- mode &= O_ACCMODE;
- if (mode != O_RDONLY)
- {
- /* issue a pseudo-error message */
- errno = SM_EMAPCANTWRITE;
- return false;
- }
- return true;
-}
-
-/*
-** DNS_MAP_PARSEARGS -- parse dns map definition args.
-**
-** Parameters:
-** map -- pointer to MAP
-** args -- pointer to the args on the config line.
-**
-** Returns:
-** true -- if everything parsed OK.
-** false -- otherwise.
-*/
-
-#define map_sizelimit map_lockfd /* overload field */
-
-struct dns_map
-{
- int dns_m_type;
-};
-
-bool
-dns_map_parseargs(map,args)
- MAP *map;
- char *args;
-{
- register char *p = args;
- struct dns_map *map_p;
-
- map_p = (struct dns_map *) xalloc(sizeof(*map_p));
- map_p->dns_m_type = -1;
- map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
-
- for (;;)
- {
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p != '-')
- break;
- switch (*++p)
- {
- case 'N':
- map->map_mflags |= MF_INCLNULL;
- map->map_mflags &= ~MF_TRY0NULL;
- break;
-
- case 'O':
- map->map_mflags &= ~MF_TRY1NULL;
- break;
-
- case 'o':
- map->map_mflags |= MF_OPTIONAL;
- break;
-
- case 'f':
- map->map_mflags |= MF_NOFOLDCASE;
- break;
-
- case 'm':
- map->map_mflags |= MF_MATCHONLY;
- break;
-
- case 'A':
- map->map_mflags |= MF_APPEND;
- break;
-
- case 'q':
- map->map_mflags |= MF_KEEPQUOTES;
- break;
-
- case 't':
- map->map_mflags |= MF_NODEFER;
- break;
-
- case 'a':
- map->map_app = ++p;
- break;
-
- case 'T':
- map->map_tapp = ++p;
- break;
-
- case 'd':
- {
- char *h;
-
- ++p;
- h = strchr(p, ' ');
- if (h != NULL)
- *h = '\0';
- map->map_timeout = convtime(p, 's');
- if (h != NULL)
- *h = ' ';
- }
- break;
-
- case 'r':
- while (isascii(*++p) && isspace(*p))
- continue;
- map->map_retry = atoi(p);
- break;
-
- 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;
-
- case 'Z':
- while (isascii(*++p) && isspace(*p))
- continue;
- map->map_sizelimit = atoi(p);
- break;
-
- /* Start of dns_map specific args */
- case 'R': /* search field */
- {
- char *h;
-
- while (isascii(*++p) && isspace(*p))
- continue;
- h = strchr(p, ' ');
- if (h != NULL)
- *h = '\0';
- map_p->dns_m_type = dns_string_to_type(p);
- if (h != NULL)
- *h = ' ';
- if (map_p->dns_m_type < 0)
- syserr("dns map %s: wrong type %s",
- map->map_mname, p);
- }
- break;
-
- case 'B': /* base domain */
- {
- char *h;
-
- while (isascii(*++p) && isspace(*p))
- continue;
- h = strchr(p, ' ');
- if (h != NULL)
- *h = '\0';
-
- /*
- ** slight abuse of map->map_file; it isn't
- ** used otherwise in this map type.
- */
-
- map->map_file = newstr(p);
- if (h != NULL)
- *h = ' ';
- }
- break;
- }
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
- }
- if (map_p->dns_m_type < 0)
- syserr("dns map %s: missing -R type", map->map_mname);
- if (map->map_app != NULL)
- map->map_app = newstr(map->map_app);
- if (map->map_tapp != NULL)
- map->map_tapp = newstr(map->map_tapp);
-
- /*
- ** Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
- ** Even if this assumption is wrong, we use only one byte,
- ** so it doesn't really matter.
- */
-
- map->map_db1 = (ARBPTR_T) map_p;
- return true;
-}
-
-/*
-** DNS_MAP_LOOKUP -- perform dns map lookup.
-**
-** Parameters:
-** map -- pointer to MAP
-** name -- name to lookup
-** av -- arguments to interpolate into buf.
-** statp -- pointer to status (EX_)
-**
-** Returns:
-** result of lookup if succeeded.
-** NULL -- otherwise.
-*/
-
-char *
-dns_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- int resnum = 0;
- char *vp = NULL, *result = NULL;
- size_t vsize;
- struct dns_map *map_p;
- RESOURCE_RECORD_T *rr = NULL;
- DNS_REPLY_T *r = NULL;
-# if NETINET6
- static char buf6[INET6_ADDRSTRLEN];
-# endif /* NETINET6 */
-
- if (tTd(38, 20))
- sm_dprintf("dns_map_lookup(%s, %s)\n",
- map->map_mname, name);
-
- map_p = (struct dns_map *)(map->map_db1);
- if (map->map_file != NULL && *map->map_file != '\0')
- {
- size_t len;
- char *appdomain;
-
- len = strlen(map->map_file) + strlen(name) + 2;
- appdomain = (char *) sm_malloc(len);
- if (appdomain == NULL)
- {
- *statp = EX_UNAVAILABLE;
- return NULL;
- }
- (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
- r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
- map->map_timeout, map->map_retry);
- sm_free(appdomain);
- }
- else
- {
- r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
- map->map_timeout, map->map_retry);
- }
-
- if (r == NULL)
- {
- result = NULL;
- if (h_errno == TRY_AGAIN || transienterror(errno))
- *statp = EX_TEMPFAIL;
- else
- *statp = EX_NOTFOUND;
- goto cleanup;
- }
- *statp = EX_OK;
- for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
- {
- char *type = NULL;
- char *value = NULL;
-
- switch (rr->rr_type)
- {
- case T_NS:
- type = "T_NS";
- value = rr->rr_u.rr_txt;
- break;
- case T_CNAME:
- type = "T_CNAME";
- value = rr->rr_u.rr_txt;
- break;
- case T_AFSDB:
- type = "T_AFSDB";
- value = rr->rr_u.rr_mx->mx_r_domain;
- break;
- case T_SRV:
- type = "T_SRV";
- value = rr->rr_u.rr_srv->srv_r_target;
- break;
- case T_PTR:
- type = "T_PTR";
- value = rr->rr_u.rr_txt;
- break;
- case T_TXT:
- type = "T_TXT";
- value = rr->rr_u.rr_txt;
- break;
- case T_MX:
- type = "T_MX";
- value = rr->rr_u.rr_mx->mx_r_domain;
- break;
-# if NETINET
- case T_A:
- type = "T_A";
- value = inet_ntoa(*(rr->rr_u.rr_a));
- break;
-# endif /* NETINET */
-# if NETINET6
- case T_AAAA:
- type = "T_AAAA";
- value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
- sizeof(buf6));
- break;
-# endif /* NETINET6 */
- }
-
- (void) strreplnonprt(value, 'X');
- if (map_p->dns_m_type != rr->rr_type)
- {
- if (tTd(38, 40))
- sm_dprintf("\tskipping type %s (%d) value %s\n",
- type != NULL ? type : "<UNKNOWN>",
- rr->rr_type,
- value != NULL ? value : "<NO VALUE>");
- continue;
- }
-
-# if NETINET6
- if (rr->rr_type == T_AAAA && value == NULL)
- {
- result = NULL;
- *statp = EX_DATAERR;
- if (tTd(38, 40))
- sm_dprintf("\tbad T_AAAA conversion\n");
- goto cleanup;
- }
-# endif /* NETINET6 */
- if (tTd(38, 40))
- sm_dprintf("\tfound type %s (%d) value %s\n",
- type != NULL ? type : "<UNKNOWN>",
- rr->rr_type,
- value != NULL ? value : "<NO VALUE>");
- if (value != NULL &&
- (map->map_coldelim == '\0' ||
- map->map_sizelimit == 1 ||
- bitset(MF_MATCHONLY, map->map_mflags)))
- {
- /* Only care about the first match */
- vp = newstr(value);
- break;
- }
- else if (vp == NULL)
- {
- /* First result */
- vp = newstr(value);
- }
- else
- {
- /* concatenate the results */
- int sz;
- char *new;
-
- sz = strlen(vp) + strlen(value) + 2;
- new = xalloc(sz);
- (void) sm_snprintf(new, sz, "%s%c%s",
- vp, map->map_coldelim, value);
- sm_free(vp);
- vp = new;
- if (map->map_sizelimit > 0 &&
- ++resnum >= map->map_sizelimit)
- break;
- }
- }
- if (vp == NULL)
- {
- result = NULL;
- *statp = EX_NOTFOUND;
- if (tTd(38, 40))
- sm_dprintf("\tno match found\n");
- goto cleanup;
- }
-
- /* Cleanly truncate for rulesets */
- truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
-
- vsize = strlen(vp);
-
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
- name, vp);
- if (bitset(MF_MATCHONLY, map->map_mflags))
- result = map_rewrite(map, name, strlen(name), NULL);
- else
- result = map_rewrite(map, vp, vsize, av);
-
- cleanup:
- if (vp != NULL)
- sm_free(vp);
- if (r != NULL)
- dns_free_data(r);
- return result;
-}
-# endif /* DNSMAP */
-#endif /* NAMED_BIND */
-
-/*
-** NDBM modules
-*/
-
-#if NDBM
-
-/*
-** NDBM_MAP_OPEN -- DBM-style map open
-*/
-
-bool
-ndbm_map_open(map, mode)
- MAP *map;
- int mode;
-{
- register DBM *dbm;
- int save_errno;
- int dfd;
- int pfd;
- long sff;
- int ret;
- int smode = S_IREAD;
- char dirfile[MAXPATHLEN];
- char pagfile[MAXPATHLEN];
- struct stat st;
- struct stat std, stp;
-
- if (tTd(38, 2))
- sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
- map->map_lockfd = -1;
- mode &= O_ACCMODE;
-
- /* do initial file and directory checks */
- if (sm_strlcpyn(dirfile, sizeof(dirfile), 2,
- map->map_file, ".dir") >= sizeof(dirfile) ||
- sm_strlcpyn(pagfile, sizeof(pagfile), 2,
- map->map_file, ".pag") >= sizeof(pagfile))
- {
- errno = 0;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("dbm map \"%s\": map file %s name too long",
- map->map_mname, map->map_file);
- return false;
- }
- sff = SFF_ROOTOK|SFF_REGONLY;
- if (mode == O_RDWR)
- {
- sff |= SFF_CREAT;
- if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
- sff |= SFF_NOSLINK;
- if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
- sff |= SFF_NOHLINK;
- smode = S_IWRITE;
- }
- else
- {
- if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
- sff |= SFF_NOWLINK;
- }
- 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 (ret != 0)
- {
- char *prob = "unsafe";
-
- /* cannot open this map */
- if (ret == ENOENT)
- prob = "missing";
- if (tTd(38, 2))
- sm_dprintf("\t%s map file: %d\n", prob, ret);
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("dbm map \"%s\": %s map file %s",
- map->map_mname, prob, map->map_file);
- return false;
- }
- if (std.st_mode == ST_MODE_NOFILE)
- mode |= O_CREAT|O_EXCL;
-
-# if LOCK_ON_OPEN
- if (mode == O_RDONLY)
- mode |= O_SHLOCK;
- else
- mode |= O_TRUNC|O_EXLOCK;
-# else /* LOCK_ON_OPEN */
- if ((mode & O_ACCMODE) == O_RDWR)
- {
-# if NOFTRUNCATE
- /*
- ** Warning: race condition. Try to lock the file as
- ** quickly as possible after opening it.
- ** This may also have security problems on some systems,
- ** but there isn't anything we can do about it.
- */
-
- mode |= O_TRUNC;
-# else /* NOFTRUNCATE */
- /*
- ** This ugly code opens the map without truncating it,
- ** locks the file, then truncates it. Necessary to
- ** avoid race conditions.
- */
-
- int dirfd;
- int pagfd;
- long sff = SFF_CREAT|SFF_OPENASROOT;
-
- if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
- sff |= SFF_NOSLINK;
- if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
- sff |= SFF_NOHLINK;
-
- dirfd = safeopen(dirfile, mode, DBMMODE, sff);
- pagfd = safeopen(pagfile, mode, DBMMODE, sff);
-
- if (dirfd < 0 || pagfd < 0)
- {
- save_errno = errno;
- if (dirfd >= 0)
- (void) close(dirfd);
- if (pagfd >= 0)
- (void) close(pagfd);
- errno = save_errno;
- syserr("ndbm_map_open: cannot create database %s",
- map->map_file);
- return false;
- }
- if (ftruncate(dirfd, (off_t) 0) < 0 ||
- ftruncate(pagfd, (off_t) 0) < 0)
- {
- save_errno = errno;
- (void) close(dirfd);
- (void) close(pagfd);
- errno = save_errno;
- syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
- map->map_file);
- return false;
- }
-
- /* if new file, get "before" bits for later filechanged check */
- if (std.st_mode == ST_MODE_NOFILE &&
- (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
- {
- save_errno = errno;
- (void) close(dirfd);
- (void) close(pagfd);
- errno = save_errno;
- syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
- map->map_file);
- return false;
- }
-
- /* have to save the lock for the duration (bletch) */
- map->map_lockfd = dirfd;
- (void) close(pagfd);
-
- /* twiddle bits for dbm_open */
- mode &= ~(O_CREAT|O_EXCL);
-# endif /* NOFTRUNCATE */
- }
-# endif /* LOCK_ON_OPEN */
-
- /* open the database */
- dbm = dbm_open(map->map_file, mode, DBMMODE);
- if (dbm == NULL)
- {
- save_errno = errno;
- if (bitset(MF_ALIAS, map->map_mflags) &&
- aliaswait(map, ".pag", false))
- return true;
-# if !LOCK_ON_OPEN && !NOFTRUNCATE
- if (map->map_lockfd >= 0)
- (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);
- return false;
- }
- dfd = dbm_dirfno(dbm);
- pfd = dbm_pagfno(dbm);
- if (dfd == pfd)
- {
- /* heuristic: if files are linked, this is actually gdbm */
- dbm_close(dbm);
-# if !LOCK_ON_OPEN && !NOFTRUNCATE
- if (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
-# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
- errno = 0;
- syserr("dbm map \"%s\": cannot support GDBM",
- map->map_mname);
- return false;
- }
-
- if (filechanged(dirfile, dfd, &std) ||
- filechanged(pagfile, pfd, &stp))
- {
- save_errno = errno;
- dbm_close(dbm);
-# if !LOCK_ON_OPEN && !NOFTRUNCATE
- if (map->map_lockfd >= 0)
- (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);
- return false;
- }
-
- 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(pfd, &st) >= 0)
- map->map_mtime = st.st_mtime;
-
- if (mode == O_RDONLY)
- {
-# 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 /* LOCK_ON_OPEN */
- if (bitset(MF_ALIAS, map->map_mflags) &&
- !aliaswait(map, ".pag", true))
- return false;
- }
- else
- {
- map->map_mflags |= MF_LOCKED;
- if (geteuid() == 0 && TrustedUid != 0)
- {
-# if HASFCHOWN
- if (fchown(dfd, TrustedUid, -1) < 0 ||
- fchown(pfd, TrustedUid, -1) < 0)
- {
- int err = errno;
-
- sm_syslog(LOG_ALERT, NOQID,
- "ownership change on %s failed: %s",
- map->map_file, sm_errstring(err));
- message("050 ownership change on %s failed: %s",
- map->map_file, sm_errstring(err));
- }
-# else /* HASFCHOWN */
- sm_syslog(LOG_ALERT, NOQID,
- "no fchown(): cannot change ownership on %s",
- map->map_file);
- message("050 no fchown(): cannot change ownership on %s",
- map->map_file);
-# endif /* HASFCHOWN */
- }
- }
- return true;
-}
-
-
-/*
-** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
-*/
-
-char *
-ndbm_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- datum key, val;
- int dfd, pfd;
- char keybuf[MAXNAME + 1];
- struct stat stbuf;
-
- if (tTd(38, 20))
- sm_dprintf("ndbm_map_lookup(%s, %s)\n",
- map->map_mname, name);
-
- key.dptr = name;
- key.dsize = strlen(name);
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- {
- if (key.dsize > sizeof(keybuf) - 1)
- key.dsize = sizeof(keybuf) - 1;
- memmove(keybuf, key.dptr, key.dsize);
- keybuf[key.dsize] = '\0';
- makelower(keybuf);
- key.dptr = keybuf;
- }
-lockdbm:
- dfd = dbm_dirfno((DBM *) map->map_db1);
- if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
- (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
- pfd = dbm_pagfno((DBM *) map->map_db1);
- if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
- stbuf.st_mtime > map->map_mtime)
- {
- /* Reopen the database to sync the cache */
- int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
- : O_RDONLY;
-
- if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
- (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
- if (map->map_class->map_open(map, omode))
- {
- map->map_mflags |= MF_OPEN;
- map->map_pid = CurrentPid;
- if ((omode && O_ACCMODE) == O_RDWR)
- map->map_mflags |= MF_WRITABLE;
- goto lockdbm;
- }
- else
- {
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- {
- extern MAPCLASS BogusMapClass;
-
- *statp = EX_TEMPFAIL;
- map->map_orgclass = map->map_class;
- map->map_class = &BogusMapClass;
- map->map_mflags |= MF_OPEN;
- map->map_pid = CurrentPid;
- syserr("Cannot reopen NDBM database %s",
- map->map_file);
- }
- return NULL;
- }
- }
- val.dptr = NULL;
- if (bitset(MF_TRY0NULL, map->map_mflags))
- {
- val = dbm_fetch((DBM *) map->map_db1, key);
- if (val.dptr != NULL)
- map->map_mflags &= ~MF_TRY1NULL;
- }
- if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
- {
- key.dsize++;
- val = dbm_fetch((DBM *) map->map_db1, key);
- if (val.dptr != NULL)
- map->map_mflags &= ~MF_TRY0NULL;
- }
- if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
- (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
- if (val.dptr == NULL)
- return NULL;
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, name, strlen(name), NULL);
- else
- return map_rewrite(map, val.dptr, val.dsize, av);
-}
-
-
-/*
-** NDBM_MAP_STORE -- store a datum in the database
-*/
-
-void
-ndbm_map_store(map, lhs, rhs)
- register MAP *map;
- char *lhs;
- char *rhs;
-{
- datum key;
- datum data;
- int status;
- char keybuf[MAXNAME + 1];
-
- if (tTd(38, 12))
- sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
- map->map_mname, lhs, rhs);
-
- key.dsize = strlen(lhs);
- key.dptr = lhs;
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- {
- if (key.dsize > sizeof(keybuf) - 1)
- key.dsize = sizeof(keybuf) - 1;
- memmove(keybuf, key.dptr, key.dsize);
- keybuf[key.dsize] = '\0';
- makelower(keybuf);
- key.dptr = keybuf;
- }
-
- data.dsize = strlen(rhs);
- data.dptr = rhs;
-
- if (bitset(MF_INCLNULL, map->map_mflags))
- {
- key.dsize++;
- data.dsize++;
- }
-
- 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);
- else
- {
- static char *buf = NULL;
- static int bufsiz = 0;
- auto int xstat;
- datum old;
-
- old.dptr = ndbm_map_lookup(map, key.dptr,
- (char **) NULL, &xstat);
- if (old.dptr != NULL && *(char *) old.dptr != '\0')
- {
- old.dsize = strlen(old.dptr);
- if (data.dsize + old.dsize + 2 > bufsiz)
- {
- if (buf != NULL)
- (void) sm_free(buf);
- bufsiz = data.dsize + old.dsize + 2;
- buf = sm_pmalloc_x(bufsiz);
- }
- (void) sm_strlcpyn(buf, bufsiz, 3,
- data.dptr, ",", old.dptr);
- data.dsize = data.dsize + old.dsize + 1;
- data.dptr = buf;
- if (tTd(38, 9))
- sm_dprintf("ndbm_map_store append=%s\n",
- data.dptr);
- }
- }
- status = dbm_store((DBM *) map->map_db1,
- key, data, DBM_REPLACE);
- }
- if (status != 0)
- syserr("readaliases: dbm put (%s): %d", lhs, status);
-}
-
-
-/*
-** NDBM_MAP_CLOSE -- close the database
-*/
-
-void
-ndbm_map_close(map)
- register MAP *map;
-{
- if (tTd(38, 9))
- sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
- map->map_mname, map->map_file, map->map_mflags);
-
- if (bitset(MF_WRITABLE, map->map_mflags))
- {
-# ifdef NDBM_YP_COMPAT
- bool inclnull;
- char buf[MAXHOSTNAMELEN];
-
- inclnull = bitset(MF_INCLNULL, map->map_mflags);
- map->map_mflags &= ~MF_INCLNULL;
-
- if (strstr(map->map_file, "/yp/") != NULL)
- {
- long save_mflags = map->map_mflags;
-
- map->map_mflags |= MF_NOFOLDCASE;
-
- (void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime());
- ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
-
- (void) gethostname(buf, sizeof(buf));
- ndbm_map_store(map, "YP_MASTER_NAME", buf);
-
- map->map_mflags = save_mflags;
- }
-
- if (inclnull)
- map->map_mflags |= MF_INCLNULL;
-# endif /* NDBM_YP_COMPAT */
-
- /* write out the distinguished alias */
- ndbm_map_store(map, "@", "@");
- }
- dbm_close((DBM *) map->map_db1);
-
- /* release lock (if needed) */
-# if !LOCK_ON_OPEN
- if (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
-# endif /* !LOCK_ON_OPEN */
-}
-
-#endif /* NDBM */
-/*
-** NEWDB (Hash and BTree) Modules
-*/
-
-#if NEWDB
-
-/*
-** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
-**
-** These do rather bizarre locking. If you can lock on open,
-** do that to avoid the condition of opening a database that
-** is being rebuilt. If you don't, we'll try to fake it, but
-** there will be a race condition. If opening for read-only,
-** we immediately release the lock to avoid freezing things up.
-** We really ought to hold the lock, but guarantee that we won't
-** be pokey about it. That's hard to do.
-*/
-
-/* 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 /* ! 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
- BTREEINFO btinfo;
-# endif /* DB_VERSION_MAJOR < 2 */
-# if DB_VERSION_MAJOR == 2
- DB_INFO btinfo;
-# endif /* DB_VERSION_MAJOR == 2 */
-# if DB_VERSION_MAJOR > 2
- void *btinfo = NULL;
-# endif /* DB_VERSION_MAJOR > 2 */
-
- if (tTd(38, 2))
- sm_dprintf("bt_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
-
-# if DB_VERSION_MAJOR < 3
- memset(&btinfo, '\0', sizeof(btinfo));
-# ifdef DB_CACHE_SIZE
- btinfo.db_cachesize = DB_CACHE_SIZE;
-# endif /* DB_CACHE_SIZE */
-# endif /* DB_VERSION_MAJOR < 3 */
-
- return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
-}
-
-bool
-hash_map_open(map, mode)
- MAP *map;
- int mode;
-{
-# if DB_VERSION_MAJOR < 2
- HASHINFO hinfo;
-# endif /* DB_VERSION_MAJOR < 2 */
-# if DB_VERSION_MAJOR == 2
- DB_INFO hinfo;
-# endif /* DB_VERSION_MAJOR == 2 */
-# if DB_VERSION_MAJOR > 2
- void *hinfo = NULL;
-# endif /* DB_VERSION_MAJOR > 2 */
-
- if (tTd(38, 2))
- sm_dprintf("hash_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
-
-# if DB_VERSION_MAJOR < 3
- memset(&hinfo, '\0', sizeof(hinfo));
-# ifdef DB_HASH_NELEM
- hinfo.h_nelem = DB_HASH_NELEM;
-# endif /* DB_HASH_NELEM */
-# ifdef DB_CACHE_SIZE
- hinfo.db_cachesize = DB_CACHE_SIZE;
-# endif /* DB_CACHE_SIZE */
-# endif /* DB_VERSION_MAJOR < 3 */
-
- return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
-}
-
-static bool
-db_map_open(map, mode, mapclassname, dbtype, openinfo)
- MAP *map;
- int mode;
- char *mapclassname;
- DBTYPE dbtype;
-# if DB_VERSION_MAJOR < 2
- const void *openinfo;
-# endif /* DB_VERSION_MAJOR < 2 */
-# if DB_VERSION_MAJOR == 2
- DB_INFO *openinfo;
-# 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;
- long sff;
- int save_errno;
- struct stat st;
- char buf[MAXPATHLEN];
-
- /* do initial file and directory checks */
- if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
- {
- errno = 0;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("map \"%s\": map file %s name too long",
- map->map_mname, map->map_file);
- return false;
- }
- i = strlen(buf);
- if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
- {
- if (sm_strlcat(buf, ".db", sizeof(buf)) >= sizeof(buf))
- {
- errno = 0;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("map \"%s\": map file %s name too long",
- map->map_mname, map->map_file);
- return false;
- }
- }
-
- mode &= O_ACCMODE;
- omode = mode;
-
- sff = SFF_ROOTOK|SFF_REGONLY;
- if (mode == O_RDWR)
- {
- sff |= SFF_CREAT;
- if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
- sff |= SFF_NOSLINK;
- if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
- sff |= SFF_NOHLINK;
- smode = S_IWRITE;
- }
- else
- {
- if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
- sff |= SFF_NOWLINK;
- }
- if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
- sff |= SFF_SAFEDIRPATH;
- i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
-
- if (i != 0)
- {
- char *prob = "unsafe";
-
- /* cannot open this map */
- if (i == ENOENT)
- prob = "missing";
- if (tTd(38, 2))
- sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
- errno = i;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("%s map \"%s\": %s map file %s",
- mapclassname, map->map_mname, prob, buf);
- return false;
- }
- if (st.st_mode == ST_MODE_NOFILE)
- omode |= O_CREAT|O_EXCL;
-
- map->map_lockfd = -1;
-
-# if LOCK_ON_OPEN
- if (mode == O_RDWR)
- omode |= O_TRUNC|O_EXLOCK;
- else
- omode |= O_SHLOCK;
-# 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
- ** must have a locked instance around the dbopen.
- */
-
- fd = open(buf, omode, DBMMODE);
- if (fd < 0)
- {
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("db_map_open: cannot pre-open database %s", buf);
- return false;
- }
-
- /* make sure no baddies slipped in just before the open... */
- if (filechanged(buf, fd, &st))
- {
- save_errno = errno;
- (void) close(fd);
- errno = save_errno;
- syserr("db_map_open(%s): file changed after pre-open", buf);
- return false;
- }
-
- /* if new file, get the "before" bits for later filechanged check */
- if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
- {
- save_errno = errno;
- (void) close(fd);
- errno = save_errno;
- syserr("db_map_open(%s): cannot fstat pre-opened file",
- buf);
- return false;
- }
-
- /* actually lock the pre-opened file */
- if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
- syserr("db_map_open: cannot lock %s", buf);
-
- /* set up mode bits for dbopen */
- if (mode == O_RDWR)
- omode |= O_TRUNC;
- omode &= ~(O_EXCL|O_CREAT);
-# endif /* LOCK_ON_OPEN */
-
-# if DB_VERSION_MAJOR < 2
- db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
-# 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;
- if (bitset(O_CREAT, omode))
- flags |= DB_CREATE;
- if (bitset(O_TRUNC, omode))
- flags |= DB_TRUNCATE;
- SM_DB_FLAG_ADD(flags);
-
-# 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,
- DBTXN /* transaction for DB 4.1 */
- buf, NULL, dbtype, flags, DBMMODE);
- if (ret != 0)
- {
-#ifdef DB_OLD_VERSION
- if (ret == DB_OLD_VERSION)
- ret = EINVAL;
-#endif /* DB_OLD_VERSION */
- (void) db->close(db, 0);
- db = NULL;
- }
- }
- errno = ret;
-# else /* DB_VERSION_MAJOR > 2 */
- errno = db_open(buf, dbtype, flags, DBMMODE,
- NULL, openinfo, &db);
-# endif /* DB_VERSION_MAJOR > 2 */
- }
-# endif /* DB_VERSION_MAJOR < 2 */
- save_errno = errno;
-
-# if !LOCK_ON_OPEN
- if (mode == O_RDWR)
- map->map_lockfd = fd;
- else
- (void) close(fd);
-# 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 (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
-# 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
- fd = db->fd(db);
-# else /* DB_VERSION_MAJOR < 2 */
- fd = -1;
- errno = db->fd(db, &fd);
-# endif /* DB_VERSION_MAJOR < 2 */
- if (filechanged(buf, fd, &st))
- {
- save_errno = errno;
-# if DB_VERSION_MAJOR < 2
- (void) db->close(db);
-# else /* DB_VERSION_MAJOR < 2 */
- errno = db->close(db, 0);
-# endif /* DB_VERSION_MAJOR < 2 */
-# if !LOCK_ON_OPEN
- if (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
-# endif /* !LOCK_ON_OPEN */
- errno = save_errno;
- syserr("db_map_open(%s): file changed after open", buf);
- return false;
- }
-
- if (mode == O_RDWR)
- map->map_mflags |= MF_LOCKED;
-# if LOCK_ON_OPEN
- if (fd >= 0 && mode == O_RDONLY)
- {
- (void) lockfile(fd, buf, NULL, LOCK_UN);
- }
-# 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 (geteuid() == 0 && TrustedUid != 0)
- {
-# if HASFCHOWN
- if (fchown(fd, TrustedUid, -1) < 0)
- {
- int err = errno;
-
- sm_syslog(LOG_ALERT, NOQID,
- "ownership change on %s failed: %s",
- buf, sm_errstring(err));
- message("050 ownership change on %s failed: %s",
- buf, sm_errstring(err));
- }
-# else /* HASFCHOWN */
- sm_syslog(LOG_ALERT, NOQID,
- "no fchown(): cannot change ownership on %s",
- map->map_file);
- message("050 no fchown(): cannot change ownership on %s",
- map->map_file);
-# endif /* HASFCHOWN */
- }
- }
-
- 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;
-
- if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
- !aliaswait(map, ".db", true))
- return false;
- return true;
-}
-
-
-/*
-** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
-*/
-
-char *
-db_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- DBT key, val;
- register DB *db = (DB *) map->map_db2;
- int i;
- int st;
- int save_errno;
- int fd;
- struct stat stbuf;
- char keybuf[MAXNAME + 1];
- char buf[MAXPATHLEN];
-
- memset(&key, '\0', sizeof(key));
- memset(&val, '\0', sizeof(val));
-
- if (tTd(38, 20))
- sm_dprintf("db_map_lookup(%s, %s)\n",
- map->map_mname, name);
-
- if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
- {
- errno = 0;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("map \"%s\": map file %s name too long",
- map->map_mname, map->map_file);
- return NULL;
- }
- i = strlen(buf);
- if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
- buf[i - 3] = '\0';
-
- key.size = strlen(name);
- if (key.size > sizeof(keybuf) - 1)
- key.size = sizeof(keybuf) - 1;
- key.data = keybuf;
- memmove(keybuf, name, key.size);
- keybuf[key.size] = '\0';
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- makelower(keybuf);
- lockdb:
-# if DB_VERSION_MAJOR < 2
- fd = db->fd(db);
-# else /* DB_VERSION_MAJOR < 2 */
- fd = -1;
- errno = db->fd(db, &fd);
-# 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)
- {
- /* Reopen the database to sync the cache */
- int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
- : O_RDONLY;
-
- if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
- (void) lockfile(fd, buf, ".db", LOCK_UN);
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
- if (map->map_class->map_open(map, omode))
- {
- map->map_mflags |= MF_OPEN;
- map->map_pid = CurrentPid;
- if ((omode && O_ACCMODE) == O_RDWR)
- map->map_mflags |= MF_WRITABLE;
- db = (DB *) map->map_db2;
- goto lockdb;
- }
- else
- {
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- {
- extern MAPCLASS BogusMapClass;
-
- *statp = EX_TEMPFAIL;
- map->map_orgclass = map->map_class;
- map->map_class = &BogusMapClass;
- map->map_mflags |= MF_OPEN;
- map->map_pid = CurrentPid;
- syserr("Cannot reopen DB database %s",
- map->map_file);
- }
- return NULL;
- }
- }
-
- st = 1;
- if (bitset(MF_TRY0NULL, map->map_mflags))
- {
-# if DB_VERSION_MAJOR < 2
- st = db->get(db, &key, &val, 0);
-# else /* DB_VERSION_MAJOR < 2 */
- errno = db->get(db, NULL, &key, &val, 0);
- switch (errno)
- {
- case DB_NOTFOUND:
- case DB_KEYEMPTY:
- st = 1;
- break;
-
- case 0:
- st = 0;
- break;
-
- default:
- st = -1;
- break;
- }
-# 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
- st = db->get(db, &key, &val, 0);
-# else /* DB_VERSION_MAJOR < 2 */
- errno = db->get(db, NULL, &key, &val, 0);
- switch (errno)
- {
- case DB_NOTFOUND:
- case DB_KEYEMPTY:
- st = 1;
- break;
-
- case 0:
- st = 0;
- break;
-
- default:
- st = -1;
- break;
- }
-# endif /* DB_VERSION_MAJOR < 2 */
- if (st == 0)
- map->map_mflags &= ~MF_TRY0NULL;
- }
- save_errno = errno;
- if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
- (void) lockfile(fd, buf, ".db", LOCK_UN);
- if (st != 0)
- {
- errno = save_errno;
- if (st < 0)
- syserr("db_map_lookup: get (%s)", name);
- return NULL;
- }
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, name, strlen(name), NULL);
- else
- return map_rewrite(map, val.data, val.size, av);
-}
-
-
-/*
-** DB_MAP_STORE -- store a datum in the NEWDB database
-*/
-
-void
-db_map_store(map, lhs, rhs)
- register MAP *map;
- char *lhs;
- char *rhs;
-{
- int status;
- DBT key;
- DBT data;
- register DB *db = map->map_db2;
- char keybuf[MAXNAME + 1];
-
- memset(&key, '\0', sizeof(key));
- memset(&data, '\0', sizeof(data));
-
- if (tTd(38, 12))
- sm_dprintf("db_map_store(%s, %s, %s)\n",
- map->map_mname, lhs, rhs);
-
- key.size = strlen(lhs);
- key.data = lhs;
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- {
- if (key.size > sizeof(keybuf) - 1)
- key.size = sizeof(keybuf) - 1;
- memmove(keybuf, key.data, key.size);
- keybuf[key.size] = '\0';
- makelower(keybuf);
- key.data = keybuf;
- }
-
- data.size = strlen(rhs);
- data.data = rhs;
-
- if (bitset(MF_INCLNULL, map->map_mflags))
- {
- key.size++;
- data.size++;
- }
-
-# 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:
- status = 1;
- break;
-
- case 0:
- status = 0;
- break;
-
- default:
- status = -1;
- break;
- }
-# endif /* DB_VERSION_MAJOR < 2 */
- if (status > 0)
- {
- if (!bitset(MF_APPEND, map->map_mflags))
- message("050 Warning: duplicate alias name %s", lhs);
- else
- {
- static char *buf = NULL;
- static int bufsiz = 0;
- DBT old;
-
- memset(&old, '\0', sizeof(old));
-
- 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 > (size_t) bufsiz)
- {
- if (buf != NULL)
- sm_free(buf);
- bufsiz = data.size + old.size + 2;
- buf = sm_pmalloc_x(bufsiz);
- }
- (void) sm_strlcpyn(buf, bufsiz, 3,
- (char *) data.data, ",",
- (char *) old.data);
- data.size = data.size + old.size + 1;
- data.data = buf;
- if (tTd(38, 9))
- sm_dprintf("db_map_store append=%s\n",
- (char *) data.data);
- }
- }
-# 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 (status != 0)
- syserr("readaliases: db put (%s)", lhs);
-}
-
-
-/*
-** DB_MAP_CLOSE -- add distinguished entries and close the database
-*/
-
-void
-db_map_close(map)
- MAP *map;
-{
- register DB *db = map->map_db2;
-
- if (tTd(38, 9))
- sm_dprintf("db_map_close(%s, %s, %lx)\n",
- map->map_mname, map->map_file, map->map_mflags);
-
- if (bitset(MF_WRITABLE, map->map_mflags))
- {
- /* write out the distinguished alias */
- db_map_store(map, "@", "@");
- }
-
- (void) db->sync(db, 0);
-
-# if !LOCK_ON_OPEN
- if (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
-# endif /* !LOCK_ON_OPEN */
-
-# if DB_VERSION_MAJOR < 2
- if (db->close(db) != 0)
-# else /* DB_VERSION_MAJOR < 2 */
- /*
- ** Berkeley DB can use internal shared memory
- ** locking for its memory pool. Closing a map
- ** opened by another process will interfere
- ** with the shared memory and locks of the parent
- ** process leaving things in a bad state.
- */
-
- /*
- ** If this map was not opened by the current
- ** process, do not close the map but recover
- ** the file descriptor.
- */
-
- if (map->map_pid != CurrentPid)
- {
- int fd = -1;
-
- errno = db->fd(db, &fd);
- if (fd >= 0)
- (void) close(fd);
- return;
- }
-
- if ((errno = db->close(db, 0)) != 0)
-# 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 /* NEWDB */
-/*
-** NIS Modules
-*/
-
-#if NIS
-
-# ifndef YPERR_BUSY
-# define YPERR_BUSY 16
-# endif /* ! YPERR_BUSY */
-
-/*
-** NIS_MAP_OPEN -- open DBM map
-*/
-
-bool
-nis_map_open(map, mode)
- MAP *map;
- int mode;
-{
- int yperr;
- register char *p;
- auto char *vp;
- auto int vsize;
-
- if (tTd(38, 2))
- sm_dprintf("nis_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
-
- mode &= O_ACCMODE;
- if (mode != O_RDONLY)
- {
- /* issue a pseudo-error message */
- errno = SM_EMAPCANTWRITE;
- return false;
- }
-
- p = strchr(map->map_file, '@');
- if (p != NULL)
- {
- *p++ = '\0';
- if (*p != '\0')
- map->map_domain = p;
- }
-
- if (*map->map_file == '\0')
- map->map_file = "mail.aliases";
-
- if (map->map_domain == NULL)
- {
- yperr = yp_get_default_domain(&map->map_domain);
- if (yperr != 0)
- {
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("451 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))
- sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
- map->map_domain, map->map_file, yperr_string(yperr));
- if (vp != NULL)
- sm_free(vp);
-
- if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
- {
- /*
- ** We ought to be calling aliaswait() here if this is an
- ** alias file, but powerful HP-UX NIS servers apparently
- ** don't insert the @:@ token into the alias map when it
- ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
- */
-
-# if 0
- if (!bitset(MF_ALIAS, map->map_mflags) ||
- aliaswait(map, NULL, true))
-# endif /* 0 */
- return true;
- }
-
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- {
- syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
- map->map_file, map->map_domain, yperr_string(yperr));
- }
-
- return false;
-}
-
-
-/*
-** NIS_MAP_LOOKUP -- look up a datum in a NIS map
-*/
-
-/* ARGSUSED3 */
-char *
-nis_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- char *vp;
- auto int vsize;
- int buflen;
- int yperr;
- char keybuf[MAXNAME + 1];
- char *SM_NONVOLATILE result = NULL;
-
- if (tTd(38, 20))
- sm_dprintf("nis_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);
- yperr = YPERR_KEY;
- vp = NULL;
- if (bitset(MF_TRY0NULL, map->map_mflags))
- {
- yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
- &vp, &vsize);
- if (yperr == 0)
- map->map_mflags &= ~MF_TRY1NULL;
- }
- if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
- {
- SM_FREE_CLR(vp);
- buflen++;
- yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
- &vp, &vsize);
- if (yperr == 0)
- map->map_mflags &= ~MF_TRY0NULL;
- }
- if (yperr != 0)
- {
- if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
- map->map_mflags &= ~(MF_VALID|MF_OPEN);
- if (vp != NULL)
- sm_free(vp);
- return NULL;
- }
- SM_TRY
- if (bitset(MF_MATCHONLY, map->map_mflags))
- result = map_rewrite(map, name, strlen(name), NULL);
- else
- result = map_rewrite(map, vp, vsize, av);
- SM_FINALLY
- if (vp != NULL)
- sm_free(vp);
- SM_END_TRY
- return result;
-}
-
-
-/*
-** NIS_GETCANONNAME -- look up canonical name in NIS
-*/
-
-static bool
-nis_getcanonname(name, hbsize, statp)
- char *name;
- int hbsize;
- int *statp;
-{
- char *vp;
- auto int vsize;
- int keylen;
- int yperr;
- static bool try0null = true;
- static bool try1null = true;
- static char *yp_domain = NULL;
- char host_record[MAXLINE];
- char cbuf[MAXNAME];
- char nbuf[MAXNAME + 1];
-
- if (tTd(38, 20))
- sm_dprintf("nis_getcanonname(%s)\n", name);
-
- if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
- {
- *statp = EX_UNAVAILABLE;
- return false;
- }
- (void) shorten_hostname(nbuf);
- keylen = strlen(nbuf);
-
- if (yp_domain == NULL)
- (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,
- &vp, &vsize);
- if (yperr == 0)
- try1null = false;
- }
- if (yperr == YPERR_KEY && try1null)
- {
- SM_FREE_CLR(vp);
- keylen++;
- yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
- &vp, &vsize);
- if (yperr == 0)
- try0null = false;
- }
- if (yperr != 0)
- {
- if (yperr == YPERR_KEY)
- *statp = EX_NOHOST;
- else if (yperr == YPERR_BUSY)
- *statp = EX_TEMPFAIL;
- else
- *statp = EX_UNAVAILABLE;
- if (vp != NULL)
- sm_free(vp);
- return false;
- }
- (void) sm_strlcpy(host_record, vp, sizeof(host_record));
- sm_free(vp);
- if (tTd(38, 44))
- sm_dprintf("got record `%s'\n", host_record);
- vp = strpbrk(host_record, "#\n");
- if (vp != NULL)
- *vp = '\0';
- if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof(cbuf)))
- {
- /* this should not happen, but.... */
- *statp = EX_NOHOST;
- return false;
- }
- if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
- {
- *statp = EX_UNAVAILABLE;
- return false;
- }
- *statp = EX_OK;
- return true;
-}
-
-#endif /* NIS */
-/*
-** NISPLUS Modules
-**
-** This code donated by Sun Microsystems.
-*/
-
-#if 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>
-
-# 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
-*/
-
-bool
-nisplus_map_open(map, mode)
- MAP *map;
- int mode;
-{
- nis_result *res = NULL;
- int retry_cnt, max_col, i;
- char qbuf[MAXLINE + NIS_MAXNAMELEN];
-
- if (tTd(38, 2))
- sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
-
- mode &= O_ACCMODE;
- if (mode != O_RDONLY)
- {
- errno = EPERM;
- return false;
- }
-
- if (*map->map_file == '\0')
- map->map_file = "mail_aliases.org_dir";
-
- if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
- {
- /* set default NISPLUS Domain to $m */
- map->map_domain = newstr(nisplus_default_domain());
- if (tTd(38, 2))
- sm_dprintf("nisplus_map_open(%s): using domain %s\n",
- map->map_file, map->map_domain);
- }
- if (!PARTIAL_NAME(map->map_file))
- {
- map->map_domain = newstr("");
- (void) sm_strlcpy(qbuf, map->map_file, sizeof(qbuf));
- }
- else
- {
- /* check to see if this map actually exists */
- (void) sm_strlcpyn(qbuf, sizeof(qbuf), 3,
- map->map_file, ".", map->map_domain);
- }
-
- retry_cnt = 0;
- while (res == NULL || res->status != NIS_SUCCESS)
- {
- res = nis_lookup(qbuf, FOLLOW_LINKS);
- switch (res->status)
- {
- case NIS_SUCCESS:
- break;
-
- case NIS_TRYAGAIN:
- case NIS_RPCERROR:
- case NIS_NAMEUNREACHABLE:
- if (retry_cnt++ > 4)
- {
- errno = EAGAIN;
- return false;
- }
- /* try not to overwhelm hosed server */
- sleep(2);
- break;
-
- default: /* all other nisplus errors */
-# if 0
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("451 4.3.5 Cannot find table %s.%s: %s",
- map->map_file, map->map_domain,
- nis_sperrno(res->status));
-# endif /* 0 */
- errno = EAGAIN;
- return false;
- }
- }
-
- if (NIS_RES_NUMOBJ(res) != 1 ||
- (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
- {
- if (tTd(38, 10))
- sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
-# if 0
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("451 4.3.5 %s.%s: %s is not a table",
- map->map_file, map->map_domain,
- nis_sperrno(res->status));
-# endif /* 0 */
- errno = EBADF;
- return false;
- }
- /* default key column is column 0 */
- if (map->map_keycolnm == NULL)
- map->map_keycolnm = newstr(COL_NAME(res,0));
-
- max_col = COL_MAX(res);
-
- /* verify the key column exist */
- for (i = 0; i < max_col; i++)
- {
- if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
- break;
- }
- if (i == max_col)
- {
- if (tTd(38, 2))
- sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
- map->map_file, map->map_keycolnm);
- errno = ENOENT;
- return false;
- }
-
- /* default value column is the last column */
- if (map->map_valcolnm == NULL)
- {
- map->map_valcolno = max_col - 1;
- return true;
- }
-
- for (i = 0; i< max_col; i++)
- {
- if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
- {
- map->map_valcolno = i;
- return true;
- }
- }
-
- if (tTd(38, 2))
- sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
- map->map_file, map->map_keycolnm);
- errno = ENOENT;
- return false;
-}
-
-
-/*
-** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
-*/
-
-char *
-nisplus_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- char *p;
- auto int vsize;
- char *skp;
- int skleft;
- char search_key[MAXNAME + 4];
- char qbuf[MAXLINE + NIS_MAXNAMELEN];
- nis_result *result;
-
- if (tTd(38, 20))
- sm_dprintf("nisplus_map_lookup(%s, %s)\n",
- map->map_mname, name);
-
- if (!bitset(MF_OPEN, map->map_mflags))
- {
- if (nisplus_map_open(map, O_RDONLY))
- {
- map->map_mflags |= MF_OPEN;
- map->map_pid = CurrentPid;
- }
- else
- {
- *statp = EX_UNAVAILABLE;
- return NULL;
- }
- }
-
- /*
- ** Copy the name to the key buffer, escaping double quote characters
- ** by doubling them and quoting "]" and "," to avoid having the
- ** NIS+ parser choke on them.
- */
-
- skleft = sizeof(search_key) - 4;
- skp = search_key;
- for (p = name; *p != '\0' && skleft > 0; p++)
- {
- switch (*p)
- {
- case ']':
- case ',':
- /* quote the character */
- *skp++ = '"';
- *skp++ = *p;
- *skp++ = '"';
- skleft -= 3;
- break;
-
- case '"':
- /* double the quote */
- *skp++ = '"';
- skleft--;
- /* FALLTHROUGH */
-
- default:
- *skp++ = *p;
- skleft--;
- break;
- }
- }
- *skp = '\0';
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- makelower(search_key);
-
- /* construct the query */
- if (PARTIAL_NAME(map->map_file))
- (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s.%s",
- map->map_keycolnm, search_key, map->map_file,
- map->map_domain);
- else
- (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s",
- map->map_keycolnm, search_key, map->map_file);
-
- if (tTd(38, 20))
- sm_dprintf("qbuf=%s\n", qbuf);
- result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
- if (result->status == NIS_SUCCESS)
- {
- int count;
- char *str;
-
- if ((count = NIS_RES_NUMOBJ(result)) != 1)
- {
- if (LogLevel > 10)
- sm_syslog(LOG_WARNING, CurEnv->e_id,
- "%s: lookup error, expected 1 entry, got %d",
- map->map_file, count);
-
- /* ignore second entry */
- if (tTd(38, 20))
- sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
- name, count);
- }
-
- p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
- /* set the length of the result */
- if (p == NULL)
- p = "";
- vsize = strlen(p);
- if (tTd(38, 20))
- sm_dprintf("nisplus_map_lookup(%s), found %s\n",
- name, p);
- if (bitset(MF_MATCHONLY, map->map_mflags))
- str = map_rewrite(map, name, strlen(name), NULL);
- else
- str = map_rewrite(map, p, vsize, av);
- nis_freeresult(result);
- *statp = EX_OK;
- return str;
- }
- else
- {
- if (result->status == NIS_NOTFOUND)
- *statp = EX_NOTFOUND;
- else if (result->status == NIS_TRYAGAIN)
- *statp = EX_TEMPFAIL;
- else
- {
- *statp = EX_UNAVAILABLE;
- map->map_mflags &= ~(MF_VALID|MF_OPEN);
- }
- }
- if (tTd(38, 20))
- sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
- nis_freeresult(result);
- return NULL;
-}
-
-
-
-/*
-** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
-*/
-
-static bool
-nisplus_getcanonname(name, hbsize, statp)
- char *name;
- int hbsize;
- int *statp;
-{
- char *vp;
- auto int vsize;
- nis_result *result;
- char *p;
- char nbuf[MAXNAME + 1];
- char qbuf[MAXLINE + NIS_MAXNAMELEN];
-
- if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
- {
- *statp = EX_UNAVAILABLE;
- return false;
- }
- (void) shorten_hostname(nbuf);
-
- p = strchr(nbuf, '.');
- if (p == NULL)
- {
- /* single token */
- (void) sm_snprintf(qbuf, sizeof(qbuf),
- "[name=%s],hosts.org_dir", nbuf);
- }
- else if (p[1] != '\0')
- {
- /* multi token -- take only first token in nbuf */
- *p = '\0';
- (void) sm_snprintf(qbuf, sizeof(qbuf),
- "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
- }
- else
- {
- *statp = EX_NOHOST;
- return false;
- }
-
- if (tTd(38, 20))
- sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
- name, qbuf);
-
- result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
- NULL, NULL);
-
- if (result->status == NIS_SUCCESS)
- {
- int count;
- char *domain;
-
- if ((count = NIS_RES_NUMOBJ(result)) != 1)
- {
- if (LogLevel > 10)
- sm_syslog(LOG_WARNING, CurEnv->e_id,
- "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
- count);
-
- /* ignore second entry */
- if (tTd(38, 20))
- sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
- name, count);
- }
-
- if (tTd(38, 20))
- sm_dprintf("nisplus_getcanonname(%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))
- sm_dprintf("nisplus_getcanonname(%s), found %s\n",
- name, vp);
- if (strchr(vp, '.') != NULL)
- {
- domain = "";
- }
- else
- {
- domain = macvalue('m', CurEnv);
- if (domain == NULL)
- domain = "";
- }
- if (hbsize > vsize + (int) strlen(domain) + 1)
- {
- if (domain[0] == '\0')
- (void) sm_strlcpy(name, vp, hbsize);
- else
- (void) sm_snprintf(name, hbsize,
- "%s.%s", vp, domain);
- *statp = EX_OK;
- }
- else
- *statp = EX_NOHOST;
- nis_freeresult(result);
- return true;
- }
- else
- {
- if (result->status == NIS_NOTFOUND)
- *statp = EX_NOHOST;
- else if (result->status == NIS_TRYAGAIN)
- *statp = EX_TEMPFAIL;
- else
- *statp = EX_UNAVAILABLE;
- }
- if (tTd(38, 20))
- sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
- name, result->status, *statp);
- nis_freeresult(result);
- return false;
-}
-
-char *
-nisplus_default_domain()
-{
- static char default_domain[MAXNAME + 1] = "";
- char *p;
-
- if (default_domain[0] != '\0')
- return default_domain;
-
- p = nis_local_directory();
- (void) sm_strlcpy(default_domain, p, sizeof(default_domain));
- return default_domain;
-}
-
-#endif /* NISPLUS */
-/*
-** LDAP Modules
-*/
-
-/*
-** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
-*/
-
-#if defined(LDAPMAP) || defined(PH_MAP)
-
-# if PH_MAP
-# define ph_map_dequote ldapmap_dequote
-# endif /* PH_MAP */
-
-static char *ldapmap_dequote __P((char *));
-
-static 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) */
-
-#if LDAPMAP
-
-static SM_LDAP_STRUCT *LDAPDefaults = NULL;
-
-/*
-** LDAPMAP_OPEN -- open LDAP map
-**
-** 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
-ldapmap_open(map, mode)
- MAP *map;
- int mode;
-{
- SM_LDAP_STRUCT *lmap;
- STAB *s;
- char *id;
-
- if (tTd(38, 2))
- sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
-
-#if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
- HASLDAPGETALIASBYNAME
- if (VendorCode == VENDOR_SUN &&
- strcmp(map->map_mname, "aliases.ldap") == 0)
- {
- return true;
- }
-#endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
-
- mode &= O_ACCMODE;
-
- /* sendmail doesn't have the ability to write to LDAP (yet) */
- if (mode != O_RDONLY)
- {
- /* issue a pseudo-error message */
- errno = SM_EMAPCANTWRITE;
- return false;
- }
-
- lmap = (SM_LDAP_STRUCT *) map->map_db1;
-
- s = ldapmap_findconn(lmap);
- if (s->s_lmap != NULL)
- {
- /* Already have a connection open to this LDAP server */
- lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
- lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
-
- /* Add this map as head of linked list */
- lmap->ldap_next = s->s_lmap;
- s->s_lmap = map;
-
- if (tTd(38, 2))
- sm_dprintf("using cached connection\n");
- return true;
- }
-
- if (tTd(38, 2))
- sm_dprintf("opening new connection\n");
-
- if (lmap->ldap_host != NULL)
- id = lmap->ldap_host;
- else if (lmap->ldap_uri != NULL)
- id = lmap->ldap_uri;
- else
- id = "localhost";
-
- /* No connection yet, connect */
- if (!sm_ldap_start(map->map_mname, lmap))
- {
- if (errno == ETIMEDOUT)
- {
- if (LogLevel > 1)
- sm_syslog(LOG_NOTICE, CurEnv->e_id,
- "timeout conning to LDAP server %.100s",
- id);
- }
-
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- {
- if (bitset(MF_NODEFER, map->map_mflags))
- {
- syserr("%s failed to %s in map %s",
-# if USE_LDAP_INIT
- "ldap_init/ldap_bind",
-# else /* USE_LDAP_INIT */
- "ldap_open",
-# endif /* USE_LDAP_INIT */
- id, map->map_mname);
- }
- else
- {
- syserr("451 4.3.5 %s failed to %s in map %s",
-# if USE_LDAP_INIT
- "ldap_init/ldap_bind",
-# else /* USE_LDAP_INIT */
- "ldap_open",
-# endif /* USE_LDAP_INIT */
- id, map->map_mname);
- }
- }
- return false;
- }
-
- /* Save connection for reuse */
- s->s_lmap = map;
- return true;
-}
-
-/*
-** LDAPMAP_CLOSE -- close ldap map
-*/
-
-void
-ldapmap_close(map)
- MAP *map;
-{
- SM_LDAP_STRUCT *lmap;
- STAB *s;
-
- if (tTd(38, 2))
- sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
-
- lmap = (SM_LDAP_STRUCT *) map->map_db1;
-
- /* Check if already closed */
- if (lmap->ldap_ld == NULL)
- return;
-
- /* Close the LDAP connection */
- sm_ldap_close(lmap);
-
- /* Mark all the maps that share the connection as closed */
- s = ldapmap_findconn(lmap);
-
- while (s->s_lmap != NULL)
- {
- MAP *smap = s->s_lmap;
-
- if (tTd(38, 2) && smap != map)
- sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
- map->map_mname, smap->map_mname);
- smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
- lmap = (SM_LDAP_STRUCT *) smap->map_db1;
- lmap->ldap_ld = NULL;
- s->s_lmap = lmap->ldap_next;
- lmap->ldap_next = NULL;
- }
-}
-
-# ifdef SUNET_ID
-/*
-** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
-** This only makes sense at Stanford University.
-*/
-
-static char *
-sunet_id_hash(str)
- char *str;
-{
- char *p, *p_last;
-
- p = str;
- p_last = p;
- while (*p != '\0')
- {
- if (islower(*p) || isdigit(*p))
- {
- *p_last = *p;
- p_last++;
- }
- else if (isupper(*p))
- {
- *p_last = tolower(*p);
- p_last++;
- }
- ++p;
- }
- if (*p_last != '\0')
- *p_last = '\0';
- return str;
-}
-# define SM_CONVERT_ID(str) sunet_id_hash(str)
-# else /* SUNET_ID */
-# define SM_CONVERT_ID(str) makelower(str)
-# endif /* SUNET_ID */
-
-/*
-** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
-*/
-
-char *
-ldapmap_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- int flags;
- int i;
- int plen = 0;
- int psize = 0;
- int msgid;
- int save_errno;
- char *vp, *p;
- char *result = NULL;
- SM_RPOOL_T *rpool;
- SM_LDAP_STRUCT *lmap = NULL;
- char *argv[SM_LDAP_ARGS];
- char keybuf[MAXKEY];
-#if SM_LDAP_ARGS != MAX_MAP_ARGS
-# ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
-#endif /* SM_LDAP_ARGS != MAX_MAP_ARGS */
-
-#if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
- HASLDAPGETALIASBYNAME
- if (VendorCode == VENDOR_SUN &&
- strcmp(map->map_mname, "aliases.ldap") == 0)
- {
- int rc;
-#if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
- extern char *__getldapaliasbyname();
- char *answer;
-
- answer = __getldapaliasbyname(name, &rc);
-#else
- char answer[MAXNAME + 1];
-
- rc = __getldapaliasbyname(name, answer, sizeof(answer));
-#endif
- if (rc != 0)
- {
- if (tTd(38, 20))
- sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n",
- name, errno);
- *statp = EX_NOTFOUND;
- return NULL;
- }
- *statp = EX_OK;
- if (tTd(38, 20))
- sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name,
- answer);
- if (bitset(MF_MATCHONLY, map->map_mflags))
- result = map_rewrite(map, name, strlen(name), NULL);
- else
- result = map_rewrite(map, answer, strlen(answer), av);
-#if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
- free(answer);
-#endif
- return result;
- }
-#endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
-
- /* Get ldap struct pointer from map */
- lmap = (SM_LDAP_STRUCT *) map->map_db1;
- sm_ldap_setopts(lmap->ldap_ld, lmap);
-
- if (lmap->ldap_multi_args)
- {
- SM_REQUIRE(av != NULL);
- memset(argv, '\0', sizeof(argv));
- for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++)
- {
- argv[i] = sm_strdup(av[i]);
- if (argv[i] == NULL)
- {
- int save_errno, j;
-
- save_errno = errno;
- for (j = 0; j < i && argv[j] != NULL; j++)
- SM_FREE(argv[j]);
- *statp = EX_TEMPFAIL;
- errno = save_errno;
- return NULL;
- }
-
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- SM_CONVERT_ID(av[i]);
- }
- }
- else
- {
- (void) sm_strlcpy(keybuf, name, sizeof(keybuf));
-
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- SM_CONVERT_ID(keybuf);
- }
-
- if (tTd(38, 20))
- {
- if (lmap->ldap_multi_args)
- {
- sm_dprintf("ldapmap_lookup(%s, argv)\n",
- map->map_mname);
- for (i = 0; i < SM_LDAP_ARGS; i++)
- {
- sm_dprintf(" argv[%d] = %s\n", i,
- argv[i] == NULL ? "NULL" : argv[i]);
- }
- }
- else
- {
- sm_dprintf("ldapmap_lookup(%s, %s)\n",
- map->map_mname, name);
- }
- }
-
- if (lmap->ldap_multi_args)
- {
- msgid = sm_ldap_search_m(lmap, argv);
-
- /* free the argv array and its content, no longer needed */
- for (i = 0; i < SM_LDAP_ARGS && argv[i] != NULL; i++)
- SM_FREE(argv[i]);
- }
- else
- msgid = sm_ldap_search(lmap, keybuf);
- if (msgid == SM_LDAP_ERR)
- {
- errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
- save_errno = errno;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- {
- /*
- ** Do not include keybuf as this error may be shown
- ** to outsiders.
- */
-
- if (bitset(MF_NODEFER, map->map_mflags))
- syserr("Error in ldap_search in map %s",
- map->map_mname);
- else
- syserr("451 4.3.5 Error in ldap_search in map %s",
- map->map_mname);
- }
- *statp = EX_TEMPFAIL;
- switch (save_errno - E_LDAPBASE)
- {
-# ifdef LDAP_SERVER_DOWN
- case LDAP_SERVER_DOWN:
-# endif /* LDAP_SERVER_DOWN */
- case LDAP_TIMEOUT:
- case LDAP_UNAVAILABLE:
- /* server disappeared, try reopen on next search */
- ldapmap_close(map);
- break;
- }
- errno = save_errno;
- return NULL;
- }
-#if SM_LDAP_ERROR_ON_MISSING_ARGS
- else if (msgid == SM_LDAP_ERR_ARG_MISS)
- {
- if (bitset(MF_NODEFER, map->map_mflags))
- syserr("Error in ldap_search in map %s, too few arguments",
- map->map_mname);
- else
- syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments",
- map->map_mname);
- *statp = EX_CONFIG;
- return NULL;
- }
-#endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
-
- *statp = EX_NOTFOUND;
- vp = NULL;
-
- flags = 0;
- if (bitset(MF_SINGLEMATCH, map->map_mflags))
- flags |= SM_LDAP_SINGLEMATCH;
- if (bitset(MF_MATCHONLY, map->map_mflags))
- flags |= SM_LDAP_MATCHONLY;
-# if _FFR_LDAP_SINGLEDN
- if (bitset(MF_SINGLEDN, map->map_mflags))
- flags |= SM_LDAP_SINGLEDN;
-# endif /* _FFR_LDAP_SINGLEDN */
-
- /* Create an rpool for search related memory usage */
- rpool = sm_rpool_new_x(NULL);
-
- p = NULL;
- *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
- rpool, &p, &plen, &psize, NULL);
- save_errno = errno;
-
- /* Copy result so rpool can be freed */
- if (*statp == EX_OK && p != NULL)
- vp = newstr(p);
- sm_rpool_free(rpool);
-
- /* need to restart LDAP connection? */
- if (*statp == EX_RESTART)
- {
- *statp = EX_TEMPFAIL;
- ldapmap_close(map);
- }
-
- errno = save_errno;
- if (*statp != EX_OK && *statp != EX_NOTFOUND)
- {
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- {
- if (bitset(MF_NODEFER, map->map_mflags))
- syserr("Error getting LDAP results in map %s",
- map->map_mname);
- else
- syserr("451 4.3.5 Error getting LDAP results in map %s",
- map->map_mname);
- }
- errno = save_errno;
- return NULL;
- }
-
- /* Did we match anything? */
- if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
- return NULL;
-
- if (*statp == EX_OK)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, CurEnv->e_id,
- "ldap %.100s => %s", name,
- vp == NULL ? "<NULL>" : vp);
- if (bitset(MF_MATCHONLY, map->map_mflags))
- result = map_rewrite(map, name, strlen(name), NULL);
- else
- {
- /* vp != NULL according to test above */
- result = map_rewrite(map, vp, strlen(vp), av);
- }
- if (vp != NULL)
- sm_free(vp); /* XXX */
- }
- return result;
-}
-
-/*
-** LDAPMAP_FINDCONN -- find an LDAP connection to the server
-**
-** Cache LDAP connections based on the host, port, bind DN,
-** secret, and PID so we don't have multiple connections open to
-** the same server for different maps. Need a separate connection
-** per PID since a parent process may close the map before the
-** child is done with it.
-**
-** Parameters:
-** lmap -- LDAP map information
-**
-** Returns:
-** Symbol table entry for the LDAP connection.
-*/
-
-static STAB *
-ldapmap_findconn(lmap)
- SM_LDAP_STRUCT *lmap;
-{
- char *format;
- char *nbuf;
- char *id;
- STAB *SM_NONVOLATILE s = NULL;
-
- if (lmap->ldap_host != NULL)
- id = lmap->ldap_host;
- else if (lmap->ldap_uri != NULL)
- id = lmap->ldap_uri;
- else
- id = "localhost";
-
- format = "%s%c%d%c%d%c%s%c%s%d";
- nbuf = sm_stringf_x(format,
- id,
- CONDELSE,
- lmap->ldap_port,
- CONDELSE,
- lmap->ldap_version,
- CONDELSE,
- (lmap->ldap_binddn == NULL ? ""
- : lmap->ldap_binddn),
- CONDELSE,
- (lmap->ldap_secret == NULL ? ""
- : lmap->ldap_secret),
- (int) CurrentPid);
- SM_TRY
- s = stab(nbuf, ST_LMAP, ST_ENTER);
- SM_FINALLY
- sm_free(nbuf);
- SM_END_TRY
- return s;
-}
-/*
-** LDAPMAP_PARSEARGS -- parse ldap map definition args.
-*/
-
-static 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 }
-};
-
-static struct ladvalues LDAPAliasDereference[] =
-{
- { "never", LDAP_DEREF_NEVER },
- { "always", LDAP_DEREF_ALWAYS },
- { "search", LDAP_DEREF_SEARCHING },
- { "find", LDAP_DEREF_FINDING },
- { NULL, 0 }
-};
-
-static struct lssvalues LDAPSearchScope[] =
-{
- { "base", LDAP_SCOPE_BASE },
- { "one", LDAP_SCOPE_ONELEVEL },
- { "sub", LDAP_SCOPE_SUBTREE },
- { NULL, 0 }
-};
-
-bool
-ldapmap_parseargs(map, args)
- MAP *map;
- char *args;
-{
- bool secretread = true;
- bool attrssetup = false;
- int i;
- register char *p = args;
- SM_LDAP_STRUCT *lmap;
- struct lamvalues *lam;
- struct ladvalues *lad;
- struct lssvalues *lss;
- char ldapfilt[MAXLINE];
- char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
-
- /* Get ldap struct pointer from map */
- lmap = (SM_LDAP_STRUCT *) map->map_db1;
-
- /* Check if setting the initial LDAP defaults */
- if (lmap == NULL || lmap != LDAPDefaults)
- {
- /* We need to alloc an SM_LDAP_STRUCT struct */
- lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap));
- if (LDAPDefaults == NULL)
- sm_ldap_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 */
-
- /* Check if setting up an alias or file class LDAP map */
- if (bitset(MF_ALIAS, map->map_mflags))
- {
- /* Comma separate if used as an alias file */
- map->map_coldelim = ',';
- if (*args == '\0')
- {
- int n;
- char *lc;
- char jbuf[MAXHOSTNAMELEN];
- char lcbuf[MAXLINE];
-
- /* Get $j */
- expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
- if (jbuf[0] == '\0')
- {
- (void) sm_strlcpy(jbuf, "localhost",
- sizeof(jbuf));
- }
-
- lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
- if (lc == NULL)
- lc = "";
- else
- {
- expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
- lc = lcbuf;
- }
-
- n = sm_snprintf(ldapfilt, sizeof(ldapfilt),
- "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
- lc, jbuf);
- if (n >= sizeof(ldapfilt))
- {
- syserr("%s: Default LDAP string too long",
- map->map_mname);
- return false;
- }
-
- /* default args for an alias LDAP entry */
- lmap->ldap_filter = ldapfilt;
- lmap->ldap_attr[0] = "objectClass";
- lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
- lmap->ldap_attr_needobjclass[0] = NULL;
- lmap->ldap_attr[1] = "sendmailMTAAliasValue";
- lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
- lmap->ldap_attr_needobjclass[1] = NULL;
- lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
- lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
- lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
- lmap->ldap_attr[3] = "sendmailMTAAliasURL";
- lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
- lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
- lmap->ldap_attr[4] = NULL;
- lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
- lmap->ldap_attr_needobjclass[4] = NULL;
- attrssetup = true;
- }
- }
- else if (bitset(MF_FILECLASS, map->map_mflags))
- {
- /* Space separate if used as a file class file */
- map->map_coldelim = ' ';
- }
-
- for (;;)
- {
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p != '-')
- break;
- switch (*++p)
- {
- case 'A':
- map->map_mflags |= MF_APPEND;
- break;
-
- case 'a':
- map->map_app = ++p;
- break;
-
- case 'D':
- map->map_mflags |= MF_DEFER;
- break;
-
- case 'f':
- map->map_mflags |= MF_NOFOLDCASE;
- break;
-
- case 'm':
- map->map_mflags |= MF_MATCHONLY;
- break;
-
- 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 'q':
- map->map_mflags |= MF_KEEPQUOTES;
- break;
-
- case 'S':
- map->map_spacesub = *++p;
- break;
-
- case 'T':
- map->map_tapp = ++p;
- break;
-
- case 't':
- map->map_mflags |= MF_NODEFER;
- 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 '1':
- map->map_mflags |= MF_SINGLEMATCH;
- break;
-
-# if _FFR_LDAP_SINGLEDN
- case '2':
- map->map_mflags |= MF_SINGLEDN;
- break;
-# endif /* _FFR_LDAP_SINGLEDN */
-
- case 'b': /* search base */
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_base = p;
- break;
-
-# if _FFR_LDAP_NETWORK_TIMEOUT
- case 'c': /* network (connect) timeout */
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_networktmo.tv_sec = atoi(p);
- break;
-# endif /* _FFR_LDAP_NETWORK_TIMEOUT */
-
- case 'd': /* Dn to bind to server as */
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_binddn = p;
- break;
-
- case 'H': /* Use LDAP URI */
-# if !USE_LDAP_INIT
- syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
- map->map_mname);
- return false;
-# else /* !USE_LDAP_INIT */
- if (lmap->ldap_host != NULL)
- {
- syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
- map->map_mname);
- return false;
- }
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_uri = p;
- break;
-# endif /* !USE_LDAP_INIT */
-
- case 'h': /* ldap host */
- while (isascii(*++p) && isspace(*p))
- continue;
- if (lmap->ldap_uri != NULL)
- {
- syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
- map->map_mname);
- return false;
- }
- lmap->ldap_host = p;
- break;
-
- case 'K':
- lmap->ldap_multi_args = true;
- break;
-
- case 'k': /* search field */
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_filter = p;
- break;
-
- case 'l': /* time limit */
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_timelimit = atoi(p);
- lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
- break;
-
- case 'M': /* Method for binding */
- while (isascii(*++p) && isspace(*p))
- continue;
-
- if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
- p += 10;
-
- for (lam = LDAPAuthMethods;
- lam != NULL && lam->lam_name != NULL; lam++)
- {
- if (sm_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;
-
- case 'n': /* retrieve attribute names only */
- lmap->ldap_attrsonly = LDAPMAP_TRUE;
- break;
-
- /*
- ** This is a string that is dependent on the
- ** method used defined by 'M'.
- */
-
- case 'P': /* Secret password for binding */
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_secret = p;
- secretread = false;
- break;
-
- case 'p': /* ldap port */
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_port = atoi(p);
- break;
-
- /* args stolen from ldapsearch.c */
- case 'R': /* don't auto chase referrals */
-# ifdef LDAP_REFERRALS
- lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
-# else /* LDAP_REFERRALS */
- syserr("compile with -DLDAP_REFERRALS for referral support");
-# endif /* LDAP_REFERRALS */
- break;
-
- case 'r': /* alias dereferencing */
- while (isascii(*++p) && isspace(*p))
- continue;
-
- if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
- p += 11;
-
- for (lad = LDAPAliasDereference;
- lad != NULL && lad->lad_name != NULL; lad++)
- {
- if (sm_strncasecmp(p, lad->lad_name,
- strlen(lad->lad_name)) == 0)
- break;
- }
- if (lad->lad_name != NULL)
- lmap->ldap_deref = lad->lad_code;
- else
- {
- /* 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;
- }
- }
- break;
-
- case 's': /* search scope */
- while (isascii(*++p) && isspace(*p))
- continue;
-
- if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
- p += 11;
-
- for (lss = LDAPSearchScope;
- lss != NULL && lss->lss_name != NULL; lss++)
- {
- if (sm_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))
- {
- char *ptr;
-
- if ((ptr = strchr(p, ' ')) != NULL)
- *ptr = '\0';
- syserr("Scope must be [base|one|sub] (not %s) in map %s",
- p, map->map_mname);
- if (ptr != NULL)
- *ptr = ' ';
- return false;
- }
- }
- break;
-
- case 'V':
- if (*++p != '\\')
- lmap->ldap_attrsep = *p;
- else
- {
- switch (*++p)
- {
- case 'n':
- lmap->ldap_attrsep = '\n';
- break;
-
- case 't':
- lmap->ldap_attrsep = '\t';
- break;
-
- default:
- lmap->ldap_attrsep = '\\';
- }
- }
- break;
-
- case 'v': /* attr to return */
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_attr[0] = p;
- lmap->ldap_attr[1] = NULL;
- break;
-
- case 'w':
- /* -w should be for passwd, -P should be for version */
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_version = atoi(p);
-# ifdef LDAP_VERSION_MAX
- if (lmap->ldap_version > LDAP_VERSION_MAX)
- {
- syserr("LDAP version %d exceeds max of %d in map %s",
- lmap->ldap_version, LDAP_VERSION_MAX,
- map->map_mname);
- return false;
- }
-# endif /* LDAP_VERSION_MAX */
-# ifdef LDAP_VERSION_MIN
- if (lmap->ldap_version < LDAP_VERSION_MIN)
- {
- syserr("LDAP version %d is lower than min of %d in map %s",
- lmap->ldap_version, LDAP_VERSION_MIN,
- map->map_mname);
- return false;
- }
-# endif /* LDAP_VERSION_MIN */
- break;
-
- case 'Z':
- while (isascii(*++p) && isspace(*p))
- continue;
- lmap->ldap_sizelimit = atoi(p);
- break;
-
- default:
- syserr("Illegal option %c map %s", *p, map->map_mname);
- break;
- }
-
- /* 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++;
- }
-
- if (*p != '\0')
- *p++ = '\0';
- }
-
- if (map->map_app != NULL)
- map->map_app = newstr(ldapmap_dequote(map->map_app));
- if (map->map_tapp != NULL)
- 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->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_uri != NULL &&
- (LDAPDefaults == NULL ||
- LDAPDefaults == lmap ||
- LDAPDefaults->ldap_uri != lmap->ldap_uri))
- lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
- map->map_domain = lmap->ldap_uri;
-
- 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))
- {
- SM_FILE_T *sfd;
- long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
-
- 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;
-
- case LDAP_AUTH_SIMPLE:
-
- /*
- ** 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, sizeof(m_tmp),
- sfd, TimeOuts.to_fileopen,
- "ldapmap_parseargs");
- (void) sm_io_close(sfd, SM_TIME_DEFAULT);
- if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
- {
- syserr("LDAP map: secret in %s too long",
- ldapmap_dequote(lmap->ldap_secret));
- return false;
- }
- 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
- */
-
- (void) sm_snprintf(m_tmp, sizeof(m_tmp),
- "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;
- /* NOTREACHED */
- 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->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))
- {
- syserr("No filter given in map %s", map->map_mname);
- return false;
- }
- }
-
- if (!attrssetup && lmap->ldap_attr[0] != NULL)
- {
- bool recurse = false;
- bool normalseen = false;
-
- i = 0;
- p = ldapmap_dequote(lmap->ldap_attr[0]);
- lmap->ldap_attr[0] = NULL;
-
- /* Prime the attr list with the objectClass attribute */
- lmap->ldap_attr[i] = "objectClass";
- lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
- lmap->ldap_attr_needobjclass[i] = NULL;
- i++;
-
- while (p != NULL)
- {
- 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')
- {
- int j;
- int use;
- char *type;
- char *needobjclass;
-
- type = strchr(v, ':');
- if (type != NULL)
- {
- *type++ = '\0';
- needobjclass = strchr(type, ':');
- if (needobjclass != NULL)
- *needobjclass++ = '\0';
- }
- else
- {
- needobjclass = NULL;
- }
-
- use = i;
-
- /* allow override on "objectClass" type */
- if (sm_strcasecmp(v, "objectClass") == 0 &&
- lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
- {
- use = 0;
- }
- else
- {
- /*
- ** Don't add something to attribute
- ** list twice.
- */
-
- for (j = 1; j < i; j++)
- {
- if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
- {
- syserr("Duplicate attribute (%s) in %s",
- v, map->map_mname);
- return false;
- }
- }
-
- lmap->ldap_attr[use] = newstr(v);
- if (needobjclass != NULL &&
- *needobjclass != '\0' &&
- *needobjclass != '*')
- {
- lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
- }
- else
- {
- lmap->ldap_attr_needobjclass[use] = NULL;
- }
-
- }
-
- if (type != NULL && *type != '\0')
- {
- if (sm_strcasecmp(type, "dn") == 0)
- {
- recurse = true;
- lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
- }
- else if (sm_strcasecmp(type, "filter") == 0)
- {
- recurse = true;
- lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
- }
- else if (sm_strcasecmp(type, "url") == 0)
- {
- recurse = true;
- lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
- }
- else if (sm_strcasecmp(type, "normal") == 0)
- {
- lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
- normalseen = true;
- }
- else
- {
- syserr("Unknown attribute type (%s) in %s",
- type, map->map_mname);
- return false;
- }
- }
- else
- {
- lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
- normalseen = true;
- }
- i++;
- }
- }
- lmap->ldap_attr[i] = NULL;
-
- /* Set in case needed in future code */
- attrssetup = true;
-
- if (recurse && !normalseen)
- {
- syserr("LDAP recursion requested in %s but no returnable attribute given",
- map->map_mname);
- return false;
- }
- if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
- {
- syserr("LDAP recursion requested in %s can not be used with -n",
- map->map_mname);
- return false;
- }
- }
- map->map_db1 = (ARBPTR_T) lmap;
- return true;
-}
-
-/*
-** 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;
-{
- STAB *class;
- MAP map;
-
- /* Allocate and set the default values */
- if (LDAPDefaults == NULL)
- LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof(*LDAPDefaults));
- sm_ldap_clear(LDAPDefaults);
-
- memset(&map, '\0', sizeof(map));
-
- /* look up the class */
- class = stab("ldap", ST_MAPCLASS, ST_FIND);
- if (class == NULL)
- {
- syserr("readcf: LDAPDefaultSpec: class ldap not available");
- return;
- }
- map.map_class = &class->s_mapclass;
- map.map_db1 = (ARBPTR_T) LDAPDefaults;
- map.map_mname = "O LDAPDefaultSpec";
-
- (void) ldapmap_parseargs(&map, spec);
-
- /* 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");
- SM_FREE_CLR(map.map_app);
- SM_FREE_CLR(map.map_tapp);
- }
-
- 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
-*/
-
-#if 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. Contact him for support.
-*/
-
-/* what version of the ph map code we're running */
-static char phmap_id[128];
-
-/* sendmail version for phmap id string */
-extern const char Version[];
-
-/* assume we're using nph-1.2.x if not specified */
-# ifndef NPH_VERSION
-# define NPH_VERSION 10200
-# endif
-
-/* compatibility for versions older than nph-1.2.0 */
-# if NPH_VERSION < 10200
-# define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
-# define PH_OPEN_DONTID PH_DONTID
-# define PH_CLOSE_FAST PH_FASTCLOSE
-# define PH_ERR_DATAERR PH_DATAERR
-# define PH_ERR_NOMATCH PH_NOMATCH
-# endif /* NPH_VERSION < 10200 */
-
-/*
-** PH_MAP_PARSEARGS -- parse ph map definition args.
-*/
-
-bool
-ph_map_parseargs(map, args)
- MAP *map;
- char *args;
-{
- register bool done;
- register char *p = args;
- PH_MAP_STRUCT *pmap = NULL;
-
- /* initialize version string */
- (void) sm_snprintf(phmap_id, sizeof(phmap_id),
- "sendmail-%s phmap-20010529 libphclient-%s",
- Version, libphclient_version);
-
- pmap = (PH_MAP_STRUCT *) xalloc(sizeof(*pmap));
-
- /* defaults */
- pmap->ph_servers = NULL;
- pmap->ph_field_list = NULL;
- pmap->ph = NULL;
- pmap->ph_timeout = 0;
- pmap->ph_fastclose = 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;
-
- case 'l':
- while (isascii(*++p) && isspace(*p))
- continue;
- pmap->ph_timeout = atoi(p);
- break;
-
- 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 'k': /* fields to search for */
- while (isascii(*++p) && isspace(*p))
- continue;
- pmap->ph_field_list = p;
- break;
-
- default:
- syserr("ph_map_parseargs: unknown option -%c", *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));
-
- 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;
-}
-
-/*
-** 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;
- if (tTd(38, 9))
- sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
- map->map_mname, pmap->ph_fastclose);
-
-
- if (pmap->ph != NULL)
- {
- ph_set_sendhook(pmap->ph, NULL);
- ph_set_recvhook(pmap->ph, NULL);
- ph_close(pmap->ph, pmap->ph_fastclose);
- }
-
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
-}
-
-static jmp_buf PHTimeout;
-
-/* ARGSUSED */
-static void
-ph_timeout(unused)
- int unused;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(PHTimeout, 1);
-}
-
-static void
-#if NPH_VERSION >= 10200
-ph_map_send_debug(appdata, text)
- void *appdata;
-#else
-ph_map_send_debug(text)
-#endif
- char *text;
-{
- if (LogLevel > 9)
- sm_syslog(LOG_NOTICE, CurEnv->e_id,
- "ph_map_send_debug: ==> %s", text);
- if (tTd(38, 20))
- sm_dprintf("ph_map_send_debug: ==> %s\n", text);
-}
-
-static void
-#if NPH_VERSION >= 10200
-ph_map_recv_debug(appdata, text)
- void *appdata;
-#else
-ph_map_recv_debug(text)
-#endif
- char *text;
-{
- if (LogLevel > 10)
- sm_syslog(LOG_NOTICE, CurEnv->e_id,
- "ph_map_recv_debug: <== %s", text);
- if (tTd(38, 21))
- sm_dprintf("ph_map_recv_debug: <== %s\n", text);
-}
-
-/*
-** PH_MAP_OPEN -- sub for opening PH map
-*/
-bool
-ph_map_open(map, mode)
- MAP *map;
- int mode;
-{
- PH_MAP_STRUCT *pmap;
- register SM_EVENT *ev = NULL;
- int save_errno = 0;
- char *hostlist, *host;
-
- if (tTd(38, 2))
- sm_dprintf("ph_map_open(%s)\n", map->map_mname);
-
- mode &= O_ACCMODE;
- if (mode != O_RDONLY)
- {
- /* issue a pseudo-error message */
- errno = SM_EMAPCANTWRITE;
- return false;
- }
-
- if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
- bitset(MF_DEFER, map->map_mflags))
- {
- if (tTd(9, 1))
- sm_dprintf("ph_map_open(%s) => DEFERRED\n",
- map->map_mname);
-
- /*
- ** Unset MF_DEFER here so that map_lookup() returns
- ** a temporary failure using the bogus map and
- ** map->map_tapp instead of the default permanent error.
- */
-
- map->map_mflags &= ~MF_DEFER;
- return false;
- }
-
- pmap = (PH_MAP_STRUCT *)map->map_db1;
- pmap->ph_fastclose = 0; /* refresh field for reopen */
-
- /* try each host in the list */
- hostlist = newstr(pmap->ph_servers);
- for (host = strtok(hostlist, " ");
- host != NULL;
- host = strtok(NULL, " "))
- {
- /* set 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",
- host);
- errno = ETIMEDOUT;
- goto ph_map_open_abort;
- }
- ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
- }
-
- /* open connection to server */
- if (ph_open(&(pmap->ph), host,
- PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
- ph_map_send_debug, ph_map_recv_debug
-#if NPH_VERSION >= 10200
- , NULL
-#endif
- ) == 0
- && ph_id(pmap->ph, phmap_id) == 0)
- {
- if (ev != NULL)
- sm_clrevent(ev);
- sm_free(hostlist); /* XXX */
- return true;
- }
-
- ph_map_open_abort:
- save_errno = errno;
- if (ev != NULL)
- sm_clrevent(ev);
- pmap->ph_fastclose = PH_CLOSE_FAST;
- ph_map_close(map);
- errno = save_errno;
- }
-
- if (bitset(MF_NODEFER, map->map_mflags))
- {
- if (errno == 0)
- errno = EAGAIN;
- syserr("ph_map_open: %s: cannot connect to PH server",
- map->map_mname);
- }
- else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
- sm_syslog(LOG_NOTICE, CurEnv->e_id,
- "ph_map_open: %s: cannot connect to PH server",
- map->map_mname);
- sm_free(hostlist); /* XXX */
- return false;
-}
-
-/*
-** PH_MAP_LOOKUP -- look up key from ph server
-*/
-
-char *
-ph_map_lookup(map, key, args, pstat)
- MAP *map;
- char *key;
- char **args;
- int *pstat;
-{
- int i, save_errno = 0;
- register SM_EVENT *ev = NULL;
- PH_MAP_STRUCT *pmap;
- char *value = NULL;
-
- pmap = (PH_MAP_STRUCT *)map->map_db1;
-
- *pstat = EX_OK;
-
- /* set 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);
- errno = ETIMEDOUT;
- *pstat = EX_TEMPFAIL;
- goto ph_map_lookup_abort;
- }
- ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
- }
-
- /* perform lookup */
- i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
- if (i == -1)
- *pstat = EX_TEMPFAIL;
- else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
- *pstat = EX_UNAVAILABLE;
-
- ph_map_lookup_abort:
- if (ev != NULL)
- sm_clrevent(ev);
-
- /*
- ** Close the connection if the timer popped
- ** or we got a temporary PH error
- */
-
- if (*pstat == EX_TEMPFAIL)
- {
- save_errno = errno;
- pmap->ph_fastclose = PH_CLOSE_FAST;
- ph_map_close(map);
- errno = save_errno;
- }
-
- if (*pstat == EX_OK)
- {
- if (tTd(38,20))
- sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
-
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, key, strlen(key), NULL);
- else
- return map_rewrite(map, value, strlen(value), args);
- }
-
- return NULL;
-}
-#endif /* PH_MAP */
-
-/*
-** syslog map
-*/
-
-#define map_prio map_lockfd /* overload field */
-
-/*
-** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
-*/
-
-bool
-syslog_map_parseargs(map, args)
- MAP *map;
- char *args;
-{
- char *p = args;
- char *priority = NULL;
-
- /* there is no check whether there is really an argument */
- while (*p != '\0')
- {
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p != '-')
- break;
- ++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)
- map->map_prio = LOG_INFO;
- else
- {
- if (sm_strncasecmp("LOG_", priority, 4) == 0)
- priority += 4;
-
-#ifdef LOG_EMERG
- if (sm_strcasecmp("EMERG", priority) == 0)
- map->map_prio = LOG_EMERG;
- else
-#endif /* LOG_EMERG */
-#ifdef LOG_ALERT
- if (sm_strcasecmp("ALERT", priority) == 0)
- map->map_prio = LOG_ALERT;
- else
-#endif /* LOG_ALERT */
-#ifdef LOG_CRIT
- if (sm_strcasecmp("CRIT", priority) == 0)
- map->map_prio = LOG_CRIT;
- else
-#endif /* LOG_CRIT */
-#ifdef LOG_ERR
- if (sm_strcasecmp("ERR", priority) == 0)
- map->map_prio = LOG_ERR;
- else
-#endif /* LOG_ERR */
-#ifdef LOG_WARNING
- if (sm_strcasecmp("WARNING", priority) == 0)
- map->map_prio = LOG_WARNING;
- else
-#endif /* LOG_WARNING */
-#ifdef LOG_NOTICE
- if (sm_strcasecmp("NOTICE", priority) == 0)
- map->map_prio = LOG_NOTICE;
- else
-#endif /* LOG_NOTICE */
-#ifdef LOG_INFO
- if (sm_strcasecmp("INFO", priority) == 0)
- map->map_prio = LOG_INFO;
- else
-#endif /* LOG_INFO */
-#ifdef LOG_DEBUG
- if (sm_strcasecmp("DEBUG", priority) == 0)
- map->map_prio = LOG_DEBUG;
- else
-#endif /* LOG_DEBUG */
- {
- syserr("syslog_map_parseargs: Unknown priority %s",
- priority);
- return false;
- }
- }
- return true;
-}
-
-/*
-** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
-*/
-
-char *
-syslog_map_lookup(map, string, args, statp)
- MAP *map;
- char *string;
- char **args;
- int *statp;
-{
- char *ptr = map_rewrite(map, string, strlen(string), args);
-
- if (ptr != NULL)
- {
- if (tTd(38, 20))
- sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
- map->map_mname, map->map_prio, ptr);
-
- sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
- }
-
- *statp = EX_OK;
- return "";
-}
-
-#if _FFR_DPRINTF_MAP
-/*
-** dprintf map
-*/
-
-#define map_dbg_level map_lockfd /* overload field */
-
-/*
-** DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages.
-*/
-
-bool
-dprintf_map_parseargs(map, args)
- MAP *map;
- char *args;
-{
- char *p = args;
- char *dbg_level = NULL;
-
- /* there is no check whether there is really an argument */
- while (*p != '\0')
- {
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p != '-')
- break;
- ++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 == 'd')
- {
- while (*++p != '\0' && isascii(*p) && isspace(*p))
- continue;
- if (*p == '\0')
- break;
- dbg_level = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
- }
- else
- {
- syserr("Illegal option %c map dprintf", *p);
- ++p;
- }
- }
-
- if (dbg_level == NULL)
- map->map_dbg_level = 0;
- else
- {
- if (!(isascii(*dbg_level) && isdigit(*dbg_level)))
- {
- syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s",
- map->map_mname, map->map_file,
- dbg_level);
- return false;
- }
- map->map_dbg_level = atoi(dbg_level);
- }
- return true;
-}
-
-/*
-** DPRINTF_MAP_LOOKUP -- rewrite and print message. Always return empty string
-*/
-
-char *
-dprintf_map_lookup(map, string, args, statp)
- MAP *map;
- char *string;
- char **args;
- int *statp;
-{
- char *ptr = map_rewrite(map, string, strlen(string), args);
-
- if (ptr != NULL && tTd(85, map->map_dbg_level))
- sm_dprintf("%s\n", ptr);
- *statp = EX_OK;
- return "";
-}
-#endif /* _FFR_DPRINTF_MAP */
-
-/*
-** HESIOD Modules
-*/
-
-#if HESIOD
-
-bool
-hes_map_open(map, mode)
- MAP *map;
- int mode;
-{
- if (tTd(38, 2))
- sm_dprintf("hes_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
-
- if (mode != O_RDONLY)
- {
- /* issue a pseudo-error message */
- errno = SM_EMAPCANTWRITE;
- return false;
- }
-
-# ifdef HESIOD_INIT
- if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
- return true;
-
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
- sm_errstring(errno));
- return false;
-# else /* HESIOD_INIT */
- if (hes_error() == HES_ER_UNINIT)
- hes_init();
- switch (hes_error())
- {
- case HES_ER_OK:
- case HES_ER_NOTFOUND:
- return true;
- }
-
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
-
- return false;
-# endif /* HESIOD_INIT */
-}
-
-char *
-hes_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- char **hp;
-
- if (tTd(38, 20))
- sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
-
- if (name[0] == '\\')
- {
- char *np;
- int nl;
- int save_errno;
- char nbuf[MAXNAME];
-
- nl = strlen(name);
- if (nl < sizeof(nbuf) - 1)
- np = nbuf;
- else
- np = xalloc(strlen(name) + 2);
- np[0] = '\\';
- (void) sm_strlcpy(&np[1], name, (sizeof(nbuf)) - 1);
-# ifdef HESIOD_INIT
- hp = hesiod_resolve(HesiodContext, np, map->map_file);
-# else /* HESIOD_INIT */
- hp = hes_resolve(np, map->map_file);
-# endif /* HESIOD_INIT */
- save_errno = errno;
- if (np != nbuf)
- sm_free(np); /* XXX */
- errno = save_errno;
- }
- else
- {
-# ifdef HESIOD_INIT
- hp = hesiod_resolve(HesiodContext, name, map->map_file);
-# else /* HESIOD_INIT */
- hp = hes_resolve(name, map->map_file);
-# endif /* HESIOD_INIT */
- }
-# ifdef HESIOD_INIT
- if (hp == NULL || *hp == NULL)
- {
- switch (errno)
- {
- case ENOENT:
- *statp = EX_NOTFOUND;
- break;
- case ECONNREFUSED:
- *statp = EX_TEMPFAIL;
- break;
- case EMSGSIZE:
- case ENOMEM:
- default:
- *statp = EX_UNAVAILABLE;
- break;
- }
- if (hp != NULL)
- hesiod_free_list(HesiodContext, hp);
- return NULL;
- }
-# else /* HESIOD_INIT */
- if (hp == NULL || hp[0] == NULL)
- {
- switch (hes_error())
- {
- case HES_ER_OK:
- *statp = EX_OK;
- break;
-
- case HES_ER_NOTFOUND:
- *statp = EX_NOTFOUND;
- break;
-
- case HES_ER_CONFIG:
- *statp = EX_UNAVAILABLE;
- break;
-
- case HES_ER_NET:
- *statp = EX_TEMPFAIL;
- break;
- }
- return NULL;
- }
-# endif /* HESIOD_INIT */
-
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, name, strlen(name), NULL);
- else
- return map_rewrite(map, hp[0], strlen(hp[0]), av);
-}
-
-/*
-** HES_MAP_CLOSE -- free the Hesiod context
-*/
-
-void
-hes_map_close(map)
- MAP *map;
-{
- if (tTd(38, 20))
- sm_dprintf("hes_map_close(%s)\n", map->map_file);
-
-# ifdef HESIOD_INIT
- /* Free the hesiod context */
- if (HesiodContext != NULL)
- {
- hesiod_end(HesiodContext);
- HesiodContext = NULL;
- }
-# endif /* HESIOD_INIT */
-}
-
-#endif /* HESIOD */
-/*
-** NeXT NETINFO Modules
-*/
-
-#if NETINFO
-
-# define NETINFO_DEFAULT_DIR "/aliases"
-# define NETINFO_DEFAULT_PROPERTY "members"
-
-/*
-** NI_MAP_OPEN -- open NetInfo Aliases
-*/
-
-bool
-ni_map_open(map, mode)
- MAP *map;
- int mode;
-{
- if (tTd(38, 2))
- sm_dprintf("ni_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
- mode &= O_ACCMODE;
-
- if (*map->map_file == '\0')
- map->map_file = NETINFO_DEFAULT_DIR;
-
- if (map->map_valcolnm == NULL)
- map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
-
- if (map->map_coldelim == '\0')
- {
- if (bitset(MF_ALIAS, map->map_mflags))
- map->map_coldelim = ',';
- else if (bitset(MF_FILECLASS, map->map_mflags))
- map->map_coldelim = ' ';
- }
- return true;
-}
-
-
-/*
-** NI_MAP_LOOKUP -- look up a datum in NetInfo
-*/
-
-char *
-ni_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- char *res;
- char *propval;
-
- if (tTd(38, 20))
- sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
-
- propval = ni_propval(map->map_file, map->map_keycolnm, name,
- map->map_valcolnm, map->map_coldelim);
-
- if (propval == NULL)
- return NULL;
-
- SM_TRY
- if (bitset(MF_MATCHONLY, map->map_mflags))
- res = map_rewrite(map, name, strlen(name), NULL);
- else
- res = map_rewrite(map, propval, strlen(propval), av);
- SM_FINALLY
- sm_free(propval);
- SM_END_TRY
- return res;
-}
-
-
-static bool
-ni_getcanonname(name, hbsize, statp)
- char *name;
- int hbsize;
- int *statp;
-{
- char *vptr;
- char *ptr;
- char nbuf[MAXNAME + 1];
-
- if (tTd(38, 20))
- sm_dprintf("ni_getcanonname(%s)\n", name);
-
- if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
- {
- *statp = EX_UNAVAILABLE;
- return false;
- }
- (void) shorten_hostname(nbuf);
-
- /* we only accept single token search key */
- if (strchr(nbuf, '.'))
- {
- *statp = EX_NOHOST;
- return false;
- }
-
- /* Do the search */
- vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
-
- if (vptr == NULL)
- {
- *statp = EX_NOHOST;
- return false;
- }
-
- /* Only want the first machine name */
- if ((ptr = strchr(vptr, '\n')) != NULL)
- *ptr = '\0';
-
- if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
- {
- sm_free(vptr);
- *statp = EX_UNAVAILABLE;
- return true;
- }
- sm_free(vptr);
- *statp = EX_OK;
- return false;
-}
-#endif /* NETINFO */
-/*
-** TEXT (unindexed text file) Modules
-**
-** This code donated by Sun Microsystems.
-*/
-
-#define map_sff map_lockfd /* overload field */
-
-
-/*
-** TEXT_MAP_OPEN -- open text table
-*/
-
-bool
-text_map_open(map, mode)
- MAP *map;
- int mode;
-{
- long sff;
- int i;
-
- if (tTd(38, 2))
- sm_dprintf("text_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
-
- mode &= O_ACCMODE;
- if (mode != O_RDONLY)
- {
- errno = EPERM;
- return false;
- }
-
- if (*map->map_file == '\0')
- {
- syserr("text map \"%s\": file name required",
- map->map_mname);
- return false;
- }
-
- if (map->map_file[0] != '/')
- {
- syserr("text map \"%s\": file name must be fully qualified",
- map->map_mname);
- return false;
- }
-
- sff = SFF_ROOTOK|SFF_REGONLY;
- if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
- sff |= SFF_NOWLINK;
- 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))
- sm_dprintf("\tunsafe map file: %d\n", i);
- errno = save_errno;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("text map \"%s\": unsafe map file %s",
- map->map_mname, map->map_file);
- return false;
- }
-
- if (map->map_keycolnm == NULL)
- map->map_keycolno = 0;
- else
- {
- if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
- {
- syserr("text map \"%s\", file %s: -k should specify a number, not %s",
- map->map_mname, map->map_file,
- map->map_keycolnm);
- return false;
- }
- map->map_keycolno = atoi(map->map_keycolnm);
- }
-
- if (map->map_valcolnm == NULL)
- map->map_valcolno = 0;
- else
- {
- if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
- {
- syserr("text map \"%s\", file %s: -v should specify a number, not %s",
- map->map_mname, map->map_file,
- map->map_valcolnm);
- return false;
- }
- map->map_valcolno = atoi(map->map_valcolnm);
- }
-
- if (tTd(38, 2))
- {
- sm_dprintf("text_map_open(%s, %s): delimiter = ",
- map->map_mname, map->map_file);
- if (map->map_coldelim == '\0')
- sm_dprintf("(white space)\n");
- else
- sm_dprintf("%c\n", map->map_coldelim);
- }
-
- map->map_sff = sff;
- return true;
-}
-
-
-/*
-** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
-*/
-
-char *
-text_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- char *vp;
- auto int vsize;
- int buflen;
- SM_FILE_T *f;
- char delim;
- int key_idx;
- bool found_it;
- long sff = map->map_sff;
- char search_key[MAXNAME + 1];
- char linebuf[MAXLINE];
- char buf[MAXNAME + 1];
-
- found_it = false;
- if (tTd(38, 20))
- sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
-
- buflen = strlen(name);
- if (buflen > sizeof(search_key) - 1)
- buflen = sizeof(search_key) - 1; /* XXX just cut if off? */
- memmove(search_key, name, buflen);
- search_key[buflen] = '\0';
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- makelower(search_key);
-
- f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
- if (f == NULL)
- {
- map->map_mflags &= ~(MF_VALID|MF_OPEN);
- *statp = EX_UNAVAILABLE;
- return NULL;
- }
- key_idx = map->map_keycolno;
- delim = map->map_coldelim;
- while (sm_io_fgets(f, SM_TIME_DEFAULT,
- linebuf, sizeof(linebuf)) != NULL)
- {
- char *p;
-
- /* skip comment line */
- if (linebuf[0] == '#')
- continue;
- p = strchr(linebuf, '\n');
- if (p != NULL)
- *p = '\0';
- p = get_column(linebuf, key_idx, delim, buf, sizeof(buf));
- if (p != NULL && sm_strcasecmp(search_key, p) == 0)
- {
- found_it = true;
- break;
- }
- }
- (void) sm_io_close(f, SM_TIME_DEFAULT);
- if (!found_it)
- {
- *statp = EX_NOTFOUND;
- return NULL;
- }
- vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof(buf));
- if (vp == NULL)
- {
- *statp = EX_NOTFOUND;
- return NULL;
- }
- vsize = strlen(vp);
- *statp = EX_OK;
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, name, strlen(name), NULL);
- else
- return map_rewrite(map, vp, vsize, av);
-}
-
-/*
-** TEXT_GETCANONNAME -- look up canonical name in hosts file
-*/
-
-static bool
-text_getcanonname(name, hbsize, statp)
- char *name;
- int hbsize;
- int *statp;
-{
- bool found;
- char *dot;
- SM_FILE_T *f;
- char linebuf[MAXLINE];
- char cbuf[MAXNAME + 1];
- char nbuf[MAXNAME + 1];
-
- if (tTd(38, 20))
- sm_dprintf("text_getcanonname(%s)\n", name);
-
- if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
- {
- *statp = EX_UNAVAILABLE;
- return false;
- }
- dot = shorten_hostname(nbuf);
-
- f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
- NULL);
- if (f == NULL)
- {
- *statp = EX_UNAVAILABLE;
- return false;
- }
- found = false;
- while (!found &&
- sm_io_fgets(f, SM_TIME_DEFAULT,
- linebuf, sizeof(linebuf)) != NULL)
- {
- char *p = strpbrk(linebuf, "#\n");
-
- if (p != NULL)
- *p = '\0';
- if (linebuf[0] != '\0')
- found = extract_canonname(nbuf, dot, linebuf,
- cbuf, sizeof(cbuf));
- }
- (void) sm_io_close(f, SM_TIME_DEFAULT);
- if (!found)
- {
- *statp = EX_NOHOST;
- return false;
- }
-
- if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
- {
- *statp = EX_UNAVAILABLE;
- return false;
- }
- *statp = EX_OK;
- return true;
-}
-/*
-** STAB (Symbol Table) Modules
-*/
-
-
-/*
-** STAB_MAP_LOOKUP -- look up alias in symbol table
-*/
-
-/* ARGSUSED2 */
-char *
-stab_map_lookup(map, name, av, pstat)
- register MAP *map;
- char *name;
- char **av;
- int *pstat;
-{
- register STAB *s;
-
- if (tTd(38, 20))
- sm_dprintf("stab_lookup(%s, %s)\n",
- map->map_mname, name);
-
- s = stab(name, ST_ALIAS, ST_FIND);
- if (s == NULL)
- return NULL;
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, name, strlen(name), NULL);
- else
- return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
-}
-
-/*
-** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
-*/
-
-void
-stab_map_store(map, lhs, rhs)
- register MAP *map;
- char *lhs;
- char *rhs;
-{
- register STAB *s;
-
- s = stab(lhs, ST_ALIAS, ST_ENTER);
- s->s_alias = newstr(rhs);
-}
-
-
-/*
-** STAB_MAP_OPEN -- initialize (reads data file)
-**
-** This is a wierd case -- it is only intended as a fallback for
-** aliases. For this reason, opens for write (only during a
-** "newaliases") always fails, and opens for read open the
-** actual underlying text file instead of the database.
-*/
-
-bool
-stab_map_open(map, mode)
- register MAP *map;
- int mode;
-{
- SM_FILE_T *af;
- long sff;
- struct stat st;
-
- if (tTd(38, 2))
- sm_dprintf("stab_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
-
- mode &= O_ACCMODE;
- if (mode != O_RDONLY)
- {
- errno = EPERM;
- return false;
- }
-
- sff = SFF_ROOTOK|SFF_REGONLY;
- if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
- sff |= SFF_NOWLINK;
- if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
- sff |= SFF_SAFEDIRPATH;
- af = safefopen(map->map_file, O_RDONLY, 0444, sff);
- if (af == NULL)
- return false;
- readaliases(map, af, false, false);
-
- if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
- map->map_mtime = st.st_mtime;
- (void) sm_io_close(af, SM_TIME_DEFAULT);
-
- return true;
-}
-/*
-** Implicit Modules
-**
-** Tries several types. For back compatibility of aliases.
-*/
-
-
-/*
-** IMPL_MAP_LOOKUP -- lookup in best open database
-*/
-
-char *
-impl_map_lookup(map, name, av, pstat)
- MAP *map;
- char *name;
- char **av;
- int *pstat;
-{
- if (tTd(38, 20))
- sm_dprintf("impl_map_lookup(%s, %s)\n",
- map->map_mname, name);
-
-#if NEWDB
- if (bitset(MF_IMPL_HASH, map->map_mflags))
- return db_map_lookup(map, name, av, pstat);
-#endif /* NEWDB */
-#if NDBM
- if (bitset(MF_IMPL_NDBM, map->map_mflags))
- return ndbm_map_lookup(map, name, av, pstat);
-#endif /* NDBM */
- return stab_map_lookup(map, name, av, pstat);
-}
-
-/*
-** IMPL_MAP_STORE -- store in open databases
-*/
-
-void
-impl_map_store(map, lhs, rhs)
- MAP *map;
- char *lhs;
- char *rhs;
-{
- if (tTd(38, 12))
- sm_dprintf("impl_map_store(%s, %s, %s)\n",
- map->map_mname, lhs, rhs);
-#if NEWDB
- if (bitset(MF_IMPL_HASH, map->map_mflags))
- db_map_store(map, lhs, rhs);
-#endif /* NEWDB */
-#if NDBM
- if (bitset(MF_IMPL_NDBM, map->map_mflags))
- ndbm_map_store(map, lhs, rhs);
-#endif /* NDBM */
- stab_map_store(map, lhs, rhs);
-}
-
-/*
-** IMPL_MAP_OPEN -- implicit database open
-*/
-
-bool
-impl_map_open(map, mode)
- MAP *map;
- int mode;
-{
- if (tTd(38, 2))
- sm_dprintf("impl_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
-
- mode &= O_ACCMODE;
-#if NEWDB
- map->map_mflags |= MF_IMPL_HASH;
- if (hash_map_open(map, mode))
- {
-# ifdef NDBM_YP_COMPAT
- if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
-# endif /* NDBM_YP_COMPAT */
- return true;
- }
- else
- map->map_mflags &= ~MF_IMPL_HASH;
-#endif /* NEWDB */
-#if NDBM
- map->map_mflags |= MF_IMPL_NDBM;
- if (ndbm_map_open(map, mode))
- {
- return true;
- }
- else
- map->map_mflags &= ~MF_IMPL_NDBM;
-#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 /* defined(NEWDB) || defined(NDBM) */
- if (mode != O_RDONLY)
- usrerr("Cannot rebuild aliases: no database format defined");
-#endif /* defined(NEWDB) || defined(NDBM) */
-
- if (mode == O_RDONLY)
- return stab_map_open(map, mode);
- else
- return false;
-}
-
-
-/*
-** IMPL_MAP_CLOSE -- close any open database(s)
-*/
-
-void
-impl_map_close(map)
- MAP *map;
-{
- if (tTd(38, 9))
- sm_dprintf("impl_map_close(%s, %s, %lx)\n",
- map->map_mname, map->map_file, map->map_mflags);
-#if NEWDB
- if (bitset(MF_IMPL_HASH, map->map_mflags))
- {
- db_map_close(map);
- map->map_mflags &= ~MF_IMPL_HASH;
- }
-#endif /* NEWDB */
-
-#if NDBM
- if (bitset(MF_IMPL_NDBM, map->map_mflags))
- {
- ndbm_map_close(map);
- map->map_mflags &= ~MF_IMPL_NDBM;
- }
-#endif /* NDBM */
-}
-/*
-** User map class.
-**
-** Provides access to the system password file.
-*/
-
-/*
-** USER_MAP_OPEN -- open user map
-**
-** Really just binds field names to field numbers.
-*/
-
-bool
-user_map_open(map, mode)
- MAP *map;
- int mode;
-{
- if (tTd(38, 2))
- sm_dprintf("user_map_open(%s, %d)\n",
- map->map_mname, mode);
-
- mode &= O_ACCMODE;
- if (mode != O_RDONLY)
- {
- /* issue a pseudo-error message */
- errno = SM_EMAPCANTWRITE;
- return false;
- }
- if (map->map_valcolnm == NULL)
- /* EMPTY */
- /* nothing */ ;
- else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
- map->map_valcolno = 1;
- else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
- map->map_valcolno = 2;
- else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
- map->map_valcolno = 3;
- else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
- map->map_valcolno = 4;
- else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
- map->map_valcolno = 5;
- else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
- map->map_valcolno = 6;
- else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
- map->map_valcolno = 7;
- else
- {
- syserr("User map %s: unknown column name %s",
- map->map_mname, map->map_valcolnm);
- return false;
- }
- return true;
-}
-
-
-/*
-** USER_MAP_LOOKUP -- look up a user in the passwd file.
-*/
-
-/* ARGSUSED3 */
-char *
-user_map_lookup(map, key, av, statp)
- MAP *map;
- char *key;
- char **av;
- int *statp;
-{
- auto bool fuzzy;
- SM_MBDB_T user;
-
- if (tTd(38, 20))
- sm_dprintf("user_map_lookup(%s, %s)\n",
- map->map_mname, key);
-
- *statp = finduser(key, &fuzzy, &user);
- if (*statp != EX_OK)
- return NULL;
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, key, strlen(key), NULL);
- else
- {
- char *rwval = NULL;
- char buf[30];
-
- switch (map->map_valcolno)
- {
- case 0:
- case 1:
- rwval = user.mbdb_name;
- break;
-
- case 2:
- rwval = "x"; /* passwd no longer supported */
- break;
-
- case 3:
- (void) sm_snprintf(buf, sizeof(buf), "%d",
- (int) user.mbdb_uid);
- rwval = buf;
- break;
-
- case 4:
- (void) sm_snprintf(buf, sizeof(buf), "%d",
- (int) user.mbdb_gid);
- rwval = buf;
- break;
-
- case 5:
- rwval = user.mbdb_fullname;
- break;
-
- case 6:
- rwval = user.mbdb_homedir;
- break;
-
- case 7:
- rwval = user.mbdb_shell;
- break;
- default:
- syserr("user_map %s: bogus field %d",
- map->map_mname, map->map_valcolno);
- return NULL;
- }
- return map_rewrite(map, rwval, strlen(rwval), av);
- }
-}
-/*
-** Program map type.
-**
-** This provides access to arbitrary programs. It should be used
-** only very sparingly, since there is no way to bound the cost
-** of invoking an arbitrary program.
-*/
-
-char *
-prog_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- int i;
- int save_errno;
- int fd;
- int status;
- auto pid_t pid;
- register char *p;
- char *rval;
- char *argv[MAXPV + 1];
- char buf[MAXLINE];
-
- if (tTd(38, 20))
- sm_dprintf("prog_map_lookup(%s, %s) %s\n",
- map->map_mname, name, map->map_file);
-
- i = 0;
- argv[i++] = map->map_file;
- if (map->map_rebuild != NULL)
- {
- (void) sm_strlcpy(buf, map->map_rebuild, sizeof(buf));
- for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
- {
- if (i >= MAXPV - 1)
- break;
- argv[i++] = p;
- }
- }
- argv[i++] = name;
- argv[i] = NULL;
- if (tTd(38, 21))
- {
- sm_dprintf("prog_open:");
- for (i = 0; argv[i] != NULL; i++)
- sm_dprintf(" %s", argv[i]);
- sm_dprintf("\n");
- }
- (void) sm_blocksignal(SIGCHLD);
- pid = prog_open(argv, &fd, CurEnv);
- if (pid < 0)
- {
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("prog_map_lookup(%s) failed (%s) -- closing",
- map->map_mname, sm_errstring(errno));
- else if (tTd(38, 9))
- sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
- map->map_mname, sm_errstring(errno));
- map->map_mflags &= ~(MF_VALID|MF_OPEN);
- *statp = EX_OSFILE;
- return NULL;
- }
- i = read(fd, buf, sizeof(buf) - 1);
- if (i < 0)
- {
- syserr("prog_map_lookup(%s): read error %s",
- map->map_mname, sm_errstring(errno));
- rval = NULL;
- }
- else if (i == 0)
- {
- if (tTd(38, 20))
- sm_dprintf("prog_map_lookup(%s): empty answer\n",
- map->map_mname);
- rval = NULL;
- }
- else
- {
- buf[i] = '\0';
- p = strchr(buf, '\n');
- if (p != NULL)
- *p = '\0';
-
- /* collect the return value */
- if (bitset(MF_MATCHONLY, map->map_mflags))
- rval = map_rewrite(map, name, strlen(name), NULL);
- else
- rval = map_rewrite(map, buf, strlen(buf), av);
-
- /* now flush any additional output */
- while ((i = read(fd, buf, sizeof(buf))) > 0)
- continue;
- }
-
- /* wait for the process to terminate */
- (void) close(fd);
- status = waitfor(pid);
- save_errno = errno;
- (void) sm_releasesignal(SIGCHLD);
- errno = save_errno;
-
- if (status == -1)
- {
- syserr("prog_map_lookup(%s): wait error %s",
- map->map_mname, sm_errstring(errno));
- *statp = EX_SOFTWARE;
- rval = NULL;
- }
- else if (WIFEXITED(status))
- {
- if ((*statp = WEXITSTATUS(status)) != EX_OK)
- rval = NULL;
- }
- else
- {
- syserr("prog_map_lookup(%s): child died on signal %d",
- map->map_mname, status);
- *statp = EX_UNAVAILABLE;
- rval = NULL;
- }
- return rval;
-}
-/*
-** Sequenced map type.
-**
-** Tries each map in order until something matches, much like
-** implicit. Stores go to the first map in the list that can
-** support storing.
-**
-** This is slightly unusual in that there are two interfaces.
-** The "sequence" interface lets you stack maps arbitrarily.
-** The "switch" interface builds a sequence map by looking
-** at a system-dependent configuration file such as
-** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
-**
-** We don't need an explicit open, since all maps are
-** opened on demand.
-*/
-
-/*
-** SEQ_MAP_PARSE -- Sequenced map parsing
-*/
-
-bool
-seq_map_parse(map, ap)
- MAP *map;
- char *ap;
-{
- int maxmap;
-
- if (tTd(38, 2))
- sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
- maxmap = 0;
- while (*ap != '\0')
- {
- register char *p;
- STAB *s;
-
- /* find beginning of map name */
- while (isascii(*ap) && isspace(*ap))
- ap++;
- for (p = ap;
- (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
- p++)
- continue;
- if (*p != '\0')
- *p++ = '\0';
- while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
- p++;
- if (*ap == '\0')
- {
- ap = p;
- continue;
- }
- s = stab(ap, ST_MAP, ST_FIND);
- if (s == NULL)
- {
- syserr("Sequence map %s: unknown member map %s",
- map->map_mname, ap);
- }
- else if (maxmap >= MAXMAPSTACK)
- {
- syserr("Sequence map %s: too many member maps (%d max)",
- map->map_mname, MAXMAPSTACK);
- maxmap++;
- }
- else if (maxmap < MAXMAPSTACK)
- {
- map->map_stack[maxmap++] = &s->s_map;
- }
- ap = p;
- }
- return true;
-}
-
-/*
-** SWITCH_MAP_OPEN -- open a switched map
-**
-** This looks at the system-dependent configuration and builds
-** a sequence map that does the same thing.
-**
-** Every system must define a switch_map_find routine in conf.c
-** that will return the list of service types associated with a
-** given service class.
-*/
-
-bool
-switch_map_open(map, mode)
- MAP *map;
- int mode;
-{
- int mapno;
- int nmaps;
- char *maptype[MAXMAPSTACK];
-
- if (tTd(38, 2))
- sm_dprintf("switch_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
-
- mode &= O_ACCMODE;
- nmaps = switch_map_find(map->map_file, maptype, map->map_return);
- if (tTd(38, 19))
- {
- sm_dprintf("\tswitch_map_find => %d\n", nmaps);
- for (mapno = 0; mapno < nmaps; mapno++)
- sm_dprintf("\t\t%s\n", maptype[mapno]);
- }
- if (nmaps <= 0 || nmaps > MAXMAPSTACK)
- return false;
-
- for (mapno = 0; mapno < nmaps; mapno++)
- {
- register STAB *s;
- char nbuf[MAXNAME + 1];
-
- if (maptype[mapno] == NULL)
- continue;
- (void) sm_strlcpyn(nbuf, sizeof(nbuf), 3,
- map->map_mname, ".", maptype[mapno]);
- s = stab(nbuf, ST_MAP, ST_FIND);
- if (s == NULL)
- {
- syserr("Switch map %s: unknown member map %s",
- map->map_mname, nbuf);
- }
- else
- {
- map->map_stack[mapno] = &s->s_map;
- if (tTd(38, 4))
- sm_dprintf("\tmap_stack[%d] = %s:%s\n",
- mapno,
- s->s_map.map_class->map_cname,
- nbuf);
- }
- }
- return true;
-}
-
-#if 0
-/*
-** SEQ_MAP_CLOSE -- close all underlying maps
-*/
-
-void
-seq_map_close(map)
- MAP *map;
-{
- int mapno;
-
- if (tTd(38, 9))
- sm_dprintf("seq_map_close(%s)\n", map->map_mname);
-
- for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
- {
- MAP *mm = map->map_stack[mapno];
-
- if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
- continue;
- mm->map_mflags |= MF_CLOSING;
- mm->map_class->map_close(mm);
- mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
- }
-}
-#endif /* 0 */
-
-/*
-** SEQ_MAP_LOOKUP -- sequenced map lookup
-*/
-
-char *
-seq_map_lookup(map, key, args, pstat)
- MAP *map;
- char *key;
- char **args;
- int *pstat;
-{
- int mapno;
- int mapbit = 0x01;
- bool tempfail = false;
-
- if (tTd(38, 20))
- sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
-
- for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
- {
- MAP *mm = map->map_stack[mapno];
- char *rv;
-
- if (mm == NULL)
- continue;
- if (!bitset(MF_OPEN, mm->map_mflags) &&
- !openmap(mm))
- {
- if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
- {
- *pstat = EX_UNAVAILABLE;
- return NULL;
- }
- continue;
- }
- *pstat = EX_OK;
- rv = mm->map_class->map_lookup(mm, key, args, pstat);
- if (rv != NULL)
- return rv;
- if (*pstat == EX_TEMPFAIL)
- {
- if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
- return NULL;
- tempfail = true;
- }
- else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
- break;
- }
- if (tempfail)
- *pstat = EX_TEMPFAIL;
- else if (*pstat == EX_OK)
- *pstat = EX_NOTFOUND;
- return NULL;
-}
-
-/*
-** SEQ_MAP_STORE -- sequenced map store
-*/
-
-void
-seq_map_store(map, key, val)
- MAP *map;
- char *key;
- char *val;
-{
- int mapno;
-
- if (tTd(38, 12))
- sm_dprintf("seq_map_store(%s, %s, %s)\n",
- map->map_mname, key, val);
-
- for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
- {
- MAP *mm = map->map_stack[mapno];
-
- if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
- continue;
-
- mm->map_class->map_store(mm, key, val);
- return;
- }
- syserr("seq_map_store(%s, %s, %s): no writable map",
- map->map_mname, key, val);
-}
-/*
-** NULL stubs
-*/
-
-/* ARGSUSED */
-bool
-null_map_open(map, mode)
- MAP *map;
- int mode;
-{
- return true;
-}
-
-/* ARGSUSED */
-void
-null_map_close(map)
- MAP *map;
-{
- return;
-}
-
-char *
-null_map_lookup(map, key, args, pstat)
- MAP *map;
- char *key;
- char **args;
- int *pstat;
-{
- *pstat = EX_NOTFOUND;
- return NULL;
-}
-
-/* ARGSUSED */
-void
-null_map_store(map, key, val)
- MAP *map;
- char *key;
- char *val;
-{
- return;
-}
-
-/*
-** BOGUS stubs
-*/
-
-char *
-bogus_map_lookup(map, key, args, pstat)
- MAP *map;
- char *key;
- char **args;
- int *pstat;
-{
- *pstat = EX_TEMPFAIL;
- return NULL;
-}
-
-MAPCLASS BogusMapClass =
-{
- "bogus-map", NULL, 0,
- NULL, bogus_map_lookup, null_map_store,
- 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))
- sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
- name == NULL ? "NULL" : name);
-
- if (name == NULL ||
- *name == '\0' ||
- (mid = macid(name)) == 0)
- {
- *statp = EX_CONFIG;
- return NULL;
- }
-
- if (av[1] == NULL)
- macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
- else
- macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
-
- *statp = EX_OK;
- return "";
-}
-/*
-** REGEX modules
-*/
-
-#if MAP_REGEX
-
-# include <regex.h>
-
-# define DEFAULT_DELIM CONDELSE
-# define END_OF_FIELDS -1
-# define ERRBUF_SIZE 80
-# define MAX_MATCH 32
-
-# define xnalloc(s) memset(xalloc(s), '\0', s);
-
-struct regex_map
-{
- regex_t *regex_pattern_buf; /* xalloc it */
- int *regex_subfields; /* move to type MAP */
- char *regex_delim; /* move to type MAP */
-};
-
-static int parse_fields __P((char *, int *, int, int));
-static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
-
-static int
-parse_fields(s, ibuf, blen, nr_substrings)
- char *s;
- int *ibuf; /* array */
- int blen; /* number of elements in ibuf */
- int nr_substrings; /* number of substrings in the pattern */
-{
- register char *cp;
- int i = 0;
- bool lastone = false;
-
- blen--; /* for terminating END_OF_FIELDS */
- cp = s;
- do
- {
- for (;; cp++)
- {
- if (*cp == ',')
- {
- *cp = '\0';
- break;
- }
- if (*cp == '\0')
- {
- lastone = true;
- break;
- }
- }
- if (i < blen)
- {
- int val = atoi(s);
-
- if (val < 0 || val >= nr_substrings)
- {
- syserr("field (%d) out of range, only %d substrings in pattern",
- val, nr_substrings);
- return -1;
- }
- ibuf[i++] = val;
- }
- else
- {
- syserr("too many fields, %d max", blen);
- return -1;
- }
- s = ++cp;
- } while (!lastone);
- ibuf[i] = END_OF_FIELDS;
- return i;
-}
-
-bool
-regex_map_init(map, ap)
- MAP *map;
- char *ap;
-{
- int regerr;
- struct regex_map *map_p;
- register char *p;
- char *sub_param = NULL;
- int pflags;
- static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
-
- if (tTd(38, 2))
- sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
- map->map_mname, ap);
-
- pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
- p = ap;
- map_p = (struct regex_map *) xnalloc(sizeof(*map_p));
- map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
-
- for (;;)
- {
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p != '-')
- break;
- switch (*++p)
- {
- case 'n': /* not */
- map->map_mflags |= MF_REGEX_NOT;
- break;
-
- case 'f': /* case sensitive */
- map->map_mflags |= MF_NOFOLDCASE;
- pflags &= ~REG_ICASE;
- break;
-
- case 'b': /* basic regular expressions */
- pflags &= ~REG_EXTENDED;
- break;
-
- case 's': /* substring match () syntax */
- sub_param = ++p;
- pflags &= ~REG_NOSUB;
- break;
-
- case 'd': /* delimiter */
- map_p->regex_delim = ++p;
- break;
-
- case 'a': /* map append */
- map->map_app = ++p;
- break;
-
- case 'm': /* matchonly */
- map->map_mflags |= MF_MATCHONLY;
- break;
-
- case 'q':
- map->map_mflags |= MF_KEEPQUOTES;
- 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';
- }
- if (tTd(38, 3))
- sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
-
- if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
- {
- /* Errorhandling */
- char errbuf[ERRBUF_SIZE];
-
- (void) regerror(regerr, map_p->regex_pattern_buf,
- errbuf, sizeof(errbuf));
- syserr("pattern-compile-error: %s", errbuf);
- sm_free(map_p->regex_pattern_buf); /* XXX */
- sm_free(map_p); /* XXX */
- return false;
- }
-
- if (map->map_app != NULL)
- map->map_app = newstr(map->map_app);
- if (map_p->regex_delim != NULL)
- map_p->regex_delim = newstr(map_p->regex_delim);
- else
- map_p->regex_delim = defdstr;
-
- if (!bitset(REG_NOSUB, pflags))
- {
- /* substring matching */
- int substrings;
- int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
-
- substrings = map_p->regex_pattern_buf->re_nsub + 1;
-
- if (tTd(38, 3))
- sm_dprintf("regex_map_init: nr of substrings %d\n",
- substrings);
-
- if (substrings >= MAX_MATCH)
- {
- syserr("too many substrings, %d max", MAX_MATCH);
- sm_free(map_p->regex_pattern_buf); /* XXX */
- sm_free(map_p); /* XXX */
- return false;
- }
- if (sub_param != NULL && sub_param[0] != '\0')
- {
- /* optional parameter -sfields */
- if (parse_fields(sub_param, fields,
- MAX_MATCH + 1, substrings) == -1)
- return false;
- }
- else
- {
- int i;
-
- /* set default fields */
- for (i = 0; i < substrings; i++)
- fields[i] = i;
- fields[i] = END_OF_FIELDS;
- }
- map_p->regex_subfields = fields;
- if (tTd(38, 3))
- {
- int *ip;
-
- sm_dprintf("regex_map_init: subfields");
- for (ip = fields; *ip != END_OF_FIELDS; ip++)
- sm_dprintf(" %d", *ip);
- sm_dprintf("\n");
- }
- }
- map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
- return true;
-}
-
-static char *
-regex_map_rewrite(map, s, slen, av)
- MAP *map;
- const char *s;
- size_t slen;
- char **av;
-{
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, av[0], strlen(av[0]), NULL);
- else
- return map_rewrite(map, s, slen, av);
-}
-
-char *
-regex_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- int reg_res;
- struct regex_map *map_p;
- regmatch_t pmatch[MAX_MATCH];
-
- if (tTd(38, 20))
- {
- char **cpp;
-
- sm_dprintf("regex_map_lookup: key '%s'\n", name);
- for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
- sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
- }
-
- map_p = (struct regex_map *)(map->map_db1);
- reg_res = regexec(map_p->regex_pattern_buf,
- name, MAX_MATCH, pmatch, 0);
-
- if (bitset(MF_REGEX_NOT, map->map_mflags))
- {
- /* option -n */
- if (reg_res == REG_NOMATCH)
- return regex_map_rewrite(map, "", (size_t) 0, av);
- else
- return NULL;
- }
- if (reg_res == REG_NOMATCH)
- return NULL;
-
- if (map_p->regex_subfields != NULL)
- {
- /* option -s */
- static char retbuf[MAXNAME];
- int fields[MAX_MATCH + 1];
- bool first = true;
- int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
- bool quotemode = false, bslashmode = false;
- register char *dp, *sp;
- char *endp, *ldp;
- int *ip;
-
- dp = retbuf;
- ldp = retbuf + sizeof(retbuf) - 1;
-
- if (av[1] != NULL)
- {
- if (parse_fields(av[1], fields, MAX_MATCH + 1,
- (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
- {
- *statp = EX_CONFIG;
- return NULL;
- }
- ip = fields;
- }
- else
- ip = map_p->regex_subfields;
-
- for ( ; *ip != END_OF_FIELDS; ip++)
- {
- if (!first)
- {
- for (sp = map_p->regex_delim; *sp; sp++)
- {
- if (dp < ldp)
- *dp++ = *sp;
- }
- }
- else
- first = false;
-
- if (*ip >= MAX_MATCH ||
- pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
- continue;
-
- sp = name + pmatch[*ip].rm_so;
- endp = name + pmatch[*ip].rm_eo;
- for (; endp > sp; sp++)
- {
- if (dp < ldp)
- {
- if (bslashmode)
- {
- *dp++ = *sp;
- bslashmode = false;
- }
- else if (quotemode && *sp != '"' &&
- *sp != '\\')
- {
- *dp++ = *sp;
- }
- else switch (*dp++ = *sp)
- {
- case '\\':
- bslashmode = true;
- break;
-
- case '(':
- cmntcnt++;
- break;
-
- case ')':
- cmntcnt--;
- break;
-
- case '<':
- anglecnt++;
- break;
-
- case '>':
- anglecnt--;
- break;
-
- case ' ':
- spacecnt++;
- break;
-
- case '"':
- quotemode = !quotemode;
- break;
- }
- }
- }
- }
- 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);
- return NULL;
- }
-
- *dp = '\0';
-
- return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
- }
- return regex_map_rewrite(map, "", (size_t)0, av);
-}
-#endif /* MAP_REGEX */
-/*
-** NSD modules
-*/
-#if MAP_NSD
-
-# include <ndbm.h>
-# define _DATUM_DEFINED
-# include <ns_api.h>
-
-typedef struct ns_map_list
-{
- ns_map_t *map; /* XXX ns_ ? */
- 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));
- memset(ns_map->map, '\0', 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, r;
- char *p;
- ns_map_t *ns_map;
- char keybuf[MAXNAME + 1];
- char buf[MAXLINE];
-
- if (tTd(38, 20))
- sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
-
- buflen = strlen(name);
- if (buflen > sizeof(keybuf) - 1)
- buflen = sizeof(keybuf) - 1; /* XXX simply cut off? */
- memmove(keybuf, name, buflen);
- keybuf[buflen] = '\0';
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- makelower(keybuf);
-
- ns_map = ns_map_t_find(map->map_file);
- if (ns_map == NULL)
- {
- if (tTd(38, 20))
- sm_dprintf("nsd_map_t_find failed\n");
- *statp = EX_UNAVAILABLE;
- return NULL;
- }
- r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
- buf, sizeof(buf));
- if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
- {
- *statp = EX_TEMPFAIL;
- return NULL;
- }
- if (r == NS_BADREQ
-# ifdef NS_NOPERM
- || r == NS_NOPERM
-# endif /* NS_NOPERM */
- )
- {
- *statp = EX_CONFIG;
- return NULL;
- }
- if (r != NS_SUCCESS)
- {
- *statp = EX_NOTFOUND;
- return NULL;
- }
-
- *statp = EX_OK;
-
- /* Null out trailing \n */
- if ((p = strchr(buf, '\n')) != NULL)
- *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))
- {
- sm_dprintf("arith_map_lookup: key '%s'\n", name);
- for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
- sm_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)
- {
- 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 '+':
- 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;
-
- case 'r':
- r = v[1] - v[0] + 1;
- if (r <= 0)
- return NULL;
- r = get_random() % r + v[0];
- 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)
- (void) sm_snprintf(result, sizeof(result),
- res ? "TRUE" : "FALSE");
- else
- (void) sm_snprintf(result, sizeof(result), "%ld", r);
- return result;
- }
- *statp = EX_CONFIG;
- return NULL;
-}
-
-#if SOCKETMAP
-
-# if NETINET || NETINET6
-# include <arpa/inet.h>
-# endif /* NETINET || NETINET6 */
-
-# define socket_map_next map_stack[0]
-
-/*
-** SOCKET_MAP_OPEN -- open socket table
-*/
-
-bool
-socket_map_open(map, mode)
- MAP *map;
- int mode;
-{
- STAB *s;
- 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 (tTd(38, 2))
- sm_dprintf("socket_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
-
- mode &= O_ACCMODE;
-
- /* sendmail doesn't have the ability to write to SOCKET (yet) */
- if (mode != O_RDONLY)
- {
- /* issue a pseudo-error message */
- errno = SM_EMAPCANTWRITE;
- return false;
- }
-
- if (*map->map_file == '\0')
- {
- syserr("socket map \"%s\": empty or missing socket information",
- map->map_mname);
- return false;
- }
-
- s = socket_map_findconn(map->map_file);
- if (s->s_socketmap != NULL)
- {
- /* Copy open connection */
- map->map_db1 = s->s_socketmap->map_db1;
-
- /* Add this map as head of linked list */
- map->socket_map_next = s->s_socketmap;
- s->s_socketmap = map;
-
- if (tTd(38, 2))
- sm_dprintf("using cached connection\n");
- return true;
- }
-
- if (tTd(38, 2))
- sm_dprintf("opening new connection\n");
-
- /* following code is ripped from milter.c */
- /* XXX It should be put in a library... */
-
- /* protocol:filename or protocol:port@host */
- memset(&addr, '\0', sizeof(addr));
- p = map->map_file;
- 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 */
- syserr("socket map \"%s\": no valid socket protocols available",
- map->map_mname);
- return false;
-# endif /* NETINET6 */
-# endif /* NETINET */
-# endif /* NETUNIX */
- }
-# if NETUNIX
- else if (sm_strcasecmp(p, "unix") == 0 ||
- sm_strcasecmp(p, "local") == 0)
- addr.sa.sa_family = AF_UNIX;
-# endif /* NETUNIX */
-# if NETINET
- else if (sm_strcasecmp(p, "inet") == 0)
- addr.sa.sa_family = AF_INET;
-# endif /* NETINET */
-# if NETINET6
- else if (sm_strcasecmp(p, "inet6") == 0)
- addr.sa.sa_family = AF_INET6;
-# endif /* NETINET6 */
- else
- {
-# ifdef EPROTONOSUPPORT
- errno = EPROTONOSUPPORT;
-# else /* EPROTONOSUPPORT */
- errno = EINVAL;
-# endif /* EPROTONOSUPPORT */
- syserr("socket map \"%s\": unknown socket type %s",
- map->map_mname, p);
- return false;
- }
- *colon++ = ':';
- }
- else
- {
- colon = p;
-#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 */
- syserr("socket map \"%s\": unknown socket type %s",
- map->map_mname, p);
- return false;
-# endif /* NETINET6 */
-# endif /* NETINET */
-#endif /* NETUNIX */
- }
-
-# 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))
- {
- syserr("socket map \"%s\": local socket name %s too long",
- map->map_mname, colon);
- return false;
- }
- errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
- S_IRUSR|S_IWUSR, NULL);
-
- if (errno != 0)
- {
- /* if not safe, don't create */
- syserr("socket map \"%s\": local socket name %s unsafe",
- map->map_mname, colon);
- return false;
- }
-
- (void) sm_strlcpy(addr.sunix.sun_path, colon,
- sizeof(addr.sunix.sun_path));
- addrlen = sizeof(struct sockaddr_un);
- }
- else
-# endif /* NETUNIX */
-# if NETINET || NETINET6
- if (false
-# if NETINET
- || addr.sa.sa_family == AF_INET
-# endif /* NETINET */
-# if NETINET6
- || addr.sa.sa_family == AF_INET6
-# endif /* NETINET6 */
- )
- {
- unsigned short port;
-
- /* Parse port@host */
- at = strchr(colon, '@');
- if (at == NULL)
- {
- syserr("socket map \"%s\": bad address %s (expected port@host)",
- map->map_mname, colon);
- return false;
- }
- *at = '\0';
- if (isascii(*colon) && isdigit(*colon))
- port = htons((unsigned short) atoi(colon));
- else
- {
-# ifdef NO_GETSERVBYNAME
- syserr("socket map \"%s\": invalid port number %s",
- map->map_mname, colon);
- return false;
-# else /* NO_GETSERVBYNAME */
- register struct servent *sp;
-
- sp = getservbyname(colon, "tcp");
- if (sp == NULL)
- {
- syserr("socket map \"%s\": unknown port name %s",
- map->map_mname, colon);
- return false;
- }
- 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 &&
- anynet_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)
- {
- syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
- map->map_mname, at);
- return false;
- }
- }
- else
- {
- syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
- map->map_mname, at);
- return false;
- }
- }
- else
- {
- hp = sm_gethostbyname(at, addr.sa.sa_family);
- if (hp == NULL)
- {
- syserr("socket map \"%s\": Unknown host name %s",
- map->map_mname, at);
- return false;
- }
- 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:
- syserr("socket map \"%s\": Unknown protocol for %s (%d)",
- map->map_mname, at, hp->h_addrtype);
-# if NETINET6
- freehostent(hp);
-# endif /* NETINET6 */
- return false;
- }
- }
- }
- else
-# endif /* NETINET || NETINET6 */
- {
- syserr("socket map \"%s\": unknown socket protocol",
- map->map_mname);
- return false;
- }
-
- /* nope, actually connecting */
- for (;;)
- {
- sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
- if (sock < 0)
- {
- save_errno = errno;
- if (tTd(38, 5))
- sm_dprintf("socket map \"%s\": error creating socket: %s\n",
- map->map_mname,
- sm_errstring(save_errno));
-# if NETINET6
- if (hp != NULL)
- freehostent(hp);
-# endif /* NETINET6 */
- return false;
- }
-
- if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
- break;
-
- /* couldn't connect.... try next address */
- save_errno = errno;
- p = CurHostName;
- CurHostName = at;
- if (tTd(38, 5))
- sm_dprintf("socket_open (%s): open %s failed: %s\n",
- map->map_mname, at, sm_errstring(save_errno));
- CurHostName = p;
- (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(38, 5))
- sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
- map->map_mname, at,
- hp->h_addrtype);
-# if NETINET6
- freehostent(hp);
-# endif /* NETINET6 */
- return false;
- }
- continue;
- }
- p = CurHostName;
- CurHostName = at;
- if (tTd(38, 5))
- sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
- map->map_mname, sm_errstring(save_errno));
- CurHostName = p;
-# if NETINET6
- if (hp != NULL)
- freehostent(hp);
-# endif /* NETINET6 */
- return false;
- }
-# if NETINET6
- if (hp != NULL)
- {
- freehostent(hp);
- hp = NULL;
- }
-# endif /* NETINET6 */
- if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
- SM_TIME_DEFAULT,
- (void *) &sock,
- SM_IO_RDWR,
- NULL)) == NULL)
- {
- close(sock);
- if (tTd(38, 2))
- sm_dprintf("socket_open (%s): failed to create stream: %s\n",
- map->map_mname, sm_errstring(errno));
- return false;
- }
-
- /* Save connection for reuse */
- s->s_socketmap = map;
- return true;
-}
-
-/*
-** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
-**
-** Cache SOCKET connections based on the connection specifier
-** and PID so we don't have multiple connections open to
-** the same server for different maps. Need a separate connection
-** per PID since a parent process may close the map before the
-** child is done with it.
-**
-** Parameters:
-** conn -- SOCKET map connection specifier
-**
-** Returns:
-** Symbol table entry for the SOCKET connection.
-*/
-
-static STAB *
-socket_map_findconn(conn)
- const char *conn;
-{
- char *nbuf;
- STAB *SM_NONVOLATILE s = NULL;
-
- nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
- SM_TRY
- s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
- SM_FINALLY
- sm_free(nbuf);
- SM_END_TRY
- return s;
-}
-
-/*
-** SOCKET_MAP_CLOSE -- close the socket
-*/
-
-void
-socket_map_close(map)
- MAP *map;
-{
- STAB *s;
- MAP *smap;
-
- if (tTd(38, 20))
- sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
- (long) CurrentPid);
-
- /* Check if already closed */
- if (map->map_db1 == NULL)
- {
- if (tTd(38, 20))
- sm_dprintf("socket_map_close(%s) already closed\n",
- map->map_file);
- return;
- }
- sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
-
- /* Mark all the maps that share the connection as closed */
- s = socket_map_findconn(map->map_file);
- smap = s->s_socketmap;
- while (smap != NULL)
- {
- MAP *next;
-
- if (tTd(38, 2) && smap != map)
- sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
- map->map_mname, smap->map_mname);
-
- smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
- smap->map_db1 = NULL;
- next = smap->socket_map_next;
- smap->socket_map_next = NULL;
- smap = next;
- }
- s->s_socketmap = NULL;
-}
-
-/*
-** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
-*/
-
-char *
-socket_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- unsigned int nettolen, replylen, recvlen;
- char *replybuf, *rval, *value, *status, *key;
- SM_FILE_T *f;
- char keybuf[MAXNAME + 1];
-
- replybuf = NULL;
- rval = NULL;
- f = (SM_FILE_T *)map->map_db1;
- if (tTd(38, 20))
- sm_dprintf("socket_map_lookup(%s, %s) %s\n",
- map->map_mname, name, map->map_file);
-
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- {
- nettolen = strlen(name);
- if (nettolen > sizeof(keybuf) - 1)
- nettolen = sizeof(keybuf) - 1;
- memmove(keybuf, name, nettolen);
- keybuf[nettolen] = '\0';
- makelower(keybuf);
- key = keybuf;
- }
- else
- key = name;
-
- nettolen = strlen(map->map_mname) + 1 + strlen(key);
- SM_ASSERT(nettolen > strlen(map->map_mname));
- SM_ASSERT(nettolen > strlen(key));
- if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
- nettolen, map->map_mname, key) == SM_IO_EOF) ||
- (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
- (sm_io_error(f)))
- {
- syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
- map->map_mname);
- *statp = EX_TEMPFAIL;
- goto errcl;
- }
-
- if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
- {
- syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
- map->map_mname);
- *statp = EX_TEMPFAIL;
- goto errcl;
- }
- if (replylen > SOCKETMAP_MAXL)
- {
- syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
- map->map_mname, replylen);
- *statp = EX_TEMPFAIL;
- goto errcl;
- }
- if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
- {
- syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
- map->map_mname);
- *statp = EX_TEMPFAIL;
- goto error;
- }
-
- replybuf = (char *) sm_malloc(replylen + 1);
- if (replybuf == NULL)
- {
- syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
- map->map_mname, replylen + 1);
- *statp = EX_OSERR;
- goto error;
- }
-
- recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
- if (recvlen < replylen)
- {
- syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
- map->map_mname, recvlen, replylen);
- *statp = EX_TEMPFAIL;
- goto errcl;
- }
- if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
- {
- syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
- map->map_mname);
- *statp = EX_TEMPFAIL;
- goto errcl;
- }
- status = replybuf;
- replybuf[recvlen] = '\0';
- value = strchr(replybuf, ' ');
- if (value != NULL)
- {
- *value = '\0';
- value++;
- }
- if (strcmp(status, "OK") == 0)
- {
- *statp = EX_OK;
-
- /* collect the return value */
- if (bitset(MF_MATCHONLY, map->map_mflags))
- rval = map_rewrite(map, key, strlen(key), NULL);
- else
- rval = map_rewrite(map, value, strlen(value), av);
- }
- else if (strcmp(status, "NOTFOUND") == 0)
- {
- *statp = EX_NOTFOUND;
- if (tTd(38, 20))
- sm_dprintf("socket_map_lookup(%s): %s not found\n",
- map->map_mname, key);
- }
- else
- {
- if (tTd(38, 5))
- sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
- map->map_mname, key, status,
- value ? value : "");
- if ((strcmp(status, "TEMP") == 0) ||
- (strcmp(status, "TIMEOUT") == 0))
- *statp = EX_TEMPFAIL;
- else if(strcmp(status, "PERM") == 0)
- *statp = EX_UNAVAILABLE;
- else
- *statp = EX_PROTOCOL;
- }
-
- if (replybuf != NULL)
- sm_free(replybuf);
- return rval;
-
- errcl:
- socket_map_close(map);
- error:
- if (replybuf != NULL)
- sm_free(replybuf);
- return rval;
-}
-#endif /* SOCKETMAP */
diff --git a/contrib/sendmail/src/map.h b/contrib/sendmail/src/map.h
deleted file mode 100644
index dda9999..0000000
--- a/contrib/sendmail/src/map.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 2006 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: map.h,v 8.3 2006/12/19 19:49:51 ca Exp $
- */
-
-#ifndef _MAP_H
-# define _MAP_H 1
-
-extern char *arith_map_lookup __P((MAP *, char *, char **, int *));
-
-extern char *bestmx_map_lookup __P((MAP *, char *, char **, int *));
-
-extern char *bogus_map_lookup __P((MAP *, char *, char **, int *));
-
-extern bool bt_map_open __P((MAP *, int));
-
-extern char *db_map_lookup __P((MAP *, char *, char **, int *));
-
-extern void db_map_store __P((MAP *, char *, char *));
-extern void db_map_close __P((MAP *));
-
-extern bool dequote_init __P((MAP *, char *));
-extern char *dequote_map __P((MAP *, char *, char **, int *));
-
-extern bool dns_map_open __P((MAP *, int));
-extern bool dns_map_parseargs __P((MAP *, char *));
-extern char *dns_map_lookup __P((MAP *, char *, char **, int *));
-
-extern bool dprintf_map_parseargs __P((MAP *, char *));
-extern char *dprintf_map_lookup __P((MAP *, char *, char **, int *));
-
-extern bool hash_map_open __P((MAP *, int));
-
-extern bool host_map_init __P((MAP *, char *));
-extern char *host_map_lookup __P((MAP *, char *, char **, int *));
-
-extern char *impl_map_lookup __P((MAP *, char *, char **, int *));
-extern void impl_map_store __P((MAP *, char *, char *));
-extern bool impl_map_open __P((MAP *, int));
-extern void impl_map_close __P((MAP *));
-
-extern char *macro_map_lookup __P((MAP *, char *, char **, int *));
-
-extern bool map_parseargs __P((MAP *, char *));
-
-extern bool nis_map_open __P((MAP *, int));
-extern char *nis_map_lookup __P((MAP *, char *, char **, int *));
-
-extern bool null_map_open __P((MAP *, int));
-extern void null_map_close __P((MAP *));
-extern char *null_map_lookup __P((MAP *, char *, char **, int *));
-extern void null_map_store __P((MAP *, char *, char *));
-
-extern char *prog_map_lookup __P((MAP *, char *, char **, int *));
-
-extern bool regex_map_init __P((MAP *, char *));
-extern char *regex_map_lookup __P((MAP *, char *, char **, int *));
-
-extern char *seq_map_lookup __P((MAP *, char *, char **, int *));
-extern void seq_map_store __P((MAP *, char *, char *));
-extern bool seq_map_parse __P((MAP *, char *));
-
-extern char *stab_map_lookup __P((MAP *, char *, char **, int *));
-extern void stab_map_store __P((MAP *, char *, char *));
-extern bool stab_map_open __P((MAP *, int));
-
-extern bool switch_map_open __P((MAP *, int));
-
-extern bool syslog_map_parseargs __P((MAP *, char *));
-extern char *syslog_map_lookup __P((MAP *, char *, char **, int *));
-
-extern bool text_map_open __P((MAP *, int));
-extern char *text_map_lookup __P((MAP *, char *, char **, int *));
-
-extern char *udb_map_lookup __P((MAP *, char *, char **, int *));
-
-extern bool user_map_open __P((MAP *, int));
-extern char *user_map_lookup __P((MAP *, char *, char **, int *));
-
-#endif /* ! _MAP_H */
diff --git a/contrib/sendmail/src/mci.c b/contrib/sendmail/src/mci.c
deleted file mode 100644
index ae33f66..0000000
--- a/contrib/sendmail/src/mci.c
+++ /dev/null
@@ -1,1561 +0,0 @@
-/*
- * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: mci.c,v 8.218 2006/08/15 23:24:57 ca Exp $")
-
-#if NETINET || NETINET6
-# include <arpa/inet.h>
-#endif /* NETINET || NETINET6 */
-
-#include <dirent.h>
-
-static int mci_generate_persistent_path __P((const char *, char *,
- int, bool));
-static bool mci_load_persistent __P((MCI *));
-static void mci_uncache __P((MCI **, bool));
-static int mci_lock_host_statfile __P((MCI *));
-static int mci_read_persistent __P((SM_FILE_T *, MCI *));
-
-/*
-** Mail Connection Information (MCI) Caching Module.
-**
-** There are actually two separate things cached. The first is
-** the set of all open connections -- these are stored in a
-** (small) list. The second is stored in the symbol table; it
-** has the overall status for all hosts, whether or not there
-** is a connection open currently.
-**
-** There should never be too many connections open (since this
-** could flood the socket table), nor should a connection be
-** allowed to sit idly for too long.
-**
-** MaxMciCache is the maximum number of open connections that
-** will be supported.
-**
-** MciCacheTimeout is the time (in seconds) that a connection
-** is permitted to survive without activity.
-**
-** We actually try any cached connections by sending a RSET
-** before we use them; if the RSET fails we close down the
-** connection and reopen it (see smtpprobe()).
-**
-** The persistent MCI code is donated by Mark Lovell and Paul
-** Vixie. It is based on the long term host status code in KJS
-** written by Paul but has been adapted by Mark to fit into the
-** MCI structure.
-*/
-
-static MCI **MciCache; /* the open connection cache */
-
-/*
-** MCI_CACHE -- enter a connection structure into the open connection cache
-**
-** This may cause something else to be flushed.
-**
-** Parameters:
-** mci -- the connection to cache.
-**
-** Returns:
-** none.
-*/
-
-void
-mci_cache(mci)
- register MCI *mci;
-{
- register MCI **mcislot;
-
- /*
- ** Find the best slot. This may cause expired connections
- ** to be closed.
- */
-
- mcislot = mci_scan(mci);
- if (mcislot == NULL)
- {
- /* we don't support caching */
- return;
- }
-
- if (mci->mci_host == NULL)
- return;
-
- /* if this is already cached, we are done */
- if (bitset(MCIF_CACHED, mci->mci_flags))
- return;
-
- /* otherwise we may have to clear the slot */
- if (*mcislot != NULL)
- mci_uncache(mcislot, true);
-
- if (tTd(42, 5))
- sm_dprintf("mci_cache: caching %p (%s) in slot %d\n",
- mci, mci->mci_host, (int) (mcislot - MciCache));
- if (tTd(91, 100))
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "mci_cache: caching %lx (%.100s) in slot %d",
- (unsigned long) mci, mci->mci_host,
- (int) (mcislot - MciCache));
-
- *mcislot = mci;
- mci->mci_flags |= MCIF_CACHED;
-}
-/*
-** MCI_SCAN -- scan the cache, flush junk, and return best slot
-**
-** Parameters:
-** savemci -- never flush this one. Can be null.
-**
-** Returns:
-** The LRU (or empty) slot.
-*/
-
-MCI **
-mci_scan(savemci)
- MCI *savemci;
-{
- time_t now;
- register MCI **bestmci;
- register MCI *mci;
- register int i;
-
- if (MaxMciCache <= 0)
- {
- /* we don't support caching */
- return NULL;
- }
-
- if (MciCache == NULL)
- {
- /* first call */
- MciCache = (MCI **) sm_pmalloc_x(MaxMciCache * sizeof(*MciCache));
- memset((char *) MciCache, '\0', MaxMciCache * sizeof(*MciCache));
- return &MciCache[0];
- }
-
- now = curtime();
- bestmci = &MciCache[0];
- for (i = 0; i < MaxMciCache; i++)
- {
- mci = MciCache[i];
- if (mci == NULL || mci->mci_state == MCIS_CLOSED)
- {
- bestmci = &MciCache[i];
- continue;
- }
- if ((mci->mci_lastuse + MciCacheTimeout <= now ||
- (mci->mci_mailer != NULL &&
- mci->mci_mailer->m_maxdeliveries > 0 &&
- mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&&
- mci != savemci)
- {
- /* connection idle too long or too many deliveries */
- bestmci = &MciCache[i];
-
- /* close it */
- mci_uncache(bestmci, true);
- continue;
- }
- if (*bestmci == NULL)
- continue;
- if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
- bestmci = &MciCache[i];
- }
- return bestmci;
-}
-/*
-** MCI_UNCACHE -- remove a connection from a slot.
-**
-** May close a connection.
-**
-** Parameters:
-** mcislot -- the slot to empty.
-** doquit -- if true, send QUIT protocol on this connection.
-** if false, we are assumed to be in a forked child;
-** all we want to do is close the file(s).
-**
-** Returns:
-** none.
-*/
-
-static void
-mci_uncache(mcislot, doquit)
- register MCI **mcislot;
- bool doquit;
-{
- register MCI *mci;
- extern ENVELOPE BlankEnvelope;
-
- mci = *mcislot;
- if (mci == NULL)
- return;
- *mcislot = NULL;
- if (mci->mci_host == NULL)
- return;
-
- mci_unlock_host(mci);
-
- if (tTd(42, 5))
- sm_dprintf("mci_uncache: uncaching %p (%s) from slot %d (%d)\n",
- mci, mci->mci_host, (int) (mcislot - MciCache),
- doquit);
- if (tTd(91, 100))
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)",
- (unsigned long) mci, mci->mci_host,
- (int) (mcislot - MciCache), doquit);
-
- mci->mci_deliveries = 0;
- if (doquit)
- {
- message("Closing connection to %s", mci->mci_host);
-
- mci->mci_flags &= ~MCIF_CACHED;
-
- /* only uses the envelope to flush the transcript file */
- if (mci->mci_state != MCIS_CLOSED)
- smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
-#if XLA
- xla_host_end(mci->mci_host);
-#endif /* XLA */
- }
- else
- {
- if (mci->mci_in != NULL)
- (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
- if (mci->mci_out != NULL)
- (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
- mci->mci_in = mci->mci_out = NULL;
- mci->mci_state = MCIS_CLOSED;
- mci->mci_exitstat = EX_OK;
- mci->mci_errno = 0;
- mci->mci_flags = 0;
-
- mci->mci_retryrcpt = false;
- mci->mci_tolist = NULL;
-#if PIPELINING
- mci->mci_okrcpts = 0;
-#endif /* PIPELINING */
- }
-
- SM_FREE_CLR(mci->mci_status);
- SM_FREE_CLR(mci->mci_rstatus);
- SM_FREE_CLR(mci->mci_heloname);
- if (mci->mci_rpool != NULL)
- {
- sm_rpool_free(mci->mci_rpool);
- mci->mci_macro.mac_rpool = NULL;
- mci->mci_rpool = NULL;
- }
-}
-/*
-** MCI_FLUSH -- flush the entire cache
-**
-** Parameters:
-** doquit -- if true, send QUIT protocol.
-** if false, just close the connection.
-** allbut -- but leave this one open.
-**
-** Returns:
-** none.
-*/
-
-void
-mci_flush(doquit, allbut)
- bool doquit;
- MCI *allbut;
-{
- register int i;
-
- if (MciCache == NULL)
- return;
-
- for (i = 0; i < MaxMciCache; i++)
- {
- if (allbut != MciCache[i])
- mci_uncache(&MciCache[i], doquit);
- }
-}
-/*
-** MCI_GET -- get information about a particular host
-**
-** Parameters:
-** host -- host to look for.
-** m -- mailer.
-**
-** Returns:
-** mci for this host (might be new).
-*/
-
-MCI *
-mci_get(host, m)
- char *host;
- MAILER *m;
-{
- register MCI *mci;
- register STAB *s;
- extern SOCKADDR CurHostAddr;
-
- /* clear CurHostAddr so we don't get a bogus address with this name */
- memset(&CurHostAddr, '\0', sizeof(CurHostAddr));
-
- /* clear out any expired connections */
- (void) mci_scan(NULL);
-
- if (m->m_mno < 0)
- syserr("!negative mno %d (%s)", m->m_mno, m->m_name);
-
- s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
- mci = &s->s_mci;
-
- /* initialize per-message data */
- mci->mci_retryrcpt = false;
- mci->mci_tolist = NULL;
-#if PIPELINING
- mci->mci_okrcpts = 0;
-#endif /* PIPELINING */
-
- if (mci->mci_rpool == NULL)
- mci->mci_rpool = sm_rpool_new_x(NULL);
-
- if (mci->mci_macro.mac_rpool == NULL)
- mci->mci_macro.mac_rpool = mci->mci_rpool;
-
- /*
- ** We don't need to load the persistent data if we have data
- ** already loaded in the cache.
- */
-
- if (mci->mci_host == NULL &&
- (mci->mci_host = s->s_name) != NULL &&
- !mci_load_persistent(mci))
- {
- if (tTd(42, 2))
- sm_dprintf("mci_get(%s %s): lock failed\n",
- host, m->m_name);
- mci->mci_exitstat = EX_TEMPFAIL;
- mci->mci_state = MCIS_CLOSED;
- mci->mci_statfile = NULL;
- return mci;
- }
-
- if (tTd(42, 2))
- {
- sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n",
- host, m->m_name, mci->mci_state, mci->mci_flags,
- mci->mci_exitstat, mci->mci_errno);
- }
-
- if (mci->mci_state == MCIS_OPEN)
- {
- /* poke the connection to see if it's still alive */
- (void) smtpprobe(mci);
-
- /* reset the stored state in the event of a timeout */
- if (mci->mci_state != MCIS_OPEN)
- {
- mci->mci_errno = 0;
- mci->mci_exitstat = EX_OK;
- mci->mci_state = MCIS_CLOSED;
- }
- else
- {
- /* get peer host address */
- /* (this should really be in the mci struct) */
- SOCKADDR_LEN_T socklen = sizeof(CurHostAddr);
-
- (void) getpeername(sm_io_getinfo(mci->mci_in,
- SM_IO_WHAT_FD, NULL),
- (struct sockaddr *) &CurHostAddr, &socklen);
- }
- }
- if (mci->mci_state == MCIS_CLOSED)
- {
- time_t now = curtime();
-
- /* if this info is stale, ignore it */
- if (mci->mci_lastuse + MciInfoTimeout <= now)
- {
- mci->mci_lastuse = now;
- mci->mci_errno = 0;
- mci->mci_exitstat = EX_OK;
- }
- }
-
- return mci;
-}
-
-/*
-** MCI_CLOSE -- (forcefully) close files used for a connection.
-** Note: this is a last resort, usually smtpquit() or endmailer()
-** should be used to close a connection.
-**
-** Parameters:
-** mci -- the connection to close.
-** where -- where has this been called?
-**
-** Returns:
-** none.
-*/
-
-void
-mci_close(mci, where)
- MCI *mci;
- char *where;
-{
- bool dumped;
-
- if (mci == NULL)
- return;
- dumped = false;
- if (mci->mci_out != NULL)
- {
- if (tTd(56, 1))
- {
- sm_dprintf("mci_close: mci_out!=NULL, where=%s\n",
- where);
- mci_dump(sm_debug_file(), mci, false);
- dumped = true;
- }
- (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT);
- mci->mci_out = NULL;
- }
- if (mci->mci_in != NULL)
- {
- if (tTd(56, 1))
- {
- sm_dprintf("mci_close: mci_in!=NULL, where=%s\n",
- where);
- if (!dumped)
- mci_dump(sm_debug_file(), mci, false);
- }
- (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT);
- mci->mci_in = NULL;
- }
- mci->mci_state = MCIS_CLOSED;
-}
-
-/*
-** MCI_NEW -- allocate new MCI structure
-**
-** Parameters:
-** rpool -- if non-NULL: allocate from that rpool.
-**
-** Returns:
-** mci (new).
-*/
-
-MCI *
-mci_new(rpool)
- SM_RPOOL_T *rpool;
-{
- register MCI *mci;
-
- if (rpool == NULL)
- mci = (MCI *) sm_malloc_x(sizeof(*mci));
- else
- mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof(*mci));
- memset((char *) mci, '\0', sizeof(*mci));
- mci->mci_rpool = sm_rpool_new_x(NULL);
- mci->mci_macro.mac_rpool = mci->mci_rpool;
- return mci;
-}
-/*
-** MCI_MATCH -- check connection cache for a particular host
-**
-** Parameters:
-** host -- host to look for.
-** m -- mailer.
-**
-** Returns:
-** true iff open connection exists.
-*/
-
-bool
-mci_match(host, m)
- char *host;
- MAILER *m;
-{
- register MCI *mci;
- register STAB *s;
-
- if (m->m_mno < 0 || m->m_mno > MAXMAILERS)
- return false;
- s = stab(host, ST_MCI + m->m_mno, ST_FIND);
- if (s == NULL)
- return false;
-
- mci = &s->s_mci;
- return mci->mci_state == MCIS_OPEN;
-}
-/*
-** MCI_SETSTAT -- set status codes in MCI structure.
-**
-** Parameters:
-** mci -- the MCI structure to set.
-** xstat -- the exit status code.
-** dstat -- the DSN status code.
-** rstat -- the SMTP status code.
-**
-** Returns:
-** none.
-*/
-
-void
-mci_setstat(mci, xstat, dstat, rstat)
- MCI *mci;
- int xstat;
- char *dstat;
- char *rstat;
-{
- /* protocol errors should never be interpreted as sticky */
- if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL)
- mci->mci_exitstat = xstat;
-
- SM_FREE_CLR(mci->mci_status);
- if (dstat != NULL)
- mci->mci_status = sm_strdup_x(dstat);
-
- SM_FREE_CLR(mci->mci_rstatus);
- if (rstat != NULL)
- mci->mci_rstatus = sm_strdup_x(rstat);
-}
-/*
-** MCI_DUMP -- dump the contents of an MCI structure.
-**
-** Parameters:
-** fp -- output file pointer
-** mci -- the MCI structure to dump.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** none.
-*/
-
-struct mcifbits
-{
- int mcif_bit; /* flag bit */
- char *mcif_name; /* flag name */
-};
-static struct mcifbits MciFlags[] =
-{
- { MCIF_VALID, "VALID" },
- { MCIF_CACHED, "CACHED" },
- { MCIF_ESMTP, "ESMTP" },
- { MCIF_EXPN, "EXPN" },
- { MCIF_SIZE, "SIZE" },
- { MCIF_8BITMIME, "8BITMIME" },
- { MCIF_7BIT, "7BIT" },
- { MCIF_INHEADER, "INHEADER" },
- { MCIF_CVT8TO7, "CVT8TO7" },
- { MCIF_DSN, "DSN" },
- { MCIF_8BITOK, "8BITOK" },
- { MCIF_CVT7TO8, "CVT7TO8" },
- { MCIF_INMIME, "INMIME" },
- { MCIF_AUTH, "AUTH" },
- { MCIF_AUTHACT, "AUTHACT" },
- { MCIF_ENHSTAT, "ENHSTAT" },
- { MCIF_PIPELINED, "PIPELINED" },
-#if STARTTLS
- { MCIF_TLS, "TLS" },
- { MCIF_TLSACT, "TLSACT" },
-#endif /* STARTTLS */
- { MCIF_DLVR_BY, "DLVR_BY" },
- { 0, NULL }
-};
-
-void
-mci_dump(fp, mci, logit)
- SM_FILE_T *fp;
- register MCI *mci;
- bool logit;
-{
- register char *p;
- char *sep;
- char buf[4000];
-
- sep = logit ? " " : "\n\t";
- p = buf;
- (void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci);
- p += strlen(p);
- if (mci == NULL)
- {
- (void) sm_snprintf(p, SPACELEFT(buf, p), "NULL");
- goto printit;
- }
- (void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags);
- p += strlen(p);
-
- /*
- ** The following check is just for paranoia. It protects the
- ** assignment in the if() clause. If there's not some minimum
- ** amount of space we can stop right now. The check will not
- ** trigger as long as sizeof(buf)=4000.
- */
-
- if (p >= buf + sizeof(buf) - 4)
- goto printit;
- if (mci->mci_flags != 0)
- {
- struct mcifbits *f;
-
- *p++ = '<'; /* protected above */
- for (f = MciFlags; f->mcif_bit != 0; f++)
- {
- if (!bitset(f->mcif_bit, mci->mci_flags))
- continue;
- (void) sm_strlcpyn(p, SPACELEFT(buf, p), 2,
- f->mcif_name, ",");
- p += strlen(p);
- }
- p[-1] = '>';
- }
-
- /* Note: sm_snprintf() takes care of NULL arguments for %s */
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
- sep, mci->mci_errno, mci->mci_herrno,
- mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep);
- p += strlen(p);
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "maxsize=%ld, phase=%s, mailer=%s,%s",
- mci->mci_maxsize, mci->mci_phase,
- mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
- sep);
- p += strlen(p);
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "status=%s, rstatus=%s,%s",
- mci->mci_status, mci->mci_rstatus, sep);
- p += strlen(p);
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "host=%s, lastuse=%s",
- mci->mci_host, ctime(&mci->mci_lastuse));
-printit:
- if (logit)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf);
- else
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s\n", buf);
-}
-/*
-** MCI_DUMP_ALL -- print the entire MCI cache
-**
-** Parameters:
-** fp -- output file pointer
-** logit -- if set, log the result instead of printing
-** to stdout.
-**
-** Returns:
-** none.
-*/
-
-void
-mci_dump_all(fp, logit)
- SM_FILE_T *fp;
- bool logit;
-{
- register int i;
-
- if (MciCache == NULL)
- return;
-
- for (i = 0; i < MaxMciCache; i++)
- mci_dump(fp, MciCache[i], logit);
-}
-/*
-** MCI_LOCK_HOST -- Lock host while sending.
-**
-** If we are contacting a host, we'll need to
-** update the status information in the host status
-** file, and if we want to do that, we ought to have
-** locked it. This has the (according to some)
-** desirable effect of serializing connectivity with
-** remote hosts -- i.e.: one connection to a given
-** host at a time.
-**
-** Parameters:
-** mci -- containing the host we want to lock.
-**
-** Returns:
-** EX_OK -- got the lock.
-** EX_TEMPFAIL -- didn't get the lock.
-*/
-
-int
-mci_lock_host(mci)
- MCI *mci;
-{
- if (mci == NULL)
- {
- if (tTd(56, 1))
- sm_dprintf("mci_lock_host: NULL mci\n");
- return EX_OK;
- }
-
- if (!SingleThreadDelivery)
- return EX_OK;
-
- return mci_lock_host_statfile(mci);
-}
-
-static int
-mci_lock_host_statfile(mci)
- MCI *mci;
-{
- int save_errno = errno;
- int retVal = EX_OK;
- char fname[MAXPATHLEN];
-
- if (HostStatDir == NULL || mci->mci_host == NULL)
- return EX_OK;
-
- if (tTd(56, 2))
- sm_dprintf("mci_lock_host: attempting to lock %s\n",
- mci->mci_host);
-
- if (mci_generate_persistent_path(mci->mci_host, fname, sizeof(fname),
- true) < 0)
- {
- /* of course this should never happen */
- if (tTd(56, 2))
- sm_dprintf("mci_lock_host: Failed to generate host path for %s\n",
- mci->mci_host);
-
- retVal = EX_TEMPFAIL;
- goto cleanup;
- }
-
- mci->mci_statfile = safefopen(fname, O_RDWR, FileMode,
- SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT);
-
- if (mci->mci_statfile == NULL)
- {
- syserr("mci_lock_host: cannot create host lock file %s", fname);
- goto cleanup;
- }
-
- if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL),
- fname, "", LOCK_EX|LOCK_NB))
- {
- if (tTd(56, 2))
- sm_dprintf("mci_lock_host: couldn't get lock on %s\n",
- fname);
- (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT);
- mci->mci_statfile = NULL;
- retVal = EX_TEMPFAIL;
- goto cleanup;
- }
-
- if (tTd(56, 12) && mci->mci_statfile != NULL)
- sm_dprintf("mci_lock_host: Sanity check -- lock is good\n");
-
-cleanup:
- errno = save_errno;
- return retVal;
-}
-/*
-** MCI_UNLOCK_HOST -- unlock host
-**
-** Clean up the lock on a host, close the file, let
-** someone else use it.
-**
-** Parameters:
-** mci -- us.
-**
-** Returns:
-** nothing.
-*/
-
-void
-mci_unlock_host(mci)
- MCI *mci;
-{
- int save_errno = errno;
-
- if (mci == NULL)
- {
- if (tTd(56, 1))
- sm_dprintf("mci_unlock_host: NULL mci\n");
- return;
- }
-
- if (HostStatDir == NULL || mci->mci_host == NULL)
- return;
-
- if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL)
- {
- if (tTd(56, 1))
- sm_dprintf("mci_unlock_host: stat file already locked\n");
- }
- else
- {
- if (tTd(56, 2))
- sm_dprintf("mci_unlock_host: store prior to unlock\n");
- mci_store_persistent(mci);
- }
-
- if (mci->mci_statfile != NULL)
- {
- (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT);
- mci->mci_statfile = NULL;
- }
-
- errno = save_errno;
-}
-/*
-** MCI_LOAD_PERSISTENT -- load persistent host info
-**
-** Load information about host that is kept
-** in common for all running sendmails.
-**
-** Parameters:
-** mci -- the host/connection to load persistent info for.
-**
-** Returns:
-** true -- lock was successful
-** false -- lock failed
-*/
-
-static bool
-mci_load_persistent(mci)
- MCI *mci;
-{
- int save_errno = errno;
- bool locked = true;
- SM_FILE_T *fp;
- char fname[MAXPATHLEN];
-
- if (mci == NULL)
- {
- if (tTd(56, 1))
- sm_dprintf("mci_load_persistent: NULL mci\n");
- return true;
- }
-
- if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL)
- return true;
-
- /* Already have the persistent information in memory */
- if (SingleThreadDelivery && mci->mci_statfile != NULL)
- return true;
-
- if (tTd(56, 1))
- sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n",
- mci->mci_host);
-
- if (mci_generate_persistent_path(mci->mci_host, fname, sizeof(fname),
- false) < 0)
- {
- /* Not much we can do if the file isn't there... */
- if (tTd(56, 1))
- sm_dprintf("mci_load_persistent: Couldn't generate host path\n");
- goto cleanup;
- }
-
- fp = safefopen(fname, O_RDONLY, FileMode,
- SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
- if (fp == NULL)
- {
- /* I can't think of any reason this should ever happen */
- if (tTd(56, 1))
- sm_dprintf("mci_load_persistent: open(%s): %s\n",
- fname, sm_errstring(errno));
- goto cleanup;
- }
-
- FileName = fname;
- locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "",
- LOCK_SH|LOCK_NB);
- if (locked)
- {
- (void) mci_read_persistent(fp, mci);
- (void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname,
- "", LOCK_UN);
- }
- FileName = NULL;
- (void) sm_io_close(fp, SM_TIME_DEFAULT);
-
-cleanup:
- errno = save_errno;
- return locked;
-}
-/*
-** MCI_READ_PERSISTENT -- read persistent host status file
-**
-** Parameters:
-** fp -- the file pointer to read.
-** mci -- the pointer to fill in.
-**
-** Returns:
-** -1 -- if the file was corrupt.
-** 0 -- otherwise.
-**
-** Warning:
-** This code makes the assumption that this data
-** will be read in an atomic fashion, and that the data
-** was written in an atomic fashion. Any other functioning
-** may lead to some form of insanity. This should be
-** perfectly safe due to underlying stdio buffering.
-*/
-
-static int
-mci_read_persistent(fp, mci)
- SM_FILE_T *fp;
- register MCI *mci;
-{
- int ver;
- register char *p;
- int saveLineNumber = LineNumber;
- char buf[MAXLINE];
-
- if (fp == NULL)
- {
- syserr("mci_read_persistent: NULL fp");
- /* NOTREACHED */
- return -1;
- }
- if (mci == NULL)
- {
- syserr("mci_read_persistent: NULL mci");
- /* NOTREACHED */
- return -1;
- }
- if (tTd(56, 93))
- {
- sm_dprintf("mci_read_persistent: fp=%lx, mci=",
- (unsigned long) fp);
- }
-
- SM_FREE_CLR(mci->mci_status);
- SM_FREE_CLR(mci->mci_rstatus);
-
- sm_io_rewind(fp, SM_TIME_DEFAULT);
- ver = -1;
- LineNumber = 0;
- while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
- {
- LineNumber++;
- p = strchr(buf, '\n');
- if (p != NULL)
- *p = '\0';
- switch (buf[0])
- {
- case 'V': /* version stamp */
- ver = atoi(&buf[1]);
- if (ver < 0 || ver > 0)
- syserr("Unknown host status version %d: %d max",
- ver, 0);
- break;
-
- case 'E': /* UNIX error number */
- mci->mci_errno = atoi(&buf[1]);
- break;
-
- case 'H': /* DNS error number */
- mci->mci_herrno = atoi(&buf[1]);
- break;
-
- case 'S': /* UNIX exit status */
- mci->mci_exitstat = atoi(&buf[1]);
- break;
-
- case 'D': /* DSN status */
- mci->mci_status = newstr(&buf[1]);
- break;
-
- case 'R': /* SMTP status */
- mci->mci_rstatus = newstr(&buf[1]);
- break;
-
- case 'U': /* last usage time */
- mci->mci_lastuse = atol(&buf[1]);
- break;
-
- case '.': /* end of file */
- if (tTd(56, 93))
- mci_dump(sm_debug_file(), mci, false);
- return 0;
-
- default:
- sm_syslog(LOG_CRIT, NOQID,
- "%s: line %d: Unknown host status line \"%s\"",
- FileName == NULL ? mci->mci_host : FileName,
- LineNumber, buf);
- LineNumber = saveLineNumber;
- return -1;
- }
- }
- LineNumber = saveLineNumber;
- if (tTd(56, 93))
- sm_dprintf("incomplete (missing dot for EOF)\n");
- if (ver < 0)
- return -1;
- return 0;
-}
-/*
-** MCI_STORE_PERSISTENT -- Store persistent MCI information
-**
-** Store information about host that is kept
-** in common for all running sendmails.
-**
-** Parameters:
-** mci -- the host/connection to store persistent info for.
-**
-** Returns:
-** none.
-*/
-
-void
-mci_store_persistent(mci)
- MCI *mci;
-{
- int save_errno = errno;
-
- if (mci == NULL)
- {
- if (tTd(56, 1))
- sm_dprintf("mci_store_persistent: NULL mci\n");
- return;
- }
-
- if (HostStatDir == NULL || mci->mci_host == NULL)
- return;
-
- if (tTd(56, 1))
- sm_dprintf("mci_store_persistent: Storing information for %s\n",
- mci->mci_host);
-
- if (mci->mci_statfile == NULL)
- {
- if (tTd(56, 1))
- sm_dprintf("mci_store_persistent: no statfile\n");
- return;
- }
-
- sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT);
-#if !NOFTRUNCATE
- (void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL),
- (off_t) 0);
-#endif /* !NOFTRUNCATE */
-
- (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n");
- (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n",
- mci->mci_errno);
- (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n",
- mci->mci_herrno);
- (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n",
- mci->mci_exitstat);
- if (mci->mci_status != NULL)
- (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT,
- "D%.80s\n",
- denlstring(mci->mci_status, true, false));
- if (mci->mci_rstatus != NULL)
- (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT,
- "R%.80s\n",
- denlstring(mci->mci_rstatus, true, false));
- (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n",
- (long)(mci->mci_lastuse));
- (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n");
-
- (void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT);
-
- errno = save_errno;
- return;
-}
-/*
-** MCI_TRAVERSE_PERSISTENT -- walk persistent status tree
-**
-** Recursively find all the mci host files in `pathname'. Default to
-** main host status directory if no path is provided.
-** Call (*action)(pathname, host) for each file found.
-**
-** Note: all information is collected in a list before it is processed.
-** This may not be the best way to do it, but it seems safest, since
-** the file system would be touched while we are attempting to traverse
-** the directory tree otherwise (during purges).
-**
-** Parameters:
-** action -- function to call on each node. If returns < 0,
-** return immediately.
-** pathname -- root of tree. If null, use main host status
-** directory.
-**
-** Returns:
-** < 0 -- if any action routine returns a negative value, that
-** value is returned.
-** 0 -- if we successfully went to completion.
-** > 0 -- return status from action()
-*/
-
-int
-mci_traverse_persistent(action, pathname)
- int (*action)__P((char *, char *));
- char *pathname;
-{
- struct stat statbuf;
- DIR *d;
- int ret;
-
- if (pathname == NULL)
- pathname = HostStatDir;
- if (pathname == NULL)
- return -1;
-
- if (tTd(56, 1))
- sm_dprintf("mci_traverse: pathname is %s\n", pathname);
-
- ret = stat(pathname, &statbuf);
- if (ret < 0)
- {
- if (tTd(56, 2))
- sm_dprintf("mci_traverse: Failed to stat %s: %s\n",
- pathname, sm_errstring(errno));
- return ret;
- }
- if (S_ISDIR(statbuf.st_mode))
- {
- bool leftone, removedone;
- size_t len;
- char *newptr;
- struct dirent *e;
- char newpath[MAXPATHLEN];
-#if MAXPATHLEN <= MAXNAMLEN - 3
- ERROR "MAXPATHLEN <= MAXNAMLEN - 3"
-#endif /* MAXPATHLEN <= MAXNAMLEN - 3 */
-
- if ((d = opendir(pathname)) == NULL)
- {
- if (tTd(56, 2))
- sm_dprintf("mci_traverse: opendir %s: %s\n",
- pathname, sm_errstring(errno));
- return -1;
- }
- len = sizeof(newpath) - MAXNAMLEN - 3;
- if (sm_strlcpy(newpath, pathname, len) >= len)
- {
- if (tTd(56, 2))
- sm_dprintf("mci_traverse: path \"%s\" too long",
- pathname);
- return -1;
- }
- newptr = newpath + strlen(newpath);
- *newptr++ = '/';
-
- /*
- ** repeat until no file has been removed
- ** this may become ugly when several files "expire"
- ** during these loops, but it's better than doing
- ** a rewinddir() inside the inner loop
- */
-
- do
- {
- leftone = removedone = false;
- while ((e = readdir(d)) != NULL)
- {
- if (e->d_name[0] == '.')
- continue;
-
- (void) sm_strlcpy(newptr, e->d_name,
- sizeof(newpath) -
- (newptr - newpath));
-
- if (StopRequest)
- stop_sendmail();
- ret = mci_traverse_persistent(action, newpath);
- if (ret < 0)
- break;
- if (ret == 1)
- leftone = true;
- if (!removedone && ret == 0 &&
- action == mci_purge_persistent)
- removedone = true;
- }
- if (ret < 0)
- break;
-
- /*
- ** The following appears to be
- ** necessary during purges, since
- ** we modify the directory structure
- */
-
- if (removedone)
- rewinddir(d);
- if (tTd(56, 40))
- sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n",
- pathname, ret, removedone, leftone);
- } while (removedone);
-
- /* purge (or whatever) the directory proper */
- if (!leftone)
- {
- *--newptr = '\0';
- ret = (*action)(newpath, NULL);
- }
- (void) closedir(d);
- }
- else if (S_ISREG(statbuf.st_mode))
- {
- char *end = pathname + strlen(pathname) - 1;
- char *start;
- char *scan;
- char host[MAXHOSTNAMELEN];
- char *hostptr = host;
-
- /*
- ** Reconstruct the host name from the path to the
- ** persistent information.
- */
-
- do
- {
- if (hostptr != host)
- *(hostptr++) = '.';
- start = end;
- while (start > pathname && *(start - 1) != '/')
- start--;
-
- if (*end == '.')
- end--;
-
- for (scan = start; scan <= end; scan++)
- *(hostptr++) = *scan;
-
- end = start - 2;
- } while (end > pathname && *end == '.');
-
- *hostptr = '\0';
-
- /*
- ** Do something with the file containing the persistent
- ** information.
- */
-
- ret = (*action)(pathname, host);
- }
-
- return ret;
-}
-/*
-** MCI_PRINT_PERSISTENT -- print persistent info
-**
-** Dump the persistent information in the file 'pathname'
-**
-** Parameters:
-** pathname -- the pathname to the status file.
-** hostname -- the corresponding host name.
-**
-** Returns:
-** 0
-*/
-
-int
-mci_print_persistent(pathname, hostname)
- char *pathname;
- char *hostname;
-{
- static bool initflag = false;
- SM_FILE_T *fp;
- int width = Verbose ? 78 : 25;
- bool locked;
- MCI mcib;
-
- /* skip directories */
- if (hostname == NULL)
- return 0;
-
- if (StopRequest)
- stop_sendmail();
-
- if (!initflag)
- {
- initflag = true;
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- " -------------- Hostname --------------- How long ago ---------Results---------\n");
- }
-
- fp = safefopen(pathname, O_RDONLY, FileMode,
- SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
-
- if (fp == NULL)
- {
- if (tTd(56, 1))
- sm_dprintf("mci_print_persistent: cannot open %s: %s\n",
- pathname, sm_errstring(errno));
- return 0;
- }
-
- FileName = pathname;
- memset(&mcib, '\0', sizeof(mcib));
- if (mci_read_persistent(fp, &mcib) < 0)
- {
- syserr("%s: could not read status file", pathname);
- (void) sm_io_close(fp, SM_TIME_DEFAULT);
- FileName = NULL;
- return 0;
- }
-
- locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname,
- "", LOCK_SH|LOCK_NB);
- (void) sm_io_close(fp, SM_TIME_DEFAULT);
- FileName = NULL;
-
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ",
- locked ? '*' : ' ', hostname,
- pintvl(curtime() - mcib.mci_lastuse, true));
- if (mcib.mci_rstatus != NULL)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width,
- mcib.mci_rstatus);
- else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Deferred: %.*s\n", width - 10,
- sm_errstring(mcib.mci_errno));
- else if (mcib.mci_exitstat != 0)
- {
- char *exmsg = sm_sysexmsg(mcib.mci_exitstat);
-
- if (exmsg == NULL)
- {
- char buf[80];
-
- (void) sm_snprintf(buf, sizeof(buf),
- "Unknown mailer error %d",
- mcib.mci_exitstat);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n",
- width, buf);
- }
- else
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n",
- width, &exmsg[5]);
- }
- else if (mcib.mci_errno == 0)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n");
- else
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n",
- width - 4, sm_errstring(mcib.mci_errno));
-
- return 0;
-}
-/*
-** MCI_PURGE_PERSISTENT -- Remove a persistence status file.
-**
-** Parameters:
-** pathname -- path to the status file.
-** hostname -- name of host corresponding to that file.
-** NULL if this is a directory (domain).
-**
-** Returns:
-** 0 -- ok
-** 1 -- file not deleted (too young, incorrect format)
-** < 0 -- some error occurred
-*/
-
-int
-mci_purge_persistent(pathname, hostname)
- char *pathname;
- char *hostname;
-{
- struct stat statbuf;
- char *end = pathname + strlen(pathname) - 1;
- int ret;
-
- if (tTd(56, 1))
- sm_dprintf("mci_purge_persistent: purging %s\n", pathname);
-
- ret = stat(pathname, &statbuf);
- if (ret < 0)
- {
- if (tTd(56, 2))
- sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n",
- pathname, sm_errstring(errno));
- return ret;
- }
- if (curtime() - statbuf.st_mtime <= MciInfoTimeout)
- return 1;
- if (hostname != NULL)
- {
- /* remove the file */
- ret = unlink(pathname);
- if (ret < 0)
- {
- if (LogLevel > 8)
- sm_syslog(LOG_ERR, NOQID,
- "mci_purge_persistent: failed to unlink %s: %s",
- pathname, sm_errstring(errno));
- if (tTd(56, 2))
- sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n",
- pathname, sm_errstring(errno));
- return ret;
- }
- }
- else
- {
- /* remove the directory */
- if (*end != '.')
- return 1;
-
- if (tTd(56, 1))
- sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname);
-
- ret = rmdir(pathname);
- if (ret < 0)
- {
- if (tTd(56, 2))
- sm_dprintf("mci_purge_persistent: rmdir %s: %s\n",
- pathname, sm_errstring(errno));
- return ret;
- }
- }
-
- return 0;
-}
-/*
-** MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname
-**
-** Given `host', convert from a.b.c to $HostStatDir/c./b./a,
-** putting the result into `path'. if `createflag' is set, intervening
-** directories will be created as needed.
-**
-** Parameters:
-** host -- host name to convert from.
-** path -- place to store result.
-** pathlen -- length of path buffer.
-** createflag -- if set, create intervening directories as
-** needed.
-**
-** Returns:
-** 0 -- success
-** -1 -- failure
-*/
-
-static int
-mci_generate_persistent_path(host, path, pathlen, createflag)
- const char *host;
- char *path;
- int pathlen;
- bool createflag;
-{
- char *elem, *p, *x, ch;
- int ret = 0;
- int len;
- char t_host[MAXHOSTNAMELEN];
-#if NETINET6
- struct in6_addr in6_addr;
-#endif /* NETINET6 */
-
- /*
- ** Rationality check the arguments.
- */
-
- if (host == NULL)
- {
- syserr("mci_generate_persistent_path: null host");
- return -1;
- }
- if (path == NULL)
- {
- syserr("mci_generate_persistent_path: null path");
- return -1;
- }
-
- if (tTd(56, 80))
- sm_dprintf("mci_generate_persistent_path(%s): ", host);
-
- if (*host == '\0' || *host == '.')
- return -1;
-
- /* make certain this is not a bracketed host number */
- if (strlen(host) > sizeof(t_host) - 1)
- return -1;
- if (host[0] == '[')
- (void) sm_strlcpy(t_host, host + 1, sizeof(t_host));
- else
- (void) sm_strlcpy(t_host, host, sizeof(t_host));
-
- /*
- ** Delete any trailing dots from the hostname.
- ** Leave 'elem' pointing at the \0.
- */
-
- elem = t_host + strlen(t_host);
- while (elem > t_host &&
- (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']')))
- *--elem = '\0';
-
- /* check for bogus bracketed address */
- if (host[0] == '[')
- {
- bool good = false;
-# if NETINET6
- if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1)
- good = true;
-# endif /* NETINET6 */
-# if NETINET
- if (inet_addr(t_host) != INADDR_NONE)
- good = true;
-# endif /* NETINET */
- if (!good)
- return -1;
- }
-
- /* check for what will be the final length of the path */
- len = strlen(HostStatDir) + 2;
- for (p = (char *) t_host; *p != '\0'; p++)
- {
- if (*p == '.')
- len++;
- len++;
- if (p[0] == '.' && p[1] == '.')
- return -1;
- }
- if (len > pathlen || len < 1)
- return -1;
- (void) sm_strlcpy(path, HostStatDir, pathlen);
- p = path + strlen(path);
- while (elem > t_host)
- {
- if (!path_is_dir(path, createflag))
- {
- ret = -1;
- break;
- }
- elem--;
- while (elem >= t_host && *elem != '.')
- elem--;
- *p++ = '/';
- x = elem + 1;
- while ((ch = *x++) != '\0' && ch != '.')
- {
- if (isascii(ch) && isupper(ch))
- ch = tolower(ch);
- if (ch == '/')
- ch = ':'; /* / -> : */
- *p++ = ch;
- }
- if (elem >= t_host)
- *p++ = '.';
- *p = '\0';
- }
- if (tTd(56, 80))
- {
- if (ret < 0)
- sm_dprintf("FAILURE %d\n", ret);
- else
- sm_dprintf("SUCCESS %s\n", path);
- }
- return ret;
-}
diff --git a/contrib/sendmail/src/milter.c b/contrib/sendmail/src/milter.c
deleted file mode 100644
index 816c7bf..0000000
--- a/contrib/sendmail/src/milter.c
+++ /dev/null
@@ -1,4736 +0,0 @@
-/*
- * Copyright (c) 1999-2006 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: milter.c,v 8.269 2007/06/06 17:26:12 ca Exp $")
-
-#if MILTER
-# include <sm/sendmail.h>
-# include <libmilter/mfapi.h>
-# include <libmilter/mfdef.h>
-
-# include <errno.h>
-# include <sm/time.h>
-# include <sys/uio.h>
-
-# if NETINET || NETINET6
-# include <arpa/inet.h>
-# if MILTER_NO_NAGLE
-# include <netinet/tcp.h>
-# endif /* MILTER_NO_NAGLE */
-# endif /* NETINET || NETINET6 */
-
-# include <sm/fdset.h>
-
-static void milter_connect_timeout __P((int));
-static void milter_error __P((struct milter *, ENVELOPE *));
-static int milter_open __P((struct milter *, bool, ENVELOPE *));
-static void milter_parse_timeouts __P((char *, struct milter *));
-static char *milter_sysread __P((struct milter *, char *, ssize_t, time_t,
- ENVELOPE *, const char *));
-static char *milter_read __P((struct milter *, char *, ssize_t *, time_t,
- ENVELOPE *, const char *));
-static char *milter_write __P((struct milter *, int, char *, ssize_t,
- time_t, ENVELOPE *, const char *));
-static char *milter_send_command __P((struct milter *, int, void *,
- ssize_t, ENVELOPE *, char *, const char *));
-static char *milter_command __P((int, void *, ssize_t, char **,
- ENVELOPE *, char *, const char *, bool));
-static char *milter_body __P((struct milter *, ENVELOPE *, char *));
-static int milter_reopen_df __P((ENVELOPE *));
-static int milter_reset_df __P((ENVELOPE *));
-static void milter_quit_filter __P((struct milter *, ENVELOPE *));
-static void milter_abort_filter __P((struct milter *, ENVELOPE *));
-static void milter_send_macros __P((struct milter *, char **, int,
- ENVELOPE *));
-static int milter_negotiate __P((struct milter *, ENVELOPE *,
- milters_T *));
-static void milter_per_connection_check __P((ENVELOPE *));
-static char *milter_headers __P((struct milter *, ENVELOPE *, char *));
-static void milter_addheader __P((struct milter *, char *, ssize_t,
- ENVELOPE *));
-static void milter_insheader __P((struct milter *, char *, ssize_t,
- ENVELOPE *));
-static void milter_changeheader __P((struct milter *, char *, ssize_t,
- ENVELOPE *));
-static void milter_chgfrom __P((char *, ssize_t, ENVELOPE *));
-static void milter_addrcpt __P((char *, ssize_t, ENVELOPE *));
-static void milter_addrcpt_par __P((char *, ssize_t, ENVELOPE *));
-static void milter_delrcpt __P((char *, ssize_t, ENVELOPE *));
-static int milter_replbody __P((char *, ssize_t, bool, ENVELOPE *));
-static int milter_set_macros __P((char *, char **, char *, int));
-
-
-/* milter states */
-# define SMFS_CLOSED 'C' /* closed for all further actions */
-# define SMFS_OPEN 'O' /* connected to remote milter filter */
-# define SMFS_INMSG 'M' /* currently servicing a message */
-# define SMFS_DONE 'D' /* done with current message */
-# define SMFS_CLOSABLE 'Q' /* done with current connection */
-# define SMFS_ERROR 'E' /* error state */
-# define SMFS_READY 'R' /* ready for action */
-# define SMFS_SKIP 'S' /* skip body */
-
-static char *MilterConnectMacros[MAXFILTERMACROS + 1];
-static char *MilterHeloMacros[MAXFILTERMACROS + 1];
-static char *MilterEnvFromMacros[MAXFILTERMACROS + 1];
-static char *MilterEnvRcptMacros[MAXFILTERMACROS + 1];
-static char *MilterDataMacros[MAXFILTERMACROS + 1];
-static char *MilterEOMMacros[MAXFILTERMACROS + 1];
-static char *MilterEOHMacros[MAXFILTERMACROS + 1];
-static size_t MilterMaxDataSize = MILTER_MAX_DATA_SIZE;
-
-# 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(initial, action) \
- if (!initial && tTd(71, 100)) \
- { \
- if (e->e_quarmsg == NULL) \
- { \
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
- "filter failure"); \
- macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
- e->e_quarmsg); \
- } \
- } \
- else if (tTd(71, 101)) \
- { \
- if (e->e_quarmsg == NULL) \
- { \
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
- "filter failure"); \
- macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
- e->e_quarmsg); \
- } \
- } \
- else if (bitnset(SMF_TEMPFAIL, m->mf_flags)) \
- *state = SMFIR_TEMPFAIL; \
- else if (bitnset(SMF_TEMPDROP, m->mf_flags)) \
- *state = SMFIR_SHUTDOWN; \
- 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) \
- sm_free(response); /* XXX */ \
- response = newstr(default); \
- } \
- else \
- { \
- char *ptr = response; \
- \
- /* Check for unprotected %'s in the string */ \
- while (*ptr != '\0') \
- { \
- if (*ptr == '%' && *++ptr != '%') \
- { \
- sm_free(response); /* XXX */ \
- response = newstr(default); \
- break; \
- } \
- ptr++; \
- } \
- }
-
-# define MILTER_DF_ERROR(msg) \
-{ \
- int save_errno = errno; \
- \
- if (tTd(64, 5)) \
- { \
- sm_dprintf(msg, dfname, sm_errstring(save_errno)); \
- sm_dprintf("\n"); \
- } \
- if (MilterLogLevel > 0) \
- sm_syslog(LOG_ERR, e->e_id, msg, dfname, sm_errstring(save_errno)); \
- if (SuperSafe == SAFE_REALLY) \
- { \
- if (e->e_dfp != NULL) \
- { \
- (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); \
- 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?
-** started -- whether this is part of a previous sequence
-**
-** Assumes 'm' is a milter structure for the current socket.
-*/
-
-# define MILTER_TIMEOUT(routine, secs, write, started, function) \
-{ \
- int ret; \
- int save_errno; \
- fd_set fds; \
- struct timeval tv; \
- \
- if (SM_FD_SETSIZE > 0 && m->mf_sock >= SM_FD_SETSIZE) \
- { \
- if (tTd(64, 5)) \
- sm_dprintf("milter_%s(%s): socket %d is larger than FD_SETSIZE %d\n", \
- (routine), m->mf_name, m->mf_sock, \
- SM_FD_SETSIZE); \
- if (MilterLogLevel > 0) \
- sm_syslog(LOG_ERR, e->e_id, \
- "Milter (%s): socket(%s) %d is larger than FD_SETSIZE %d", \
- m->mf_name, (routine), m->mf_sock, \
- SM_FD_SETSIZE); \
- milter_error(m, e); \
- return NULL; \
- } \
- \
- do \
- { \
- FD_ZERO(&fds); \
- SM_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); \
- } while (ret < 0 && errno == EINTR); \
- \
- switch (ret) \
- { \
- case 0: \
- if (tTd(64, 5)) \
- sm_dprintf("milter_%s(%s): timeout, where=%s\n", \
- (routine), m->mf_name, (function)); \
- if (MilterLogLevel > 0) \
- sm_syslog(LOG_ERR, e->e_id, \
- "Milter (%s): timeout %s data %s, where=%s", \
- m->mf_name, \
- started ? "during" : "before", \
- (routine), (function)); \
- milter_error(m, e); \
- return NULL; \
- \
- case -1: \
- save_errno = errno; \
- if (tTd(64, 5)) \
- sm_dprintf("milter_%s(%s): select: %s\n", (routine), \
- m->mf_name, sm_errstring(save_errno)); \
- if (MilterLogLevel > 0) \
- { \
- sm_syslog(LOG_ERR, e->e_id, \
- "Milter (%s): select(%s): %s", \
- m->mf_name, (routine), \
- sm_errstring(save_errno)); \
- } \
- milter_error(m, e); \
- return NULL; \
- \
- default: \
- if (SM_FD_ISSET(m->mf_sock, &fds)) \
- break; \
- if (tTd(64, 5)) \
- sm_dprintf("milter_%s(%s): socket not ready\n", \
- (routine), m->mf_name); \
- if (MilterLogLevel > 0) \
- { \
- sm_syslog(LOG_ERR, e->e_id, \
- "Milter (%s): socket(%s) not ready", \
- m->mf_name, (routine)); \
- } \
- milter_error(m, e); \
- 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, where)
- struct milter *m;
- char *buf;
- ssize_t sz;
- time_t to;
- ENVELOPE *e;
- const char *where;
-{
- time_t readstart = 0;
- ssize_t len, curl;
- bool started = false;
-
- curl = 0;
-
- if (to > 0)
- readstart = curtime();
-
- for (;;)
- {
- if (to > 0)
- {
- time_t now;
-
- now = curtime();
- if (now - readstart >= to)
- {
- if (tTd(64, 5))
- sm_dprintf("milter_sys_read (%s): timeout %s data read in %s",
- m->mf_name,
- started ? "during" : "before",
- where);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): timeout %s data read in %s",
- m->mf_name,
- started ? "during" : "before",
- where);
- milter_error(m, e);
- return NULL;
- }
- to -= now - readstart;
- readstart = now;
- MILTER_TIMEOUT("read", to, false, started, where);
- }
-
- len = read(m->mf_sock, buf + curl, sz - curl);
-
- if (len < 0)
- {
- int save_errno = errno;
-
- if (tTd(64, 5))
- sm_dprintf("milter_sys_read(%s): read returned %ld: %s\n",
- m->mf_name, (long) len,
- sm_errstring(save_errno));
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): read returned %ld: %s",
- m->mf_name, (long) len,
- sm_errstring(save_errno));
- milter_error(m, e);
- return NULL;
- }
-
- started = true;
- curl += len;
- if (len == 0 || curl >= sz)
- break;
-
- }
-
- if (curl != sz)
- {
- if (tTd(64, 5))
- sm_dprintf("milter_sys_read(%s): cmd read returned %ld, expecting %ld\n",
- m->mf_name, (long) curl, (long) sz);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "milter_sys_read(%s): cmd read returned %ld, expecting %ld",
- m->mf_name, (long) curl, (long) sz);
- milter_error(m, e);
- return NULL;
- }
- return buf;
-}
-
-static char *
-milter_read(m, cmd, rlen, to, e, where)
- struct milter *m;
- char *cmd;
- ssize_t *rlen;
- time_t to;
- ENVELOPE *e;
- const char *where;
-{
- time_t readstart = 0;
- ssize_t expl;
- mi_int32 i;
-# if MILTER_NO_NAGLE && defined(TCP_CORK)
- int cork = 0;
-# endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
- char *buf;
- char data[MILTER_LEN_BYTES + 1];
-
- if (m->mf_sock < 0)
- {
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "milter_read(%s): socket closed, where=%s",
- m->mf_name, where);
- milter_error(m, e);
- return NULL;
- }
-
- *rlen = 0;
- *cmd = '\0';
-
- if (to > 0)
- readstart = curtime();
-
-# if MILTER_NO_NAGLE && defined(TCP_CORK)
- setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
- sizeof(cork));
-# endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
-
- if (milter_sysread(m, data, sizeof(data), to, e, where) == NULL)
- return NULL;
-
-# if MILTER_NO_NAGLE && defined(TCP_CORK)
- cork = 1;
- setsockopt(m->mf_sock, IPPROTO_TCP, TCP_CORK, (char *)&cork,
- sizeof(cork));
-# endif /* MILTER_NO_NAGLE && defined(TCP_CORK) */
-
- /* reset timeout */
- if (to > 0)
- {
- time_t now;
-
- now = curtime();
- if (now - readstart >= to)
- {
- if (tTd(64, 5))
- sm_dprintf("milter_read(%s): timeout before data read, where=%s\n",
- m->mf_name, where);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter read(%s): timeout before data read, where=%s",
- m->mf_name, where);
- milter_error(m, e);
- 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))
- sm_dprintf("milter_read(%s): expecting %ld bytes\n",
- m->mf_name, (long) expl);
-
- if (expl < 0)
- {
- if (tTd(64, 5))
- sm_dprintf("milter_read(%s): read size %ld out of range, where=%s\n",
- m->mf_name, (long) expl, where);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "milter_read(%s): read size %ld out of range, where=%s",
- m->mf_name, (long) expl, where);
- milter_error(m, e);
- return NULL;
- }
-
- if (expl == 0)
- return NULL;
-
- buf = (char *) xalloc(expl);
-
- if (milter_sysread(m, buf, expl, to, e, where) == NULL)
- {
- sm_free(buf); /* XXX */
- return NULL;
- }
-
- if (tTd(64, 50))
- sm_dprintf("milter_read(%s): Returning %*s\n",
- m->mf_name, (int) expl, buf);
- *rlen = expl;
- return buf;
-}
-
-/*
-** MILTER_WRITE -- write to a remote milter filter
-**
-** Parameters:
-** 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, where)
- struct milter *m;
- int cmd;
- char *buf;
- ssize_t len;
- time_t to;
- ENVELOPE *e;
- const char *where;
-{
- time_t writestart = (time_t) 0;
- ssize_t sl, i;
- int num_vectors;
- mi_int32 nl;
- char command = (char) cmd;
- char data[MILTER_LEN_BYTES + 1];
- bool started = false;
- struct iovec vector[2];
-
- /*
- ** At most two buffers will be written, though
- ** only one may actually be used (see num_vectors).
- ** The first is the size/command and the second is the command data.
- */
-
- if (len < 0 || len > MilterMaxDataSize)
- {
- if (tTd(64, 5))
- sm_dprintf("milter_write(%s): length %ld out of range\n",
- m->mf_name, (long) len);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "milter_write(%s): length %ld out of range",
- m->mf_name, (long) len);
- milter_error(m, e);
- return NULL;
- }
- if (m->mf_sock < 0)
- {
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "milter_write(%s): socket closed",
- m->mf_name);
- milter_error(m, e);
- return NULL;
- }
-
- if (tTd(64, 20))
- sm_dprintf("milter_write(%s): cmd %c, len %ld\n",
- m->mf_name, command, (long) len);
-
- nl = htonl(len + 1); /* add 1 for the command char */
- (void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
- data[MILTER_LEN_BYTES] = command;
- sl = MILTER_LEN_BYTES + 1;
-
- /* set up the vector for the size / command */
- vector[0].iov_base = (void *) data;
- vector[0].iov_len = sl;
-
- /*
- ** Determine if there is command data. If so, there will be two
- ** vectors. If not, there will be only one. The vectors are set
- ** up here and 'num_vectors' and 'sl' are set appropriately.
- */
-
- /* NOTE: len<0 has already been checked for. Pedantic */
- if (len <= 0 || buf == NULL)
- {
- /* There is no command data -- only a size / command data */
- num_vectors = 1;
- }
- else
- {
- /*
- ** There is both size / command and command data.
- ** Set up the vector for the command data.
- */
-
- num_vectors = 2;
- sl += len;
- vector[1].iov_base = (void *) buf;
- vector[1].iov_len = len;
-
- if (tTd(64, 50))
- sm_dprintf("milter_write(%s): Sending %*s\n",
- m->mf_name, (int) len, buf);
- }
-
- if (to > 0)
- {
- writestart = curtime();
- MILTER_TIMEOUT("write", to, true, started, where);
- }
-
- /* write the vector(s) */
- i = writev(m->mf_sock, vector, num_vectors);
- if (i != sl)
- {
- int save_errno = errno;
-
- if (tTd(64, 5))
- sm_dprintf("milter_write(%s): write(%c) returned %ld, expected %ld: %s\n",
- m->mf_name, command, (long) i, (long) sl,
- sm_errstring(save_errno));
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): write(%c) returned %ld, expected %ld: %s",
- m->mf_name, command, (long) i, (long) sl,
- sm_errstring(save_errno));
- milter_error(m, e);
- 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 successful && !parseonly,
-** 0 upon parse success if parseonly,
-** -1 otherwise.
-*/
-
-static jmp_buf MilterConnectTimeout;
-
-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))
- sm_dprintf("X%s: empty or missing socket information\n",
- m->mf_name);
- if (parseonly)
- syserr("X%s: empty or missing socket information",
- m->mf_name);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): empty or missing socket information",
- m->mf_name);
- milter_error(m, e);
- return -1;
- }
-
- /* protocol:filename or protocol:port@host */
- memset(&addr, '\0', sizeof(addr));
- 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 */
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): no valid socket protocols available",
- m->mf_name);
- milter_error(m, e);
- return -1;
-# endif /* NETINET6 */
-# endif /* NETINET */
-# endif /* NETUNIX */
- }
-# if NETUNIX
- else if (sm_strcasecmp(p, "unix") == 0 ||
- sm_strcasecmp(p, "local") == 0)
- addr.sa.sa_family = AF_UNIX;
-# endif /* NETUNIX */
-# if NETINET
- else if (sm_strcasecmp(p, "inet") == 0)
- addr.sa.sa_family = AF_INET;
-# endif /* NETINET */
-# if NETINET6
- else if (sm_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))
- sm_dprintf("X%s: unknown socket type %s\n",
- m->mf_name, p);
- if (parseonly)
- syserr("X%s: unknown socket type %s",
- m->mf_name, p);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): unknown socket type %s",
- m->mf_name, p);
- milter_error(m, e);
- 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))
- sm_dprintf("X%s: local socket name %s too long\n",
- m->mf_name, colon);
- errno = EINVAL;
- if (parseonly)
- syserr("X%s: local socket name %s too long",
- m->mf_name, colon);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): local socket name %s too long",
- m->mf_name, colon);
- milter_error(m, e);
- 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)
- (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
- "WARNING: X%s: local socket name %s missing\n",
- m->mf_name, colon);
- }
- else if (errno != 0)
- {
- /* if not safe, don't create */
- save_errno = errno;
- if (tTd(64, 5))
- sm_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 (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): local socket name %s unsafe",
- m->mf_name, colon);
- milter_error(m, e);
- return -1;
- }
-
- (void) sm_strlcpy(addr.sunix.sun_path, colon,
- sizeof(addr.sunix.sun_path));
- addrlen = sizeof(struct sockaddr_un);
- }
- else
-# endif /* NETUNIX */
-# if NETINET || NETINET6
- if (false
-# if NETINET
- || addr.sa.sa_family == AF_INET
-# endif /* NETINET */
-# if NETINET6
- || addr.sa.sa_family == AF_INET6
-# endif /* NETINET6 */
- )
- {
- unsigned short port;
-
- /* Parse port@host */
- at = strchr(colon, '@');
- if (at == NULL)
- {
- if (tTd(64, 5))
- sm_dprintf("X%s: bad address %s (expected port@host)\n",
- m->mf_name, colon);
- if (parseonly)
- syserr("X%s: bad address %s (expected port@host)",
- m->mf_name, colon);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): bad address %s (expected port@host)",
- m->mf_name, colon);
- milter_error(m, e);
- return -1;
- }
- *at = '\0';
- if (isascii(*colon) && isdigit(*colon))
- port = htons((unsigned short) atoi(colon));
- else
- {
-# ifdef NO_GETSERVBYNAME
- if (tTd(64, 5))
- sm_dprintf("X%s: invalid port number %s\n",
- m->mf_name, colon);
- if (parseonly)
- syserr("X%s: invalid port number %s",
- m->mf_name, colon);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): invalid port number %s",
- m->mf_name, colon);
- milter_error(m, e);
- return -1;
-# else /* NO_GETSERVBYNAME */
- struct servent *sp;
-
- sp = getservbyname(colon, "tcp");
- if (sp == NULL)
- {
- save_errno = errno;
- if (tTd(64, 5))
- sm_dprintf("X%s: unknown port name %s\n",
- m->mf_name, colon);
- errno = save_errno;
- if (parseonly)
- syserr("X%s: unknown port name %s",
- m->mf_name, colon);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): unknown port name %s",
- m->mf_name, colon);
- milter_error(m, e);
- 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 &&
- anynet_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))
- sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
- m->mf_name, at);
- if (parseonly)
- syserr("X%s: Invalid numeric domain spec \"%s\"",
- m->mf_name, at);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): Invalid numeric domain spec \"%s\"",
- m->mf_name, at);
- milter_error(m, e);
- return -1;
- }
- }
- else
- {
- if (tTd(64, 5))
- sm_dprintf("X%s: Invalid numeric domain spec \"%s\"\n",
- m->mf_name, at);
- if (parseonly)
- syserr("X%s: Invalid numeric domain spec \"%s\"",
- m->mf_name, at);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): Invalid numeric domain spec \"%s\"",
- m->mf_name, at);
- milter_error(m, e);
- return -1;
- }
- }
- else
- {
- hp = sm_gethostbyname(at, addr.sa.sa_family);
- if (hp == NULL)
- {
- save_errno = errno;
- if (tTd(64, 5))
- sm_dprintf("X%s: Unknown host name %s\n",
- m->mf_name, at);
- errno = save_errno;
- if (parseonly)
- syserr("X%s: Unknown host name %s",
- m->mf_name, at);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): Unknown host name %s",
- m->mf_name, at);
- milter_error(m, e);
- 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))
- sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
- m->mf_name, at,
- hp->h_addrtype);
- if (parseonly)
- syserr("X%s: Unknown protocol for %s (%d)",
- m->mf_name, at, hp->h_addrtype);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): Unknown protocol for %s (%d)",
- m->mf_name, at,
- hp->h_addrtype);
- milter_error(m, e);
-# if NETINET6
- freehostent(hp);
-# endif /* NETINET6 */
- return -1;
- }
- }
- }
- else
-# endif /* NETINET || NETINET6 */
- {
- if (tTd(64, 5))
- sm_dprintf("X%s: unknown socket protocol\n",
- m->mf_name);
- if (parseonly)
- syserr("X%s: unknown socket protocol", m->mf_name);
- else if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): unknown socket protocol",
- m->mf_name);
- milter_error(m, e);
- return -1;
- }
-
- /* just parsing through? */
- if (parseonly)
- {
- m->mf_state = SMFS_READY;
-# if NETINET6
- if (hp != NULL)
- freehostent(hp);
-# endif /* NETINET6 */
- return 0;
- }
-
- /* sanity check */
- if (m->mf_state != SMFS_READY &&
- m->mf_state != SMFS_CLOSED)
- {
- /* shouldn't happen */
- if (tTd(64, 1))
- sm_dprintf("Milter (%s): Trying to open filter in state %c\n",
- m->mf_name, (char) m->mf_state);
- milter_error(m, e);
-# if NETINET6
- if (hp != NULL)
- freehostent(hp);
-# endif /* NETINET6 */
- 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))
- sm_dprintf("Milter (%s): error creating socket: %s\n",
- m->mf_name,
- sm_errstring(save_errno));
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): error creating socket: %s",
- m->mf_name, sm_errstring(save_errno));
- milter_error(m, e);
-# if NETINET6
- if (hp != NULL)
- freehostent(hp);
-# endif /* NETINET6 */
- return -1;
- }
-
- if (setjmp(MilterConnectTimeout) == 0)
- {
- SM_EVENT *ev = NULL;
- int i;
-
- if (m->mf_timeout[SMFTO_CONNECT] > 0)
- ev = sm_setevent(m->mf_timeout[SMFTO_CONNECT],
- milter_connect_timeout, 0);
-
- i = connect(sock, (struct sockaddr *) &addr, addrlen);
- save_errno = errno;
- if (ev != NULL)
- sm_clrevent(ev);
- errno = save_errno;
- if (i >= 0)
- break;
- }
-
- /* couldn't connect.... try next address */
- save_errno = errno;
- p = CurHostName;
- CurHostName = at;
- if (tTd(64, 5))
- sm_dprintf("milter_open (%s): open %s failed: %s\n",
- m->mf_name, at, sm_errstring(save_errno));
- if (MilterLogLevel > 13)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter (%s): open %s failed: %s",
- m->mf_name, at, sm_errstring(save_errno));
- CurHostName = p;
- (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))
- sm_dprintf("X%s: Unknown protocol for %s (%d)\n",
- m->mf_name, at,
- hp->h_addrtype);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): Unknown protocol for %s (%d)",
- m->mf_name, at,
- hp->h_addrtype);
- milter_error(m, e);
-# if NETINET6
- freehostent(hp);
-# endif /* NETINET6 */
- return -1;
- }
- continue;
- }
- p = CurHostName;
- CurHostName = at;
- if (tTd(64, 5))
- sm_dprintf("X%s: error connecting to filter: %s\n",
- m->mf_name, sm_errstring(save_errno));
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): error connecting to filter: %s",
- m->mf_name, sm_errstring(save_errno));
- CurHostName = p;
- milter_error(m, e);
-# if NETINET6
- if (hp != NULL)
- freehostent(hp);
-# endif /* NETINET6 */
- return -1;
- }
- m->mf_state = SMFS_OPEN;
-# if NETINET6
- if (hp != NULL)
- {
- freehostent(hp);
- hp = NULL;
- }
-# endif /* NETINET6 */
-# if MILTER_NO_NAGLE && !defined(TCP_CORK)
- {
- int nodelay = 1;
-
- setsockopt(m->mf_sock, IPPROTO_TCP, TCP_NODELAY,
- (char *)&nodelay, sizeof(nodelay));
- }
-# endif /* MILTER_NO_NAGLE && !defined(TCP_CORK) */
- return sock;
-}
-
-static void
-milter_connect_timeout(ignore)
- int ignore;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(MilterConnectTimeout, 1);
-}
-
-/*
-** MILTER_SETUP -- setup structure for a mail filter
-**
-** Parameters:
-** line -- the options line.
-**
-** Returns:
-** none
-*/
-
-void
-milter_setup(line)
- char *line;
-{
- char fcode;
- char *p;
- struct milter *m;
- STAB *s;
-
- /* collect the filter name */
- for (p = line;
- *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
- p++)
- continue;
- if (*p != '\0')
- *p++ = '\0';
- if (line[0] == '\0')
- {
- syserr("name required for mail filter");
- return;
- }
- m = (struct milter *) xalloc(sizeof(*m));
- memset((char *) m, '\0', sizeof(*m));
- m->mf_name = newstr(line);
- m->mf_state = SMFS_READY;
- m->mf_sock = -1;
- m->mf_timeout[SMFTO_CONNECT] = (time_t) 300;
- m->mf_timeout[SMFTO_WRITE] = (time_t) 10;
- m->mf_timeout[SMFTO_READ] = (time_t) 10;
- m->mf_timeout[SMFTO_EOM] = (time_t) 300;
-#if _FFR_MILTER_CHECK
- m->mf_mta_prot_version = SMFI_PROT_VERSION;
- m->mf_mta_prot_flags = SMFI_CURR_PROT;
- m->mf_mta_actions = SMFI_CURR_ACTS;
-#endif /* _FFR_MILTER_CHECK */
-
- /* now scan through and assign info from the fields */
- while (*p != '\0')
- {
- char *delimptr;
-
- while (*p != '\0' &&
- (*p == ',' || (isascii(*p) && isspace(*p))))
- p++;
-
- /* p now points to field code */
- fcode = *p;
- while (*p != '\0' && *p != '=' && *p != ',')
- p++;
- if (*p++ != '=')
- {
- syserr("X%s: `=' expected", m->mf_name);
- return;
- }
- while (isascii(*p) && isspace(*p))
- p++;
-
- /* p now points to the field body */
- p = munchstring(p, &delimptr, ',');
-
- /* install the field into the filter struct */
- switch (fcode)
- {
- case 'S': /* socket */
- if (p == NULL)
- m->mf_conn = NULL;
- else
- m->mf_conn = newstr(p);
- break;
-
- case 'F': /* Milter flags configured on MTA */
- for (; *p != '\0'; p++)
- {
- if (!(isascii(*p) && isspace(*p)))
- setbitn(bitidx(*p), m->mf_flags);
- }
- break;
-
- case 'T': /* timeouts */
- milter_parse_timeouts(p, m);
- break;
-
-#if _FFR_MILTER_CHECK
- case 'a':
- m->mf_mta_actions = strtoul(p, NULL, 0);
- break;
- case 'f':
- m->mf_mta_prot_flags = strtoul(p, NULL, 0);
- break;
- case 'v':
- m->mf_mta_prot_version = strtoul(p, NULL, 0);
- break;
-#endif /* _FFR_MILTER_CHECK */
-
- default:
- syserr("X%s: unknown filter equate %c=",
- m->mf_name, fcode);
- break;
- }
- p = delimptr;
- }
-
- /* early check for errors */
- (void) milter_open(m, true, CurEnv);
-
- /* enter the filter into the symbol table */
- s = stab(m->mf_name, ST_MILTER, ST_ENTER);
- if (s->s_milter != NULL)
- syserr("X%s: duplicate filter definition", m->mf_name);
- else
- s->s_milter = m;
-}
-
-/*
-** MILTER_CONFIG -- parse option list into an array and check config
-**
-** 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_config(spec, list, max)
- char *spec;
- struct milter **list;
- int max;
-{
- int numitems = 0;
- 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;
-
- /* if not set, set to LogLevel */
- if (MilterLogLevel == -1)
- MilterLogLevel = LogLevel;
-}
-
-/*
-** 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;
- int tcode;
- 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, ';');
- tcode = -1;
-
- /* install the field into the filter struct */
- switch (fcode)
- {
- case 'C':
- tcode = SMFTO_CONNECT;
- break;
-
- case 'S':
- tcode = SMFTO_WRITE;
- break;
-
- case 'R':
- tcode = SMFTO_READ;
- break;
-
- case 'E':
- tcode = SMFTO_EOM;
- break;
-
- default:
- if (tTd(64, 5))
- sm_dprintf("X%s: %c unknown\n",
- m->mf_name, fcode);
- syserr("X%s: unknown filter timeout %c",
- m->mf_name, fcode);
- break;
- }
- if (tcode >= 0)
- {
- m->mf_timeout[tcode] = convtime(p, 's');
- if (tTd(64, 5))
- sm_dprintf("X%s: %c=%ld\n",
- m->mf_name, fcode,
- (u_long) m->mf_timeout[tcode]);
- }
- p = delimptr;
- }
-}
-
-/*
-** MILTER_SET_MACROS -- set milter macros
-**
-** Parameters:
-** name -- name of milter.
-** macros -- where to store macros.
-** val -- the value of the option.
-** nummac -- current number of macros
-**
-** Returns:
-** new number of macros
-*/
-
-static int
-milter_set_macros(name, macros, val, nummac)
- char *name;
- char **macros;
- char *val;
- int nummac;
-{
- char *p;
-
- 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;
- return -1;
- }
- macros[nummac++] = macro;
- }
- macros[nummac] = NULL;
- return nummac;
-}
-
-/*
-** 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 */
- unsigned char mo_code; /* code for option */
-} MilterOptTab[] =
-{
-# define MO_MACROS_CONNECT SMFIM_CONNECT
- { "macros.connect", MO_MACROS_CONNECT },
-# define MO_MACROS_HELO SMFIM_HELO
- { "macros.helo", MO_MACROS_HELO },
-# define MO_MACROS_ENVFROM SMFIM_ENVFROM
- { "macros.envfrom", MO_MACROS_ENVFROM },
-# define MO_MACROS_ENVRCPT SMFIM_ENVRCPT
- { "macros.envrcpt", MO_MACROS_ENVRCPT },
-# define MO_MACROS_DATA SMFIM_DATA
- { "macros.data", MO_MACROS_DATA },
-# define MO_MACROS_EOM SMFIM_EOM
- { "macros.eom", MO_MACROS_EOM },
-# define MO_MACROS_EOH SMFIM_EOH
- { "macros.eoh", MO_MACROS_EOH },
-
-# define MO_LOGLEVEL 0x07
- { "loglevel", MO_LOGLEVEL },
-# if _FFR_MAXDATASIZE
-# define MO_MAXDATASIZE 0x08
- { "maxdatasize", MO_MAXDATASIZE },
-# endif /* _FFR_MAXDATASIZE */
- { NULL, (unsigned char)-1 },
-};
-
-void
-milter_set_option(name, val, sticky)
- char *name;
- char *val;
- bool sticky;
-{
- int nummac, r;
- struct milteropt *mo;
- char **macros = NULL;
-
- nummac = 0;
- if (tTd(37, 2) || tTd(64, 5))
- sm_dprintf("milter_set_option(%s = %s)", name, val);
-
- if (name == NULL)
- {
- syserr("milter_set_option: invalid Milter option, must specify suboption");
- return;
- }
-
- for (mo = MilterOptTab; mo->mo_name != NULL; mo++)
- {
- if (sm_strcasecmp(mo->mo_name, name) == 0)
- break;
- }
-
- if (mo->mo_name == NULL)
- {
- syserr("milter_set_option: invalid Milter option %s", name);
- return;
- }
-
- /*
- ** See if this option is preset for us.
- */
-
- if (!sticky && bitnset(mo->mo_code, StickyMilterOpt))
- {
- if (tTd(37, 2) || tTd(64,5))
- sm_dprintf(" (ignored)\n");
- return;
- }
-
- if (tTd(37, 2) || tTd(64,5))
- sm_dprintf("\n");
-
- switch (mo->mo_code)
- {
- case MO_LOGLEVEL:
- MilterLogLevel = atoi(val);
- break;
-
-#if _FFR_MAXDATASIZE
- case MO_MAXDATASIZE:
- MilterMaxDataSize = (size_t)atol(val);
- break;
-#endif /* _FFR_MAXDATASIZE */
-
- 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;
- /* FALLTHROUGH */
-
- case MO_MACROS_EOH:
- if (macros == NULL)
- macros = MilterEOHMacros;
- /* FALLTHROUGH */
-
- case MO_MACROS_EOM:
- if (macros == NULL)
- macros = MilterEOMMacros;
- /* FALLTHROUGH */
-
- case MO_MACROS_DATA:
- if (macros == NULL)
- macros = MilterDataMacros;
-
- r = milter_set_macros(name, macros, val, nummac);
- if (r >= 0)
- nummac = r;
- 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 data 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) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
-
- /*
- ** In SuperSafe == SAFE_REALLY mode, e->e_dfp is a read-only FP so
- ** close and reopen writable (later close and reopen
- ** read only again).
- **
- ** In SuperSafe != SAFE_REALLY mode, e->e_dfp still points at the
- ** buffered file I/O descriptor, still open for writing so there
- ** isn't any work to do here (except checking for consistency).
- */
-
- if (SuperSafe == SAFE_REALLY)
- {
- /* close read-only data file */
- if (bitset(EF_HAS_DF, e->e_flags) && e->e_dfp != NULL)
- {
- (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
- e->e_flags &= ~EF_HAS_DF;
- }
-
- /* open writable */
- if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
- SM_IO_RDWR_B, NULL)) == NULL)
- {
- MILTER_DF_ERROR("milter_reopen_df: sm_io_open %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 data 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) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER), sizeof(dfname));
-
- if (sm_io_flush(e->e_dfp, SM_TIME_DEFAULT) != 0 ||
- sm_io_error(e->e_dfp))
- {
- MILTER_DF_ERROR("milter_reset_df: error writing/flushing %s: %s");
- return -1;
- }
- else if (SuperSafe != SAFE_REALLY)
- {
- /* skip next few clauses */
- /* EMPTY */
- }
- else if ((afd = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL)) >= 0
- && fsync(afd) < 0)
- {
- MILTER_DF_ERROR("milter_reset_df: error sync'ing %s: %s");
- return -1;
- }
- else if (sm_io_close(e->e_dfp, SM_TIME_DEFAULT) < 0)
- {
- MILTER_DF_ERROR("milter_reset_df: error closing %s: %s");
- return -1;
- }
- else if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
- SM_IO_RDONLY_B, NULL)) == NULL)
- {
- MILTER_DF_ERROR("milter_reset_df: error reopening %s: %s");
- return -1;
- }
- else
- e->e_flags |= EF_HAS_DF;
- return 0;
-}
-
-/*
-** 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))
- sm_dprintf("milter_quit_filter(%s)\n", m->mf_name);
- if (MilterLogLevel > 18)
- sm_syslog(LOG_INFO, e->e_id, "Milter (%s): quit filter",
- m->mf_name);
-
- /* Never replace error state */
- if (m->mf_state == SMFS_ERROR)
- 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, "quit_filter");
- if (m->mf_sock >= 0)
- {
- (void) close(m->mf_sock);
- m->mf_sock = -1;
- }
- if (m->mf_state != SMFS_ERROR)
- m->mf_state = SMFS_CLOSED;
-}
-
-/*
-** 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))
- sm_dprintf("milter_abort_filter(%s)\n", m->mf_name);
- if (MilterLogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id, "Milter (%s): abort filter",
- m->mf_name);
-
- if (m->mf_sock < 0 ||
- m->mf_state != SMFS_INMSG)
- return;
-
- (void) milter_write(m, SMFIC_ABORT, (char *) NULL, 0,
- m->mf_timeout[SMFTO_WRITE], e, "abort_filter");
- 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;
- int cmd;
- ENVELOPE *e;
-{
- int i;
- int mid;
- char command = (char) cmd;
- char *v;
- char *buf, *bp;
- char exp[MAXLINE];
- 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]);
- if (mid == 0)
- continue;
- v = macvalue(mid, e);
- if (v == NULL)
- continue;
- expand(v, exp, sizeof(exp), e);
- s += strlen(macros[i]) + 1 + strlen(exp) + 1;
- }
-
- if (s < 0)
- return;
-
- buf = (char *) xalloc(s);
- bp = buf;
- *bp++ = command;
- for (i = 0; macros[i] != NULL; i++)
- {
- mid = macid(macros[i]);
- if (mid == 0)
- continue;
- v = macvalue(mid, e);
- if (v == NULL)
- continue;
- expand(v, exp, sizeof(exp), e);
-
- if (tTd(64, 10))
- sm_dprintf("milter_send_macros(%s, %c): %s=%s\n",
- m->mf_name, command, macros[i], exp);
-
- (void) sm_strlcpy(bp, macros[i], s - (bp - buf));
- bp += strlen(bp) + 1;
- (void) sm_strlcpy(bp, exp, s - (bp - buf));
- bp += strlen(bp) + 1;
- }
- (void) milter_write(m, SMFIC_MACRO, buf, s,
- m->mf_timeout[SMFTO_WRITE], e, "send_macros");
- sm_free(buf);
-}
-
-/*
-** MILTER_SEND_COMMAND -- send a command and return the response for a filter
-**
-** Parameters:
-** m -- current milter filter
-** cmd -- 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, cmd, data, sz, e, state, where)
- struct milter *m;
- int cmd;
- void *data;
- ssize_t sz;
- ENVELOPE *e;
- char *state;
- const char *where;
-{
- char rcmd;
- ssize_t rlen;
- unsigned long skipflag;
- unsigned long norespflag = 0;
- char command = (char) cmd;
- char *action;
- char *defresponse;
- char *response;
-
- if (tTd(64, 10))
- sm_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;
- norespflag = SMFIP_NR_CONN;
- action = "connect";
- defresponse = "554 Command rejected";
- break;
-
- case SMFIC_HELO:
- skipflag = SMFIP_NOHELO;
- norespflag = SMFIP_NR_HELO;
- action = "helo";
- defresponse = "550 Command rejected";
- break;
-
- case SMFIC_MAIL:
- skipflag = SMFIP_NOMAIL;
- norespflag = SMFIP_NR_MAIL;
- action = "mail";
- defresponse = "550 5.7.1 Command rejected";
- break;
-
- case SMFIC_RCPT:
- skipflag = SMFIP_NORCPT;
- norespflag = SMFIP_NR_RCPT;
- action = "rcpt";
- defresponse = "550 5.7.1 Command rejected";
- break;
-
- case SMFIC_HEADER:
- skipflag = SMFIP_NOHDRS;
- norespflag = SMFIP_NR_HDR;
- action = "header";
- defresponse = "550 5.7.1 Command rejected";
- break;
-
- case SMFIC_BODY:
- skipflag = SMFIP_NOBODY;
- norespflag = SMFIP_NR_BODY;
- action = "body";
- defresponse = "554 5.7.1 Command rejected";
- break;
-
- case SMFIC_EOH:
- skipflag = SMFIP_NOEOH;
- norespflag = SMFIP_NR_EOH;
- action = "eoh";
- defresponse = "550 5.7.1 Command rejected";
- break;
-
- case SMFIC_UNKNOWN:
- skipflag = SMFIP_NOUNKNOWN;
- norespflag = SMFIP_NR_UNKN;
- action = "unknown";
- defresponse = "550 5.7.1 Command rejected";
- break;
-
- case SMFIC_DATA:
- skipflag = SMFIP_NODATA;
- norespflag = SMFIP_NR_DATA;
- action = "data";
- 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;
- action = "default";
- defresponse = "550 5.7.1 Command rejected";
- break;
- }
-
- if (tTd(64, 10))
- sm_dprintf("milter_send_command(%s): skip=%lx, pflags=%x\n",
- m->mf_name, skipflag, m->mf_pflags);
-
- /* check if filter wants this command */
- if (skipflag != 0 && bitset(skipflag, m->mf_pflags))
- return NULL;
-
- /* send the command to the filter */
- (void) milter_write(m, command, data, sz,
- m->mf_timeout[SMFTO_WRITE], e, where);
- if (m->mf_state == SMFS_ERROR)
- {
- MILTER_CHECK_ERROR(false, return NULL);
- return NULL;
- }
-
- /* check if filter sends response to this command */
- if (norespflag != 0 && bitset(norespflag, m->mf_pflags))
- return NULL;
-
- /* get the response from the filter */
- response = milter_read(m, &rcmd, &rlen,
- m->mf_timeout[SMFTO_READ], e, where);
- if (m->mf_state == SMFS_ERROR)
- {
- MILTER_CHECK_ERROR(false, return NULL);
- return NULL;
- }
-
- if (tTd(64, 10))
- sm_dprintf("milter_send_command(%s): returned %c\n",
- m->mf_name, (char) rcmd);
-
- switch (rcmd)
- {
- case SMFIR_REPLYCODE:
- MILTER_CHECK_REPLYCODE(defresponse);
- if (MilterLogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "milter=%s, action=%s, reject=%s",
- m->mf_name, action, response);
- *state = rcmd;
- break;
-
- case SMFIR_REJECT:
- if (MilterLogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "milter=%s, action=%s, reject",
- m->mf_name, action);
- *state = rcmd;
- break;
-
- case SMFIR_DISCARD:
- if (MilterLogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "milter=%s, action=%s, discard",
- m->mf_name, action);
- *state = rcmd;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "milter=%s, action=%s, tempfail",
- m->mf_name, action);
- *state = rcmd;
- break;
-
- case SMFIR_ACCEPT:
- /* this filter is done with message/connection */
- if (command == SMFIC_HELO ||
- command == SMFIC_CONNECT)
- m->mf_state = SMFS_CLOSABLE;
- else
- m->mf_state = SMFS_DONE;
- if (MilterLogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "milter=%s, action=%s, accepted",
- m->mf_name, action);
- break;
-
- case SMFIR_CONTINUE:
- /* if MAIL command is ok, filter is in message state */
- if (command == SMFIC_MAIL)
- m->mf_state = SMFS_INMSG;
- if (MilterLogLevel > 12)
- sm_syslog(LOG_INFO, e->e_id,
- "milter=%s, action=%s, continue",
- m->mf_name, action);
- break;
-
- case SMFIR_SKIP:
- if (MilterLogLevel > 12)
- sm_syslog(LOG_INFO, e->e_id,
- "milter=%s, action=%s, skip",
- m->mf_name, action);
- m->mf_state = SMFS_SKIP;
- break;
-
- default:
- /* Invalid response to command */
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "milter_send_command(%s): action=%s returned bogus response %c",
- m->mf_name, action, rcmd);
- milter_error(m, e);
- break;
- }
-
- if (*state != SMFIR_REPLYCODE && response != NULL)
- {
- sm_free(response); /* XXX */
- response = NULL;
- }
- return response;
-}
-
-/*
-** MILTER_COMMAND -- send a command and return the response for each filter
-**
-** Parameters:
-** cmd -- 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.
-** where -- description of calling function (logging).
-** cmd_error -- did the SMTP command cause an error?
-**
-** Returns:
-** response string (may be NULL)
-*/
-
-static char *
-milter_command(cmd, data, sz, macros, e, state, where, cmd_error)
- int cmd;
- void *data;
- ssize_t sz;
- char **macros;
- ENVELOPE *e;
- char *state;
- const char *where;
- bool cmd_error;
-{
- int i;
- char command = (char) cmd;
- char *response = NULL;
- time_t tn = 0;
-
- if (tTd(64, 10))
- sm_dprintf("milter_command: cmd %c len %ld\n",
- command, (long) sz);
-
- *state = SMFIR_CONTINUE;
- for (i = 0; InputFilters[i] != NULL; i++)
- {
- struct milter *m = InputFilters[i];
-
- /* previous problem? */
- if (m->mf_state == SMFS_ERROR)
- {
- MILTER_CHECK_ERROR(false, continue);
- break;
- }
-
- /* 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(false, continue);
- break;
- }
- }
-
- if (MilterLogLevel > 21)
- tn = curtime();
-
- /*
- ** send the command if
- ** there is no error
- ** or it's RCPT and the client asked for it:
- ** !cmd_error ||
- ** where == "rcpt" && m->mf_pflags & SMFIP_RCPT_REJ != 0
- ** negate that condition and use continue
- */
-
- if (cmd_error &&
- (strcmp(where, "rcpt") != 0 ||
- (m->mf_pflags & SMFIP_RCPT_REJ) == 0))
- continue;
-
- response = milter_send_command(m, command, data, sz, e, state,
- where);
-
- if (MilterLogLevel > 21)
- {
- /* log the time it took for the command per filter */
- sm_syslog(LOG_INFO, e->e_id,
- "Milter (%s): time command (%c), %d",
- m->mf_name, command, (int) (tn - curtime()));
- }
-
- if (*state != SMFIR_CONTINUE)
- break;
- }
- return response;
-}
-
-static int milter_getsymlist __P((struct milter *, char *, int, int));
-
-static int
-milter_getsymlist(m, buf, rlen, offset)
- struct milter *m;
- char *buf;
- int rlen;
- int offset;
-{
- int i, r, nummac;
- mi_int32 v;
-
- SM_ASSERT(m != NULL);
- SM_ASSERT(buf != NULL);
-
- while (offset + MILTER_LEN_BYTES < rlen)
- {
- size_t len;
- char **macros;
-
- nummac = 0;
- (void) memcpy((char *) &v, buf + offset, MILTER_LEN_BYTES);
- i = ntohl(v);
- if (i < SMFIM_FIRST || i > SMFIM_LAST)
- return -1;
- offset += MILTER_LEN_BYTES;
- macros = NULL;
-
- switch (i)
- {
- 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;
- /* FALLTHROUGH */
-
- case MO_MACROS_EOM:
- if (macros == NULL)
- macros = MilterEOMMacros;
- /* FALLTHROUGH */
-
- case MO_MACROS_EOH:
- if (macros == NULL)
- macros = MilterEOHMacros;
- /* FALLTHROUGH */
-
- case MO_MACROS_DATA:
- if (macros == NULL)
- macros = MilterDataMacros;
-
- len = strlen(buf + offset);
- if (len > 0)
- {
- r = milter_set_macros(m->mf_name, macros,
- buf + offset, nummac);
- if (r >= 0)
- nummac = r;
- }
- break;
-
- default:
- return -1;
- }
- if (len == 0)
- return -1;
- offset += len + 1;
- }
-
- return 0;
-}
-
-/*
-** MILTER_NEGOTIATE -- get version and flags from filter
-**
-** Parameters:
-** m -- milter filter structure.
-** e -- current envelope.
-** milters -- milters structure.
-**
-** Returns:
-** 0 on success, -1 otherwise
-*/
-
-static int
-milter_negotiate(m, e, milters)
- struct milter *m;
- ENVELOPE *e;
- milters_T *milters;
-{
- char rcmd;
- mi_int32 fvers, fflags, pflags;
- mi_int32 mta_prot_vers, mta_prot_flags, mta_actions;
- ssize_t rlen;
- char *response;
- char data[MILTER_OPTLEN];
-
- /* sanity check */
- if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
- {
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): negotiate, impossible state",
- m->mf_name);
- milter_error(m, e);
- return -1;
- }
-
-#if _FFR_MILTER_CHECK
- mta_prot_vers = m->mf_mta_prot_version;
- mta_prot_flags = m->mf_mta_prot_flags;
- mta_actions = m->mf_mta_actions;
-#else /* _FFR_MILTER_CHECK */
- mta_prot_vers = SMFI_PROT_VERSION;
- mta_prot_flags = SMFI_CURR_PROT;
- mta_actions = SMFI_CURR_ACTS;
-#endif /* _FFR_MILTER_CHECK */
-
- fvers = htonl(mta_prot_vers);
- pflags = htonl(mta_prot_flags);
- fflags = htonl(mta_actions);
- (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, "negotiate");
-
- if (m->mf_state == SMFS_ERROR)
- return -1;
-
- if (tTd(64, 5))
- sm_dprintf("milter_negotiate(%s): send: version %lu, fflags 0x%lx, pflags 0x%lx\n",
- m->mf_name, ntohl(fvers), ntohl(fflags), ntohl(pflags));
-
- response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e,
- "negotiate");
- if (m->mf_state == SMFS_ERROR)
- return -1;
-
- if (rcmd != SMFIC_OPTNEG)
- {
- if (tTd(64, 5))
- sm_dprintf("milter_negotiate(%s): returned %c instead of %c\n",
- m->mf_name, rcmd, SMFIC_OPTNEG);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): negotiate: returned %c instead of %c",
- m->mf_name, rcmd, SMFIC_OPTNEG);
- if (response != NULL)
- sm_free(response); /* XXX */
- milter_error(m, e);
- return -1;
- }
-
- /* Make sure we have enough bytes for the version */
- if (response == NULL || rlen < MILTER_LEN_BYTES)
- {
- if (tTd(64, 5))
- sm_dprintf("milter_negotiate(%s): did not return valid info\n",
- m->mf_name);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): negotiate: did not return valid info",
- m->mf_name);
- if (response != NULL)
- sm_free(response); /* XXX */
- milter_error(m, e);
- 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))
- sm_dprintf("milter_negotiate(%s): did not return enough info\n",
- m->mf_name);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): negotiate: did not return enough info",
- m->mf_name);
- if (response != NULL)
- sm_free(response); /* XXX */
- milter_error(m, e);
- 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);
-
- 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))
- sm_dprintf("milter_negotiate(%s): version %d != MTA milter version %d\n",
- m->mf_name, m->mf_fvers, SMFI_VERSION);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): negotiate: version %d != MTA milter version %d",
- m->mf_name, m->mf_fvers, SMFI_VERSION);
- milter_error(m, e);
- goto error;
- }
-
- /* check for filter feature mismatch */
- if ((m->mf_fflags & mta_actions) != m->mf_fflags)
- {
- if (tTd(64, 5))
- sm_dprintf("milter_negotiate(%s): filter abilities 0x%x != MTA milter abilities 0x%lx\n",
- m->mf_name, m->mf_fflags,
- (unsigned long) mta_actions);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): negotiate: filter abilities 0x%x != MTA milter abilities 0x%lx",
- m->mf_name, m->mf_fflags,
- (unsigned long) mta_actions);
- milter_error(m, e);
- goto error;
- }
-
- /* check for protocol feature mismatch */
- if ((m->mf_pflags & mta_prot_flags) != m->mf_pflags)
- {
- if (tTd(64, 5))
- sm_dprintf("milter_negotiate(%s): protocol abilities 0x%x != MTA milter abilities 0x%lx\n",
- m->mf_name, m->mf_pflags,
- (unsigned long) mta_prot_flags);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): negotiate: protocol abilities 0x%x != MTA milter abilities 0x%lx",
- m->mf_name, m->mf_pflags,
- (unsigned long) mta_prot_flags);
- milter_error(m, e);
- goto error;
- }
-
- if (m->mf_fvers <= 2)
- m->mf_pflags |= SMFIP_NOUNKNOWN;
- if (m->mf_fvers <= 3)
- m->mf_pflags |= SMFIP_NODATA;
-
- if (rlen > MILTER_OPTLEN)
- {
- milter_getsymlist(m, response, rlen, MILTER_OPTLEN);
- }
-
- if (bitset(SMFIF_DELRCPT, m->mf_fflags))
- milters->mis_flags |= MIS_FL_DEL_RCPT;
- if (!bitset(SMFIP_NORCPT, m->mf_pflags) &&
- !bitset(SMFIP_NR_RCPT, m->mf_pflags))
- milters->mis_flags |= MIS_FL_REJ_RCPT;
-
- if (tTd(64, 5))
- sm_dprintf("milter_negotiate(%s): received: version %u, fflags 0x%x, pflags 0x%x\n",
- m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
- return 0;
-
- error:
- if (response != NULL)
- sm_free(response); /* XXX */
- return -1;
-}
-
-/*
-** 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_CLOSABLE)
- milter_quit_filter(m, e);
- }
-}
-
-/*
-** MILTER_ERROR -- Put a milter filter into error state
-**
-** Parameters:
-** m -- the broken filter.
-** e -- current envelope.
-**
-** Returns:
-** none
-*/
-
-static void
-milter_error(m, e)
- struct milter *m;
- ENVELOPE *e;
-{
- /*
- ** 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;
-
- if (MilterLogLevel > 0)
- sm_syslog(LOG_INFO, e->e_id, "Milter (%s): to error state",
- m->mf_name);
-}
-
-/*
-** MILTER_HEADERS -- send headers to a single milter filter
-**
-** Parameters:
-** 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;
-
- if (MilterLogLevel > 17)
- sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, send",
- m->mf_name);
-
- for (h = e->e_header; h != NULL; h = h->h_link)
- {
- int len_n, len_v, len_t, len_f;
- char *buf, *hv;
-
- /* don't send over deleted headers */
- if (h->h_value == NULL)
- {
- /* strip H_USER so not counted in milter_changeheader() */
- h->h_flags &= ~H_USER;
- continue;
- }
-
- /* skip auto-generated */
- if (!bitset(H_USER, h->h_flags))
- continue;
-
- if (tTd(64, 10))
- sm_dprintf("milter_headers: %s:%s\n",
- h->h_field, h->h_value);
- if (MilterLogLevel > 21)
- sm_syslog(LOG_INFO, e->e_id, "Milter (%s): header, %s",
- m->mf_name, h->h_field);
-
- if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags)
- || *(h->h_value) != ' ')
- hv = h->h_value;
- else
- hv = h->h_value + 1;
- len_f = strlen(h->h_field) + 1;
- len_t = len_f + strlen(hv) + 1;
- if (len_t < 0)
- continue;
- buf = (char *) xalloc(len_t);
-
- /*
- ** Note: currently the call to dequote_internal_chars()
- ** is not required as h_field is supposed to be 7-bit US-ASCII.
- */
-
- len_n = dequote_internal_chars(h->h_field, buf, len_f);
- SM_ASSERT(len_n < len_f);
- len_v = dequote_internal_chars(hv, buf + len_n + 1,
- len_t - len_n - 1);
- SM_ASSERT(len_t >= len_n + 1 + len_v + 1);
- len_t = len_n + 1 + len_v + 1;
-
- /* send it over */
- response = milter_send_command(m, SMFIC_HEADER, buf,
- len_t, e, state, "header");
- sm_free(buf);
- if (m->mf_state == SMFS_ERROR ||
- m->mf_state == SMFS_DONE ||
- *state != SMFIR_CONTINUE)
- break;
- }
- if (MilterLogLevel > 17)
- sm_syslog(LOG_INFO, e->e_id, "Milter (%s): headers, sent",
- m->mf_name);
- return response;
-}
-
-/*
-** MILTER_BODY -- send the body to a filter
-**
-** Parameters:
-** 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))
- sm_dprintf("milter_body\n");
-
- if (bfrewind(e->e_dfp) < 0)
- {
- ExitStat = EX_IOERR;
- *state = SMFIR_TEMPFAIL;
- syserr("milter_body: %s/%cf%s: rewind error",
- qid_printqueue(e->e_qgrp, e->e_qdir),
- DATAFL_LETTER, e->e_id);
- return NULL;
- }
-
- if (MilterLogLevel > 17)
- sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, send",
- m->mf_name);
- bp = buf;
- while ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) != SM_IO_EOF)
- {
- /* Change LF to CRLF */
- if (c == '\n')
- {
-#if !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF
- /* Not a CRLF already? */
- if (prevchar != '\r')
-#endif /* !_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF */
- {
- /* 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,
- "body chunk");
- bp = buf;
- if (bufchar != '\0')
- {
- *bp++ = bufchar;
- bufchar = '\0';
- prevchar = bufchar;
- }
- }
- if (m->mf_state == SMFS_ERROR ||
- m->mf_state == SMFS_DONE ||
- m->mf_state == SMFS_SKIP ||
- *state != SMFIR_CONTINUE)
- break;
- }
-
- /* check for read errors */
- if (sm_io_error(e->e_dfp))
- {
- ExitStat = EX_IOERR;
- if (*state == SMFIR_CONTINUE ||
- *state == SMFIR_ACCEPT ||
- m->mf_state == SMFS_SKIP)
- {
- *state = SMFIR_TEMPFAIL;
- if (response != NULL)
- {
- sm_free(response); /* XXX */
- response = NULL;
- }
- }
- syserr("milter_body: %s/%cf%s: read error",
- qid_printqueue(e->e_qgrp, e->e_qdir),
- DATAFL_LETTER, e->e_id);
- return response;
- }
-
- /* send last body chunk */
- if (bp > buf &&
- m->mf_state != SMFS_ERROR &&
- m->mf_state != SMFS_DONE &&
- m->mf_state != SMFS_SKIP &&
- *state == SMFIR_CONTINUE)
- {
- /* send chunk */
- response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
- e, state, "last body chunk");
- bp = buf;
- }
- if (MilterLogLevel > 17)
- sm_syslog(LOG_INFO, e->e_id, "Milter (%s): body, sent",
- m->mf_name);
- if (m->mf_state == SMFS_SKIP)
- {
- *state = SMFIR_CONTINUE;
- m->mf_state = SMFS_READY;
- }
-
- return response;
-}
-
-/*
-** Actions
-*/
-
-/*
-** ADDLEADINGSPACE -- Add a leading space to a string
-**
-** Parameters:
-** str -- string
-** rp -- resource pool for allocations
-**
-** Returns:
-** pointer to new string
-*/
-
-static char *addleadingspace __P((char *, SM_RPOOL_T *));
-
-static char *
-addleadingspace(str, rp)
- char *str;
- SM_RPOOL_T *rp;
-{
- size_t l;
- char *new;
-
- SM_ASSERT(str != NULL);
- l = strlen(str);
- SM_ASSERT(l + 2 > l);
- new = sm_rpool_malloc_x(rp, l + 2);
- new[0] = ' ';
- new[1] = '\0';
- sm_strlcpy(new + 1, str, l + 1);
- return new;
-}
-
-/*
-** MILTER_ADDHEADER -- Add the supplied header to the message
-**
-** Parameters:
-** m -- current filter.
-** response -- encoded form of header/value.
-** rlen -- length of response.
-** e -- current envelope.
-**
-** Returns:
-** none
-*/
-
-static void
-milter_addheader(m, response, rlen, e)
- struct milter *m;
- char *response;
- ssize_t rlen;
- ENVELOPE *e;
-{
- int mh_v_len;
- char *val, *mh_value;
- HDR *h;
-
- if (tTd(64, 10))
- sm_dprintf("milter_addheader: ");
-
- /* sanity checks */
- if (response == NULL)
- {
- if (tTd(64, 10))
- sm_dprintf("NULL response\n");
- return;
- }
-
- if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
- {
- if (tTd(64, 10))
- sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
- (int) strlen(response), (int) (rlen - 1));
- 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))
- sm_dprintf("didn't follow protocol (part len)\n");
- return;
- }
-
- if (*response == '\0')
- {
- if (tTd(64, 10))
- sm_dprintf("empty field name\n");
- return;
- }
-
- for (h = e->e_header; h != NULL; h = h->h_link)
- {
- if (sm_strcasecmp(h->h_field, response) == 0 &&
- !bitset(H_USER, h->h_flags) &&
- !bitset(H_TRACE, h->h_flags))
- break;
- }
-
- mh_v_len = 0;
- mh_value = quote_internal_chars(val, NULL, &mh_v_len);
-
- /* add to e_msgsize */
- e->e_msgsize += strlen(response) + 2 + strlen(val);
-
- if (h != NULL)
- {
- if (tTd(64, 10))
- sm_dprintf("Replace default header %s value with %s\n",
- h->h_field, mh_value);
- if (MilterLogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter change: default header %s value with %s",
- h->h_field, mh_value);
- if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
- h->h_value = mh_value;
- else
- {
- h->h_value = addleadingspace (mh_value, e->e_rpool);
- SM_FREE(mh_value);
- }
- h->h_flags |= H_USER;
- }
- else
- {
- if (tTd(64, 10))
- sm_dprintf("Add %s: %s\n", response, mh_value);
- if (MilterLogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter add: header: %s: %s",
- response, mh_value);
- addheader(newstr(response), mh_value, H_USER, e,
- !bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
- SM_FREE(mh_value);
- }
-}
-
-/*
-** MILTER_INSHEADER -- Insert the supplied header
-**
-** Parameters:
-** m -- current filter.
-** response -- encoded form of header/value.
-** rlen -- length of response.
-** e -- current envelope.
-**
-** Returns:
-** none
-**
-** Notes:
-** Unlike milter_addheader(), this does not attempt to determine
-** if the header already exists in the envelope, even a
-** deleted version. It just blindly inserts.
-*/
-
-static void
-milter_insheader(m, response, rlen, e)
- struct milter *m;
- char *response;
- ssize_t rlen;
- ENVELOPE *e;
-{
- mi_int32 idx, i;
- int mh_v_len;
- char *field, *val, *mh_value;
-
- if (tTd(64, 10))
- sm_dprintf("milter_insheader: ");
-
- /* sanity checks */
- if (response == NULL)
- {
- if (tTd(64, 10))
- sm_dprintf("NULL response\n");
- return;
- }
-
- if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
- {
- if (tTd(64, 10))
- sm_dprintf("didn't follow protocol (total len)\n");
- return;
- }
-
- /* decode */
- (void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
- idx = 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))
- sm_dprintf("didn't follow protocol (part len)\n");
- return;
- }
-
- if (*field == '\0')
- {
- if (tTd(64, 10))
- sm_dprintf("empty field name\n");
- return;
- }
-
- /* add to e_msgsize */
- e->e_msgsize += strlen(response) + 2 + strlen(val);
-
- if (tTd(64, 10))
- sm_dprintf("Insert (%d) %s: %s\n", idx, field, val);
- if (MilterLogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter insert (%d): header: %s: %s",
- idx, field, val);
- mh_v_len = 0;
- mh_value = quote_internal_chars(val, NULL, &mh_v_len);
- insheader(idx, newstr(field), mh_value, H_USER, e,
- !bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
- SM_FREE(mh_value);
-}
-
-/*
-** MILTER_CHANGEHEADER -- Change the supplied header in the message
-**
-** Parameters:
-** m -- current filter.
-** response -- encoded form of header/index/value.
-** rlen -- length of response.
-** e -- current envelope.
-**
-** Returns:
-** none
-*/
-
-static void
-milter_changeheader(m, response, rlen, e)
- struct milter *m;
- char *response;
- ssize_t rlen;
- ENVELOPE *e;
-{
- mi_int32 i, index;
- int mh_v_len;
- char *field, *val, *mh_value;
- HDR *h, *sysheader;
-
- if (tTd(64, 10))
- sm_dprintf("milter_changeheader: ");
-
- /* sanity checks */
- if (response == NULL)
- {
- if (tTd(64, 10))
- sm_dprintf("NULL response\n");
- return;
- }
-
- if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
- {
- if (tTd(64, 10))
- sm_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))
- sm_dprintf("didn't follow protocol (part len)\n");
- return;
- }
-
- if (*field == '\0')
- {
- if (tTd(64, 10))
- sm_dprintf("empty field name\n");
- return;
- }
-
- mh_v_len = 0;
- mh_value = quote_internal_chars(val, NULL, &mh_v_len);
-
- sysheader = NULL;
- for (h = e->e_header; h != NULL; h = h->h_link)
- {
- if (sm_strcasecmp(h->h_field, field) == 0)
- {
- if (bitset(H_USER, h->h_flags) && --index <= 0)
- {
- sysheader = NULL;
- break;
- }
- else if (!bitset(H_USER, h->h_flags) &&
- !bitset(H_TRACE, h->h_flags))
- {
- /*
- ** DRUMS msg-fmt draft says can only have
- ** multiple occurences of trace fields,
- ** so make sure we replace any non-trace,
- ** non-user field.
- */
-
- sysheader = h;
- }
- }
- }
-
- /* if not found as user-provided header at index, use sysheader */
- if (h == NULL)
- h = sysheader;
-
- if (h == NULL)
- {
- if (*val == '\0')
- {
- if (tTd(64, 10))
- sm_dprintf("Delete (noop) %s\n", field);
- if (MilterLogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter delete (noop): header: %s"
- , field);
- }
- else
- {
- /* treat modify value with no existing header as add */
- if (tTd(64, 10))
- sm_dprintf("Add %s: %s\n", field, mh_value);
- if (MilterLogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter change (add): header: %s: %s"
- , field, mh_value);
- addheader(newstr(field), mh_value, H_USER, e,
- !bitset(SMFIP_HDR_LEADSPC, m->mf_pflags));
- }
- return;
- }
-
- if (tTd(64, 10))
- {
- if (*val == '\0')
- {
- sm_dprintf("Delete%s %s:%s\n",
- h == sysheader ? " (default header)" : "",
- field,
- h->h_value == NULL ? "<NULL>" : h->h_value);
- }
- else
- {
- sm_dprintf("Change%s %s: from %s to %s\n",
- h == sysheader ? " (default header)" : "",
- field,
- h->h_value == NULL ? "<NULL>" : h->h_value,
- mh_value);
- }
- }
-
- if (MilterLogLevel > 8)
- {
- if (*val == '\0')
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter delete: header%s %s:%s",
- h == sysheader ? " (default header)" : "",
- field,
- h->h_value == NULL ? "<NULL>" : h->h_value);
- }
- else
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter change: header%s %s: from %s to %s",
- h == sysheader ? " (default header)" : "",
- field,
- h->h_value == NULL ? "<NULL>" : h->h_value,
- mh_value);
- }
- }
-
- if (h != sysheader && h->h_value != NULL)
- {
- size_t l;
-
- l = strlen(h->h_value);
- if (l > e->e_msgsize)
- e->e_msgsize = 0;
- else
- e->e_msgsize -= l;
- /* rpool, don't free: sm_free(h->h_value); XXX */
- }
-
- if (*val == '\0')
- {
- /* Remove "Field: " from message size */
- if (h != sysheader)
- {
- size_t l;
-
- l = strlen(h->h_field) + 2;
- if (l > e->e_msgsize)
- e->e_msgsize = 0;
- else
- e->e_msgsize -= l;
- }
- h->h_value = NULL;
- SM_FREE(mh_value);
- }
- else
- {
- if (bitset(SMFIP_HDR_LEADSPC, m->mf_pflags))
- h->h_value = mh_value;
- else
- {
- h->h_value = addleadingspace (mh_value, e->e_rpool);
- SM_FREE(mh_value);
- }
- h->h_flags |= H_USER;
- e->e_msgsize += strlen(h->h_value);
- }
-}
-
-/*
-** MILTER_SPLIT_RESPONSE -- Split response into fields.
-**
-** Parameters:
-** response -- encoded repsonse.
-** rlen -- length of response.
-** pargc -- number of arguments (ouput)
-**
-** Returns:
-** array of pointers to the individual strings
-*/
-
-static char **milter_split_response __P((char *, ssize_t, int *));
-
-static char **
-milter_split_response(response, rlen, pargc)
- char *response;
- ssize_t rlen;
- int *pargc;
-{
- char **s;
- size_t i;
- int elem, nelem;
-
- SM_ASSERT(response != NULL);
- SM_ASSERT(pargc != NULL);
- *pargc = 0;
- if (rlen < 2 || strlen(response) >= (size_t) rlen)
- {
- if (tTd(64, 10))
- sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
- (int) strlen(response), (int) (rlen - 1));
- return NULL;
- }
-
- nelem = 0;
- for (i = 0; i < rlen; i++)
- {
- if (response[i] == '\0')
- ++nelem;
- }
- if (nelem == 0)
- return NULL;
-
- /* last entry is only for the name */
- s = (char **)malloc(nelem * (sizeof(*s)));
- if (s == NULL)
- return NULL;
- s[0] = response;
- for (i = 0, elem = 0; i < rlen && elem < nelem; i++)
- {
- if (response[i] == '\0')
- {
- ++elem;
- if (i + 1 >= rlen)
- s[elem] = NULL;
- else
- s[elem] = &(response[i + 1]);
- }
- }
- *pargc = nelem;
-
- if (tTd(64, 10))
- {
- for (elem = 0; elem < nelem; elem++)
- sm_dprintf("argv[%d]=\"%s\"\n", elem, s[elem]);
- }
-
- /* overwrite last entry (already done above, just paranoia) */
- s[elem] = NULL;
- return s;
-}
-
-/*
-** MILTER_CHGFROM -- Change the envelope sender address
-**
-** Parameters:
-** response -- encoded form of recipient address.
-** rlen -- length of response.
-** e -- current envelope.
-**
-** Returns:
-** none
-*/
-
-static void
-milter_chgfrom(response, rlen, e)
- char *response;
- ssize_t rlen;
- ENVELOPE *e;
-{
- int olderrors, argc;
- char **argv;
-
- if (tTd(64, 10))
- sm_dprintf("milter_chgfrom: ");
-
- /* sanity checks */
- if (response == NULL)
- {
- if (tTd(64, 10))
- sm_dprintf("NULL response\n");
- return;
- }
-
- if (*response == '\0' ||
- strlen(response) + 1 > (size_t) rlen)
- {
- if (tTd(64, 10))
- sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
- (int) strlen(response), (int) (rlen - 1));
- return;
- }
-
- if (tTd(64, 10))
- sm_dprintf("%s\n", response);
- if (MilterLogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id, "Milter chgfrom: %s", response);
- argv = milter_split_response(response, rlen, &argc);
- if (argc < 1 || argc > 2)
- {
- if (tTd(64, 10))
- sm_dprintf("didn't follow protocol argc=%d\n", argc);
- return;
- }
-
- olderrors = Errors;
- setsender(argv[0], e, NULL, '\0', false);
- if (argc == 2)
- {
- reset_mail_esmtp_args(e);
-
- /*
- ** need "features" here: how to get those? via e?
- ** "fake" it for now: allow everything.
- */
-
- parse_esmtp_args(e, NULL, argv[0], argv[1], "MAIL", NULL,
- mail_esmtp_args);
- }
- Errors = olderrors;
- return;
-}
-
-/*
-** MILTER_ADDRCPT_PAR -- 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_par(response, rlen, e)
- char *response;
- ssize_t rlen;
- ENVELOPE *e;
-{
- int olderrors, argc;
- char *delimptr;
- char **argv;
- ADDRESS *a;
-
- if (tTd(64, 10))
- sm_dprintf("milter_addrcpt_par: ");
-
- /* sanity checks */
- if (response == NULL)
- {
- if (tTd(64, 10))
- sm_dprintf("NULL response\n");
- return;
- }
-
- if (tTd(64, 10))
- sm_dprintf("%s\n", response);
- if (MilterLogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
-
- argv = milter_split_response(response, rlen, &argc);
- if (argc < 1 || argc > 2)
- {
- if (tTd(64, 10))
- sm_dprintf("didn't follow protocol argc=%d\n", argc);
- return;
- }
- olderrors = Errors;
-
- /* how to set ESMTP arguments? */
- a = parseaddr(argv[0], NULLADDR, RF_COPYALL, ' ', &delimptr, e, true);
-
- if (a != NULL && olderrors == Errors)
- {
- parse_esmtp_args(e, a, argv[0], argv[1], "RCPT", NULL,
- rcpt_esmtp_args);
- if (olderrors == Errors)
- a = recipient(a, &e->e_sendqueue, 0, e);
- else
- sm_dprintf("olderrors=%d, Errors=%d\n",
- olderrors, Errors);
- }
- else
- {
- sm_dprintf("a=%p, olderrors=%d, Errors=%d\n",
- a, olderrors, Errors);
- }
-
- Errors = olderrors;
- return;
-}
-
-/*
-** 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;
-{
- int olderrors;
-
- if (tTd(64, 10))
- sm_dprintf("milter_addrcpt: ");
-
- /* sanity checks */
- if (response == NULL)
- {
- if (tTd(64, 10))
- sm_dprintf("NULL response\n");
- return;
- }
-
- if (*response == '\0' ||
- strlen(response) + 1 != (size_t) rlen)
- {
- if (tTd(64, 10))
- sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
- (int) strlen(response), (int) (rlen - 1));
- return;
- }
-
- if (tTd(64, 10))
- sm_dprintf("%s\n", response);
- if (MilterLogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id, "Milter add: rcpt: %s", response);
- olderrors = Errors;
- (void) sendtolist(response, NULLADDR, &e->e_sendqueue, 0, e);
- Errors = olderrors;
- 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))
- sm_dprintf("milter_delrcpt: ");
-
- /* sanity checks */
- if (response == NULL)
- {
- if (tTd(64, 10))
- sm_dprintf("NULL response\n");
- return;
- }
-
- if (*response == '\0' ||
- strlen(response) + 1 != (size_t) rlen)
- {
- if (tTd(64, 10))
- sm_dprintf("didn't follow protocol (total len %d != rlen %d)\n",
- (int) strlen(response), (int) (rlen - 1));
- return;
- }
-
- if (tTd(64, 10))
- sm_dprintf("%s\n", response);
- if (MilterLogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id, "Milter delete: rcpt %s",
- response);
- (void) removefromlist(response, &e->e_sendqueue, e);
- return;
-}
-
-/*
-** MILTER_REPLBODY -- Replace the current data 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))
- sm_dprintf("milter_replbody\n");
-
- /* If a new filter, reset previous character and truncate data file */
- if (newfilter)
- {
- off_t prevsize;
- char dfname[MAXPATHLEN];
-
- (void) sm_strlcpy(dfname, queuename(e, DATAFL_LETTER),
- sizeof(dfname));
-
- /* Reset prevchar */
- prevchar = '\0';
-
- /* Get the current data file information */
- prevsize = sm_io_getinfo(e->e_dfp, SM_IO_WHAT_SIZE, NULL);
- if (prevsize < 0)
- prevsize = 0;
-
- /* truncate current data file */
- if (sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
- {
- if (sm_io_setinfo(e->e_dfp, SM_BF_TRUNCATE, NULL) < 0)
- {
- MILTER_DF_ERROR("milter_replbody: sm_io truncate %s: %s");
- return -1;
- }
- }
- else
- {
- int err;
-
- err = sm_io_error(e->e_dfp);
- (void) sm_io_flush(e->e_dfp, SM_TIME_DEFAULT);
-
- /*
- ** Clear error if tried to fflush()
- ** a read-only file pointer and
- ** there wasn't a previous error.
- */
-
- if (err == 0)
- sm_io_clearerr(e->e_dfp);
-
- /* errno is set implicitly by fseek() before return */
- err = sm_io_seek(e->e_dfp, SM_TIME_DEFAULT,
- 0, SEEK_SET);
- if (err < 0)
- {
- MILTER_DF_ERROR("milter_replbody: sm_io_seek %s: %s");
- return -1;
- }
-# if NOFTRUNCATE
- /* XXX: Not much we can do except rewind it */
- errno = EINVAL;
- MILTER_DF_ERROR("milter_replbody: ftruncate not available on this platform (%s:%s)");
- return -1;
-# else /* NOFTRUNCATE */
- err = ftruncate(sm_io_getinfo(e->e_dfp,
- SM_IO_WHAT_FD, NULL),
- 0);
- if (err < 0)
- {
- MILTER_DF_ERROR("milter_replbody: sm_io ftruncate %s: %s");
- return -1;
- }
-# endif /* NOFTRUNCATE */
- }
-
- if (prevsize > e->e_msgsize)
- e->e_msgsize = 0;
- else
- e->e_msgsize -= prevsize;
- }
-
- if (newfilter && MilterLogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id, "Milter message: body replaced");
-
- if (response == NULL)
- {
- /* Flush the buffered '\r' */
- if (prevchar == '\r')
- {
- (void) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, prevchar);
- 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) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT,
- prevchar);
- 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) sm_io_putc(e->e_dfp, SM_TIME_DEFAULT, response[i]);
- 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.
-** milters -- milters structure.
-**
-** Returns:
-** true iff at least one filter is active
-*/
-
-/* ARGSUSED */
-bool
-milter_init(e, state, milters)
- ENVELOPE *e;
- char *state;
- milters_T *milters;
-{
- int i;
-
- if (tTd(64, 10))
- sm_dprintf("milter_init\n");
-
- memset(milters, '\0', sizeof(*milters));
- *state = SMFIR_CONTINUE;
- if (InputFilters[0] == NULL)
- {
- if (MilterLogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: no active filter");
- return false;
- }
-
- for (i = 0; InputFilters[i] != NULL; i++)
- {
- struct milter *m = InputFilters[i];
-
- m->mf_sock = milter_open(m, false, e);
- if (m->mf_state == SMFS_ERROR)
- {
- MILTER_CHECK_ERROR(true, continue);
- break;
- }
-
- if (m->mf_sock < 0 ||
- milter_negotiate(m, e, milters) < 0 ||
- m->mf_state == SMFS_ERROR)
- {
- if (tTd(64, 5))
- sm_dprintf("milter_init(%s): failed to %s\n",
- m->mf_name,
- m->mf_sock < 0 ? "open" :
- "negotiate");
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "Milter (%s): init failed to %s",
- m->mf_name,
- m->mf_sock < 0 ? "open" :
- "negotiate");
-
- /* if negotation failure, close socket */
- milter_error(m, e);
- MILTER_CHECK_ERROR(true, continue);
- continue;
- }
- if (MilterLogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter (%s): init success to %s",
- m->mf_name,
- m->mf_sock < 0 ? "open" : "negotiate");
- }
-
- /*
- ** 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);
-
- return true;
-}
-
-/*
-** 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;
- unsigned 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))
- sm_dprintf("milter_connect(%s)\n", hostname);
- if (MilterLogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id, "Milter: connect to filters");
-
- /* gather data */
- switch (addr.sa.sa_family)
- {
-# 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 = addr.sin.sin_port;
- sockinfo = (char *) inet_ntoa(addr.sin.sin_addr);
- break;
-# endif /* NETINET */
-
-# if NETINET6
- case AF_INET6:
- if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr))
- family = SMFIA_INET;
- else
- family = SMFIA_INET6;
- port = 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, "connect", false);
- sm_free(buf); /* XXX */
-
- /*
- ** If this message connection is done for,
- ** close the filters.
- */
-
- if (*state != SMFIR_CONTINUE)
- {
- if (MilterLogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id, "Milter: connect, ending");
- milter_quit(e);
- }
- else
- milter_per_connection_check(e);
-
- /*
- ** 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')
- {
- if (strncmp(response, "421 ", 4) == 0)
- *state = SMFIR_SHUTDOWN;
- else
- *state = SMFIR_TEMPFAIL;
- }
- else
- *state = SMFIR_REJECT;
- if (response != NULL)
- {
- sm_free(response); /* XXX */
- 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;
-{
- int i;
- char *response;
-
- if (tTd(64, 10))
- sm_dprintf("milter_helo(%s)\n", helo);
-
- /* HELO/EHLO can come at any point */
- for (i = 0; InputFilters[i] != NULL; i++)
- {
- struct milter *m = InputFilters[i];
-
- switch (m->mf_state)
- {
- case SMFS_INMSG:
- /* abort in message filters */
- milter_abort_filter(m, e);
- /* FALLTHROUGH */
-
- case SMFS_DONE:
- /* reset done filters */
- m->mf_state = SMFS_OPEN;
- break;
- }
- }
-
- response = milter_command(SMFIC_HELO, helo, strlen(helo) + 1,
- MilterHeloMacros, e, state, "helo", false);
- 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))
- {
- sm_dprintf("milter_envfrom:");
- for (i = 0; args[i] != NULL; i++)
- sm_dprintf(" %s", args[i]);
- sm_dprintf("\n");
- }
-
- /* sanity check */
- if (args[0] == NULL)
- {
- *state = SMFIR_REJECT;
- if (MilterLogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: reject, no sender");
- return NULL;
- }
-
- /* 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;
-
- if (s < 0)
- {
- *state = SMFIR_TEMPFAIL;
- return NULL;
- }
-
- buf = (char *) xalloc(s);
- bp = buf;
- for (i = 0; args[i] != NULL; i++)
- {
- (void) sm_strlcpy(bp, args[i], s - (bp - buf));
- bp += strlen(bp) + 1;
- }
-
- if (MilterLogLevel > 14)
- sm_syslog(LOG_INFO, e->e_id, "Milter: sender: %s", buf);
-
- /* send it over */
- response = milter_command(SMFIC_MAIL, buf, s, MilterEnvFromMacros,
- e, state, "mail", false);
- sm_free(buf); /* XXX */
-
- /*
- ** If filter rejects/discards a per message command,
- ** abort the other filters since we are done with the
- ** current message.
- */
-
- MILTER_CHECK_DONE_MSG();
- if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
- sm_syslog(LOG_INFO, e->e_id, "Milter: reject, sender");
- 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.
-** rcpt_error -- does RCPT have an error?
-**
-** Returns:
-** response string (may be NULL)
-*/
-
-char *
-milter_envrcpt(args, e, state, rcpt_error)
- char **args;
- ENVELOPE *e;
- char *state;
- bool rcpt_error;
-{
- int i;
- char *buf, *bp;
- char *response;
- ssize_t s;
-
- if (tTd(64, 10))
- {
- sm_dprintf("milter_envrcpt:");
- for (i = 0; args[i] != NULL; i++)
- sm_dprintf(" %s", args[i]);
- sm_dprintf("\n");
- }
-
- /* sanity check */
- if (args[0] == NULL)
- {
- *state = SMFIR_REJECT;
- if (MilterLogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id, "Milter: reject, no rcpt");
- return NULL;
- }
-
- /* put together data */
- s = 0;
- for (i = 0; args[i] != NULL; i++)
- s += strlen(args[i]) + 1;
-
- if (s < 0)
- {
- *state = SMFIR_TEMPFAIL;
- return NULL;
- }
-
- buf = (char *) xalloc(s);
- bp = buf;
- for (i = 0; args[i] != NULL; i++)
- {
- (void) sm_strlcpy(bp, args[i], s - (bp - buf));
- bp += strlen(bp) + 1;
- }
-
- if (MilterLogLevel > 14)
- sm_syslog(LOG_INFO, e->e_id, "Milter: rcpts: %s", buf);
-
- /* send it over */
- response = milter_command(SMFIC_RCPT, buf, s, MilterEnvRcptMacros,
- e, state, "rcpt", rcpt_error);
- sm_free(buf); /* XXX */
- return response;
-}
-
-/*
-** MILTER_DATA_CMD -- send SMTP DATA command info to milter filters
-**
-** Parameters:
-** e -- current envelope.
-** state -- return state from response.
-**
-** Returns:
-** response string (may be NULL)
-*/
-
-char *
-milter_data_cmd(e, state)
- ENVELOPE *e;
- char *state;
-{
- if (tTd(64, 10))
- sm_dprintf("milter_data_cmd\n");
-
- /* send it over */
- return milter_command(SMFIC_DATA, NULL, 0, MilterDataMacros, e, state,
- "data", false);
-}
-
-/*
-** 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 data file? */
- bool dfopen = false; /* data file 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))
- sm_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;
-
- /* previous problem? */
- if (m->mf_state == SMFS_ERROR)
- {
- MILTER_CHECK_ERROR(false, continue);
- break;
- }
-
- /* 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))
- sm_dprintf("milter_data: eoh\n");
-
- if (MilterEOHMacros[0] != NULL)
- {
- milter_send_macros(m, MilterEOHMacros,
- SMFIC_EOH, e);
- MILTER_CHECK_RESULTS();
- }
-
- /* send it over */
- response = milter_send_command(m, SMFIC_EOH, NULL, 0,
- e, state, "eoh");
- 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();
- }
-
- if (MilterEOMMacros[0] != NULL)
- {
- milter_send_macros(m, MilterEOMMacros,
- SMFIC_BODYEOB, e);
- MILTER_CHECK_RESULTS();
- }
-
- /* send the final body chunk */
- (void) milter_write(m, SMFIC_BODYEOB, NULL, 0,
- m->mf_timeout[SMFTO_WRITE], e, "eom");
-
- /* 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))
- sm_dprintf("milter_data(%s): EOM ACK/NAK timeout\n",
- m->mf_name);
- if (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "milter_data(%s): EOM ACK/NAK timeout",
- m->mf_name);
- milter_error(m, e);
- MILTER_CHECK_ERROR(false, break);
- break;
- }
-
- response = milter_read(m, &rcmd, &rlen,
- m->mf_timeout[SMFTO_READ], e,
- "body");
- if (m->mf_state == SMFS_ERROR)
- break;
-
- if (tTd(64, 10))
- sm_dprintf("milter_data(%s): state %c\n",
- m->mf_name, (char) rcmd);
-
- switch (rcmd)
- {
- case SMFIR_REPLYCODE:
- MILTER_CHECK_REPLYCODE("554 5.7.1 Command rejected");
- if (MilterLogLevel > 12)
- sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject=%s",
- m->mf_name, response);
- *state = rcmd;
- m->mf_state = SMFS_DONE;
- break;
-
- case SMFIR_REJECT: /* log msg at end of function */
- if (MilterLogLevel > 12)
- sm_syslog(LOG_INFO, e->e_id, "milter=%s, reject",
- m->mf_name);
- *state = rcmd;
- m->mf_state = SMFS_DONE;
- break;
-
- case SMFIR_DISCARD:
- if (MilterLogLevel > 12)
- sm_syslog(LOG_INFO, e->e_id, "milter=%s, discard",
- m->mf_name);
- *state = rcmd;
- m->mf_state = SMFS_DONE;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 12)
- sm_syslog(LOG_INFO, e->e_id, "milter=%s, tempfail",
- m->mf_name);
- *state = rcmd;
- m->mf_state = SMFS_DONE;
- break;
-
- 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_QUARANTINE:
- if (!bitset(SMFIF_QUARANTINE, m->mf_fflags))
- {
- if (MilterLogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "milter_data(%s): lied about quarantining, honoring request anyway",
- m->mf_name);
- }
- if (response == NULL)
- response = newstr("");
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "milter=%s, quarantine=%s",
- m->mf_name, response);
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
- response);
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), e->e_quarmsg);
- break;
-
- case SMFIR_ADDHEADER:
- if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
- {
- if (MilterLogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "milter_data(%s): lied about adding headers, honoring request anyway",
- m->mf_name);
- }
- milter_addheader(m, response, rlen, e);
- break;
-
- case SMFIR_INSHEADER:
- if (!bitset(SMFIF_ADDHDRS, m->mf_fflags))
- {
- if (MilterLogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "milter_data(%s): lied about adding headers, honoring request anyway",
- m->mf_name);
- }
- milter_insheader(m, response, rlen, e);
- break;
-
- case SMFIR_CHGHEADER:
- if (!bitset(SMFIF_CHGHDRS, m->mf_fflags))
- {
- if (MilterLogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "milter_data(%s): lied about changing headers, honoring request anyway",
- m->mf_name);
- }
- milter_changeheader(m, response, rlen, e);
- break;
-
- case SMFIR_CHGFROM:
- if (!bitset(SMFIF_CHGFROM, m->mf_fflags))
- {
- if (MilterLogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "milter_data(%s) lied about changing sender, honoring request anyway",
- m->mf_name);
- }
- milter_chgfrom(response, rlen, e);
- break;
-
- case SMFIR_ADDRCPT:
- if (!bitset(SMFIF_ADDRCPT, m->mf_fflags))
- {
- if (MilterLogLevel > 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_ADDRCPT_PAR:
- if (!bitset(SMFIF_ADDRCPT_PAR, m->mf_fflags))
- {
- if (MilterLogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "milter_data(%s) lied about adding recipients with parameters, honoring request anyway",
- m->mf_name);
- }
- milter_addrcpt_par(response, rlen, e);
- break;
-
- case SMFIR_DELRCPT:
- if (!bitset(SMFIF_DELRCPT, m->mf_fflags))
- {
- if (MilterLogLevel > 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 (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "milter_data(%s): lied about replacing body, rejecting request and tempfailing message",
- m->mf_name);
- replfailed = true;
- 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 (MilterLogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "milter_data(%s): returned bogus response %c",
- m->mf_name, rcmd);
- milter_error(m, e);
- break;
- }
- if (rcmd != SMFIR_REPLYCODE && response != NULL)
- {
- sm_free(response); /* XXX */
- 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(false, 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;
- SM_FREE_CLR(response);
- }
-
- if (dfopen)
- {
- (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
- 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;
- SM_FREE_CLR(response);
- }
-
- errno = save_errno;
- syserr("milter_data: %s/%cf%s: read error",
- qid_printqueue(e->e_qgrp, e->e_qdir),
- DATAFL_LETTER, e->e_id);
- }
-
- MILTER_CHECK_DONE_MSG();
- if (MilterLogLevel > 10 && *state == SMFIR_REJECT)
- sm_syslog(LOG_INFO, e->e_id, "Milter: reject, data");
- return response;
-}
-
-/*
-** MILTER_UNKNOWN -- send any unrecognized or unimplemented command
-** string to milter filters
-**
-** Parameters:
-** smtpcmd -- the string itself.
-** e -- current envelope.
-** state -- return state from response.
-**
-**
-** Returns:
-** response string (may be NULL)
-*/
-
-char *
-milter_unknown(smtpcmd, e, state)
- char *smtpcmd;
- ENVELOPE *e;
- char *state;
-{
- if (tTd(64, 10))
- sm_dprintf("milter_unknown(%s)\n", smtpcmd);
-
- return milter_command(SMFIC_UNKNOWN, smtpcmd, strlen(smtpcmd) + 1,
- NULL, e, state, "unknown", false);
-}
-
-/*
-** 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))
- sm_dprintf("milter_quit(%s)\n", e->e_id);
-
- for (i = 0; InputFilters[i] != NULL; i++)
- milter_quit_filter(InputFilters[i], e);
-}
-
-/*
-** MILTER_ABORT -- informs the filter(s) that we are aborting current message
-**
-** Parameters:
-** e -- current envelope.
-**
-** Returns:
-** none
-*/
-
-void
-milter_abort(e)
- ENVELOPE *e;
-{
- int i;
-
- if (tTd(64, 10))
- sm_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 /* MILTER */
diff --git a/contrib/sendmail/src/mime.c b/contrib/sendmail/src/mime.c
deleted file mode 100644
index af71d79..0000000
--- a/contrib/sendmail/src/mime.c
+++ /dev/null
@@ -1,1325 +0,0 @@
-/*
- * Copyright (c) 1998-2003, 2006 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.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sendmail.h>
-#include <string.h>
-
-SM_RCSID("@(#)$Id: mime.c,v 8.147 2007/09/26 23:29:11 ca Exp $")
-
-/*
-** MIME support.
-**
-** I am indebted to John Beck of Hewlett-Packard, who contributed
-** his code to me for inclusion. As it turns out, I did not use
-** his code since he used a "minimum change" approach that used
-** several temp files, and I wanted a "minimum impact" approach
-** that would avoid copying. However, looking over his code
-** helped me cement my understanding of the problem.
-**
-** I also looked at, but did not directly use, Nathaniel
-** Borenstein's "code.c" module. Again, it functioned as
-** a file-to-file translator, which did not fit within my
-** design bounds, but it was a useful base for understanding
-** the problem.
-*/
-
-/* use "old" mime 7 to 8 algorithm by default */
-#ifndef MIME7TO8_OLD
-# define MIME7TO8_OLD 1
-#endif /* ! MIME7TO8_OLD */
-
-#if MIME8TO7
-static int isboundary __P((char *, char **));
-static int mimeboundary __P((char *, char **));
-static int mime_getchar __P((SM_FILE_T *, char **, int *));
-static int mime_getchar_crlf __P((SM_FILE_T *, char **, int *));
-
-/* character set for hex and base64 encoding */
-static char Base16Code[] = "0123456789ABCDEF";
-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) */
-
-static char *MimeBoundaryNames[] =
-{
- "SYNTAX", "NOTSEP", "INTERMED", "FINAL"
-};
-
-static bool MapNLtoCRLF;
-
-/*
-** MIME8TO7 -- output 8 bit body in 7 bit format
-**
-** The header has already been output -- this has to do the
-** 8 to 7 bit conversion. It would be easy if we didn't have
-** to deal with nested formats (multipart/xxx and message/rfc822).
-**
-** We won't be called if we don't have to do a conversion, and
-** appropriate MIME-Version: and Content-Type: fields have been
-** output. Any Content-Transfer-Encoding: field has not been
-** output, and we can add it here.
-**
-** Parameters:
-** mci -- mailer connection information.
-** header -- the header for this body part.
-** e -- envelope.
-** boundaries -- the currently pending message boundaries.
-** NULL if we are processing the outer portion.
-** flags -- to tweak processing.
-** level -- recursion level.
-**
-** Returns:
-** An indicator of what terminated the message part:
-** MBT_FINAL -- the final boundary
-** MBT_INTERMED -- an intermediate boundary
-** MBT_NOTSEP -- an end of file
-** SM_IO_EOF -- I/O error occurred
-*/
-
-struct args
-{
- char *a_field; /* name of field */
- char *a_value; /* value of that field */
-};
-
-int
-mime8to7(mci, header, e, boundaries, flags, level)
- register MCI *mci;
- HDR *header;
- register ENVELOPE *e;
- char **boundaries;
- int flags;
- int level;
-{
- register char *p;
- int linelen;
- int bt;
- off_t offset;
- size_t sectionsize, sectionhighbits;
- int i;
- char *type;
- char *subtype;
- char *cte;
- char **pvp;
- int argc = 0;
- char *bp;
- bool use_qp = false;
- struct args argv[MAXMIMEARGS];
- char bbuf[128];
- char buf[MAXLINE];
- char pvpbuf[MAXLINE];
- extern unsigned char MimeTokenTab[256];
-
- if (level > MAXMIMENESTING)
- {
- if (!bitset(EF_TOODEEP, e->e_flags))
- {
- if (tTd(43, 4))
- sm_dprintf("mime8to7: too deep, level=%d\n",
- level);
- usrerr("mime8to7: recursion level %d exceeded",
- level);
- e->e_flags |= EF_DONT_MIME|EF_TOODEEP;
- }
- }
- if (tTd(43, 1))
- {
- sm_dprintf("mime8to7: flags = %x, boundaries =", flags);
- if (boundaries[0] == NULL)
- sm_dprintf(" <none>");
- else
- {
- for (i = 0; boundaries[i] != NULL; i++)
- sm_dprintf(" %s", boundaries[i]);
- }
- sm_dprintf("\n");
- }
- MapNLtoCRLF = true;
- p = hvalue("Content-Transfer-Encoding", header);
- if (p == NULL ||
- (pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL,
- MimeTokenTab, false)) == NULL ||
- pvp[0] == NULL)
- {
- cte = NULL;
- }
- else
- {
- cataddr(pvp, NULL, buf, sizeof(buf), '\0', false);
- cte = sm_rpool_strdup_x(e->e_rpool, buf);
- }
-
- type = subtype = NULL;
- p = hvalue("Content-Type", header);
- if (p == NULL)
- {
- if (bitset(M87F_DIGEST, flags))
- p = "message/rfc822";
- else
- p = "text/plain";
- }
- if (p != NULL &&
- (pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL,
- MimeTokenTab, false)) != NULL &&
- pvp[0] != NULL)
- {
- if (tTd(43, 40))
- {
- for (i = 0; pvp[i] != NULL; i++)
- sm_dprintf("pvp[%d] = \"%s\"\n", i, pvp[i]);
- }
- type = *pvp++;
- if (*pvp != NULL && strcmp(*pvp, "/") == 0 &&
- *++pvp != NULL)
- {
- subtype = *pvp++;
- }
-
- /* break out parameters */
- while (*pvp != NULL && argc < MAXMIMEARGS)
- {
- /* skip to semicolon separator */
- while (*pvp != NULL && strcmp(*pvp, ";") != 0)
- pvp++;
- 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].a_field = *pvp++;
-
- /* see if there is a value */
- if (*pvp != NULL && strcmp(*pvp, "=") == 0 &&
- (*++pvp == NULL || strcmp(*pvp, ";") != 0))
- {
- argv[argc].a_value = *pvp;
- argc++;
- }
- }
- }
-
- /* check for disaster cases */
- if (type == NULL)
- type = "-none-";
- if (subtype == NULL)
- subtype = "-none-";
-
- /* don't propagate some flags more than one level into the message */
- flags &= ~M87F_DIGEST;
-
- /*
- ** Check for cases that can not be encoded.
- **
- ** For example, you can't encode certain kinds of types
- ** or already-encoded messages. If we find this case,
- ** just copy it through.
- */
-
- (void) sm_snprintf(buf, sizeof(buf), "%.100s/%.100s", type, subtype);
- if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e')))
- flags |= M87F_NO8BIT;
-
-# ifdef USE_B_CLASS
- if (wordinclass(buf, 'b') || wordinclass(type, 'b'))
- MapNLtoCRLF = false;
-# endif /* USE_B_CLASS */
- if (wordinclass(buf, 'q') || wordinclass(type, 'q'))
- use_qp = true;
-
- /*
- ** Multipart requires special processing.
- **
- ** Do a recursive descent into the message.
- */
-
- if (sm_strcasecmp(type, "multipart") == 0 &&
- (!bitset(M87F_NO8BIT, flags) || bitset(M87F_NO8TO7, flags)) &&
- !bitset(EF_TOODEEP, e->e_flags)
- )
- {
-
- if (sm_strcasecmp(subtype, "digest") == 0)
- flags |= M87F_DIGEST;
-
- for (i = 0; i < argc; i++)
- {
- if (sm_strcasecmp(argv[i].a_field, "boundary") == 0)
- break;
- }
- if (i >= argc || argv[i].a_value == NULL)
- {
- usrerr("mime8to7: Content-Type: \"%s\": %s boundary",
- i >= argc ? "missing" : "bogus", p);
- p = "---";
-
- /* avoid bounce loops */
- e->e_flags |= EF_DONT_MIME;
- }
- else
- {
- p = argv[i].a_value;
- stripquotes(p);
- }
- if (sm_strlcpy(bbuf, p, sizeof(bbuf)) >= sizeof(bbuf))
- {
- usrerr("mime8to7: multipart boundary \"%s\" too long",
- p);
-
- /* avoid bounce loops */
- e->e_flags |= EF_DONT_MIME;
- }
-
- if (tTd(43, 1))
- sm_dprintf("mime8to7: multipart boundary \"%s\"\n",
- bbuf);
- for (i = 0; i < MAXMIMENESTING; i++)
- {
- if (boundaries[i] == NULL)
- break;
- }
- if (i >= MAXMIMENESTING)
- {
- if (tTd(43, 4))
- sm_dprintf("mime8to7: too deep, i=%d\n", i);
- if (!bitset(EF_TOODEEP, e->e_flags))
- usrerr("mime8to7: multipart nesting boundary too deep");
-
- /* avoid bounce loops */
- e->e_flags |= EF_DONT_MIME|EF_TOODEEP;
- }
- else
- {
- boundaries[i] = bbuf;
- boundaries[i + 1] = NULL;
- }
- mci->mci_flags |= MCIF_INMIME;
-
- /* skip the early "comment" prologue */
- if (!putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
- bt = MBT_FINAL;
- while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof(buf))
- != NULL)
- {
- bt = mimeboundary(buf, boundaries);
- if (bt != MBT_NOTSEP)
- break;
- if (!putxline(buf, strlen(buf), mci,
- PXLF_MAPFROM|PXLF_STRIP8BIT))
- goto writeerr;
- if (tTd(43, 99))
- sm_dprintf(" ...%s", buf);
- }
- if (sm_io_eof(e->e_dfp))
- bt = MBT_FINAL;
- while (bt != MBT_FINAL)
- {
- auto HDR *hdr = NULL;
-
- (void) sm_strlcpyn(buf, sizeof(buf), 2, "--", bbuf);
- if (!putline(buf, mci))
- goto writeerr;
- if (tTd(43, 35))
- sm_dprintf(" ...%s\n", buf);
- collect(e->e_dfp, false, &hdr, e, false);
- if (tTd(43, 101))
- putline("+++after collect", mci);
- if (!putheader(mci, hdr, e, flags))
- goto writeerr;
- if (tTd(43, 101))
- putline("+++after putheader", mci);
- bt = mime8to7(mci, hdr, e, boundaries, flags,
- level + 1);
- if (bt == SM_IO_EOF)
- goto writeerr;
- }
- (void) sm_strlcpyn(buf, sizeof(buf), 3, "--", bbuf, "--");
- if (!putline(buf, mci))
- goto writeerr;
- if (tTd(43, 35))
- sm_dprintf(" ...%s\n", buf);
- boundaries[i] = NULL;
- mci->mci_flags &= ~MCIF_INMIME;
-
- /* skip the late "comment" epilogue */
- while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof(buf))
- != NULL)
- {
- bt = mimeboundary(buf, boundaries);
- if (bt != MBT_NOTSEP)
- break;
- if (!putxline(buf, strlen(buf), mci,
- PXLF_MAPFROM|PXLF_STRIP8BIT))
- goto writeerr;
- if (tTd(43, 99))
- sm_dprintf(" ...%s", buf);
- }
- if (sm_io_eof(e->e_dfp))
- bt = MBT_FINAL;
- if (tTd(43, 3))
- sm_dprintf("\t\t\tmime8to7=>%s (multipart)\n",
- MimeBoundaryNames[bt]);
- return bt;
- }
-
- /*
- ** Message/xxx types -- recurse exactly once.
- **
- ** Class 's' is predefined to have "rfc822" only.
- */
-
- if (sm_strcasecmp(type, "message") == 0)
- {
- if (!wordinclass(subtype, 's') ||
- bitset(EF_TOODEEP, e->e_flags))
- {
- flags |= M87F_NO8BIT;
- }
- else
- {
- auto HDR *hdr = NULL;
-
- if (!putline("", mci))
- goto writeerr;
-
- mci->mci_flags |= MCIF_INMIME;
- collect(e->e_dfp, false, &hdr, e, false);
- if (tTd(43, 101))
- putline("+++after collect", mci);
- if (!putheader(mci, hdr, e, flags))
- goto writeerr;
- if (tTd(43, 101))
- putline("+++after putheader", mci);
- if (hvalue("MIME-Version", hdr) == NULL &&
- !bitset(M87F_NO8TO7, flags) &&
- !putline("MIME-Version: 1.0", mci))
- goto writeerr;
- bt = mime8to7(mci, hdr, e, boundaries, flags,
- level + 1);
- mci->mci_flags &= ~MCIF_INMIME;
- return bt;
- }
- }
-
- /*
- ** Non-compound body type
- **
- ** Compute the ratio of seven to eight bit characters;
- ** use that as a heuristic to decide how to do the
- ** encoding.
- */
-
- sectionsize = sectionhighbits = 0;
- if (!bitset(M87F_NO8BIT|M87F_NO8TO7, flags))
- {
- /* remember where we were */
- offset = sm_io_tell(e->e_dfp, SM_TIME_DEFAULT);
- if (offset == -1)
- syserr("mime8to7: cannot sm_io_tell on %cf%s",
- DATAFL_LETTER, e->e_id);
-
- /* do a scan of this body type to count character types */
- while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof(buf))
- != NULL)
- {
- if (mimeboundary(buf, boundaries) != MBT_NOTSEP)
- break;
- for (p = buf; *p != '\0'; p++)
- {
- /* count bytes with the high bit set */
- sectionsize++;
- if (bitset(0200, *p))
- sectionhighbits++;
- }
-
- /*
- ** Heuristic: if 1/4 of the first 4K bytes are 8-bit,
- ** assume base64. This heuristic avoids double-reading
- ** large graphics or video files.
- */
-
- if (sectionsize >= 4096 &&
- sectionhighbits > sectionsize / 4)
- break;
- }
-
- /* return to the original offset for processing */
- /* XXX use relative seeks to handle >31 bit file sizes? */
- if (sm_io_seek(e->e_dfp, SM_TIME_DEFAULT, offset, SEEK_SET) < 0)
- syserr("mime8to7: cannot sm_io_fseek on %cf%s",
- DATAFL_LETTER, e->e_id);
- else
- sm_io_clearerr(e->e_dfp);
- }
-
- /*
- ** Heuristically determine encoding method.
- ** If more than 1/8 of the total characters have the
- ** eighth bit set, use base64; else use quoted-printable.
- ** However, only encode binary encoded data as base64,
- ** since otherwise the NL=>CRLF mapping will be a problem.
- */
-
- if (tTd(43, 8))
- {
- sm_dprintf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n",
- (long) sectionhighbits, (long) sectionsize,
- cte == NULL ? "[none]" : cte,
- type == NULL ? "[none]" : type,
- subtype == NULL ? "[none]" : subtype);
- }
- if (cte != NULL && sm_strcasecmp(cte, "binary") == 0)
- sectionsize = sectionhighbits;
- linelen = 0;
- bp = buf;
- if (sectionhighbits == 0)
- {
- /* no encoding necessary */
- if (cte != NULL &&
- bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME,
- mci->mci_flags) &&
- !bitset(M87F_NO8TO7, flags))
- {
- /*
- ** Skip _unless_ in MIME mode and potentially
- ** converting from 8 bit to 7 bit MIME. See
- ** putheader() for the counterpart where the
- ** CTE header is skipped in the opposite
- ** situation.
- */
-
- (void) sm_snprintf(buf, sizeof(buf),
- "Content-Transfer-Encoding: %.200s", cte);
- if (!putline(buf, mci))
- goto writeerr;
- if (tTd(43, 36))
- sm_dprintf(" ...%s\n", buf);
- }
- if (!putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
- while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof(buf))
- != NULL)
- {
- if (!bitset(MCIF_INLONGLINE, mci->mci_flags))
- {
- bt = mimeboundary(buf, boundaries);
- if (bt != MBT_NOTSEP)
- break;
- }
- if (!putxline(buf, strlen(buf), mci,
- PXLF_MAPFROM|PXLF_NOADDEOL))
- goto writeerr;
- }
- if (sm_io_eof(e->e_dfp))
- bt = MBT_FINAL;
- }
- else if (!MapNLtoCRLF ||
- (sectionsize / 8 < sectionhighbits && !use_qp))
- {
- /* use base64 encoding */
- int c1, c2;
-
- if (tTd(43, 36))
- sm_dprintf(" ...Content-Transfer-Encoding: base64\n");
- if (!putline("Content-Transfer-Encoding: base64", mci))
- goto writeerr;
- (void) sm_snprintf(buf, sizeof(buf),
- "X-MIME-Autoconverted: from 8bit to base64 by %s id %s",
- MyHostName, e->e_id);
- if (!putline(buf, mci) || !putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
- while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) !=
- SM_IO_EOF)
- {
- if (linelen > 71)
- {
- *bp = '\0';
- if (!putline(buf, mci))
- goto writeerr;
- linelen = 0;
- bp = buf;
- }
- linelen += 4;
- *bp++ = Base64Code[(c1 >> 2)];
- c1 = (c1 & 0x03) << 4;
- c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
- if (c2 == SM_IO_EOF)
- {
- *bp++ = Base64Code[c1];
- *bp++ = '=';
- *bp++ = '=';
- break;
- }
- c1 |= (c2 >> 4) & 0x0f;
- *bp++ = Base64Code[c1];
- c1 = (c2 & 0x0f) << 2;
- c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
- if (c2 == SM_IO_EOF)
- {
- *bp++ = Base64Code[c1];
- *bp++ = '=';
- break;
- }
- c1 |= (c2 >> 6) & 0x03;
- *bp++ = Base64Code[c1];
- *bp++ = Base64Code[c2 & 0x3f];
- }
- *bp = '\0';
- if (!putline(buf, mci))
- goto writeerr;
- }
- else
- {
- /* use quoted-printable encoding */
- int c1, c2;
- int fromstate;
- BITMAP256 badchars;
-
- /* set up map of characters that must be mapped */
- clrbitmap(badchars);
- for (c1 = 0x00; c1 < 0x20; c1++)
- setbitn(c1, badchars);
- clrbitn('\t', badchars);
- for (c1 = 0x7f; c1 < 0x100; c1++)
- setbitn(c1, badchars);
- setbitn('=', badchars);
- if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags))
- for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++)
- setbitn(*p, badchars);
-
- if (tTd(43, 36))
- sm_dprintf(" ...Content-Transfer-Encoding: quoted-printable\n");
- if (!putline("Content-Transfer-Encoding: quoted-printable",
- mci))
- goto writeerr;
- (void) sm_snprintf(buf, sizeof(buf),
- "X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s",
- MyHostName, e->e_id);
- if (!putline(buf, mci) || !putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
- fromstate = 0;
- c2 = '\n';
- while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) !=
- SM_IO_EOF)
- {
- if (c1 == '\n')
- {
- if (c2 == ' ' || c2 == '\t')
- {
- *bp++ = '=';
- *bp++ = Base16Code[(c2 >> 4) & 0x0f];
- *bp++ = Base16Code[c2 & 0x0f];
- }
- if (buf[0] == '.' && bp == &buf[1])
- {
- buf[0] = '=';
- *bp++ = Base16Code[('.' >> 4) & 0x0f];
- *bp++ = Base16Code['.' & 0x0f];
- }
- *bp = '\0';
- if (!putline(buf, mci))
- goto writeerr;
- linelen = fromstate = 0;
- bp = buf;
- c2 = c1;
- continue;
- }
- if (c2 == ' ' && linelen == 4 && fromstate == 4 &&
- bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
- {
- *bp++ = '=';
- *bp++ = '2';
- *bp++ = '0';
- linelen += 3;
- }
- else if (c2 == ' ' || c2 == '\t')
- {
- *bp++ = c2;
- linelen++;
- }
- if (linelen > 72 &&
- (linelen > 75 || c1 != '.' ||
- (linelen > 73 && c2 == '.')))
- {
- if (linelen > 73 && c2 == '.')
- bp--;
- else
- c2 = '\n';
- *bp++ = '=';
- *bp = '\0';
- if (!putline(buf, mci))
- goto writeerr;
- linelen = fromstate = 0;
- bp = buf;
- if (c2 == '.')
- {
- *bp++ = '.';
- linelen++;
- }
- }
- if (bitnset(bitidx(c1), badchars))
- {
- *bp++ = '=';
- *bp++ = Base16Code[(c1 >> 4) & 0x0f];
- *bp++ = Base16Code[c1 & 0x0f];
- linelen += 3;
- }
- else if (c1 != ' ' && c1 != '\t')
- {
- if (linelen < 4 && c1 == "From"[linelen])
- fromstate++;
- *bp++ = c1;
- linelen++;
- }
- c2 = c1;
- }
-
- /* output any saved character */
- if (c2 == ' ' || c2 == '\t')
- {
- *bp++ = '=';
- *bp++ = Base16Code[(c2 >> 4) & 0x0f];
- *bp++ = Base16Code[c2 & 0x0f];
- linelen += 3;
- }
-
- if (linelen > 0 || boundaries[0] != NULL)
- {
- *bp = '\0';
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- }
- if (tTd(43, 3))
- sm_dprintf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
- return bt;
-
- writeerr:
- return SM_IO_EOF;
-}
-/*
-** MIME_GETCHAR -- get a character for MIME processing
-**
-** Treats boundaries as SM_IO_EOF.
-**
-** Parameters:
-** fp -- the input file.
-** boundaries -- the current MIME boundaries.
-** btp -- if the return value is SM_IO_EOF, *btp is set to
-** the type of the boundary.
-**
-** Returns:
-** The next character in the input stream.
-*/
-
-static int
-mime_getchar(fp, boundaries, btp)
- register SM_FILE_T *fp;
- char **boundaries;
- int *btp;
-{
- int c;
- static unsigned char *bp = NULL;
- static int buflen = 0;
- static bool atbol = true; /* at beginning of line */
- static int bt = MBT_SYNTAX; /* boundary type of next SM_IO_EOF */
- static unsigned char buf[128]; /* need not be a full line */
- int start = 0; /* indicates position of - in buffer */
-
- if (buflen == 1 && *bp == '\n')
- {
- /* last \n in buffer may be part of next MIME boundary */
- c = *bp;
- }
- else if (buflen > 0)
- {
- buflen--;
- return *bp++;
- }
- else
- c = sm_io_getc(fp, SM_TIME_DEFAULT);
- bp = buf;
- buflen = 0;
- if (c == '\n')
- {
- /* might be part of a MIME boundary */
- *bp++ = c;
- atbol = true;
- c = sm_io_getc(fp, SM_TIME_DEFAULT);
- if (c == '\n')
- {
- (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
- return c;
- }
- start = 1;
- }
- if (c != SM_IO_EOF)
- *bp++ = c;
- else
- bt = MBT_FINAL;
- if (atbol && c == '-')
- {
- /* check for a message boundary */
- c = sm_io_getc(fp, SM_TIME_DEFAULT);
- if (c != '-')
- {
- if (c != SM_IO_EOF)
- *bp++ = c;
- else
- bt = MBT_FINAL;
- buflen = bp - buf - 1;
- bp = buf;
- return *bp++;
- }
-
- /* got "--", now check for rest of separator */
- *bp++ = '-';
- while (bp < &buf[sizeof(buf) - 2] &&
- (c = sm_io_getc(fp, SM_TIME_DEFAULT)) != SM_IO_EOF &&
- c != '\n')
- {
- *bp++ = c;
- }
- *bp = '\0'; /* XXX simply cut off? */
- bt = mimeboundary((char *) &buf[start], boundaries);
- switch (bt)
- {
- case MBT_FINAL:
- case MBT_INTERMED:
- /* we have a message boundary */
- buflen = 0;
- *btp = bt;
- return SM_IO_EOF;
- }
-
- if (bp < &buf[sizeof(buf) - 2] && c != SM_IO_EOF)
- *bp++ = c;
- }
-
- atbol = c == '\n';
- buflen = bp - buf - 1;
- if (buflen < 0)
- {
- *btp = bt;
- return SM_IO_EOF;
- }
- bp = buf;
- return *bp++;
-}
-/*
-** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF
-**
-** Parameters:
-** fp -- the input file.
-** boundaries -- the current MIME boundaries.
-** btp -- if the return value is SM_IO_EOF, *btp is set to
-** the type of the boundary.
-**
-** Returns:
-** The next character in the input stream.
-*/
-
-static int
-mime_getchar_crlf(fp, boundaries, btp)
- register SM_FILE_T *fp;
- char **boundaries;
- int *btp;
-{
- static bool sendlf = false;
- int c;
-
- if (sendlf)
- {
- sendlf = false;
- return '\n';
- }
- c = mime_getchar(fp, boundaries, btp);
- if (c == '\n' && MapNLtoCRLF)
- {
- sendlf = true;
- return '\r';
- }
- return c;
-}
-/*
-** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type
-**
-** Parameters:
-** line -- the input line.
-** boundaries -- the set of currently pending boundaries.
-**
-** Returns:
-** MBT_NOTSEP -- if this is not a separator line
-** MBT_INTERMED -- if this is an intermediate separator
-** MBT_FINAL -- if this is a final boundary
-** MBT_SYNTAX -- if this is a boundary for the wrong
-** enclosure -- i.e., a syntax error.
-*/
-
-static int
-mimeboundary(line, boundaries)
- register char *line;
- char **boundaries;
-{
- int type = MBT_NOTSEP;
- int i;
- int savec;
-
- if (line[0] != '-' || line[1] != '-' || boundaries == NULL)
- return MBT_NOTSEP;
- i = strlen(line);
- if (i > 0 && line[i - 1] == '\n')
- i--;
-
- /* strip off trailing whitespace */
- while (i > 0 && (line[i - 1] == ' ' || line[i - 1] == '\t'
-#if _FFR_MIME_CR_OK
- || line[i - 1] == '\r'
-#endif /* _FFR_MIME_CR_OK */
- ))
- i--;
- savec = line[i];
- line[i] = '\0';
-
- if (tTd(43, 5))
- sm_dprintf("mimeboundary: line=\"%s\"... ", line);
-
- /* check for this as an intermediate boundary */
- if (isboundary(&line[2], boundaries) >= 0)
- type = MBT_INTERMED;
- else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0)
- {
- /* check for a final boundary */
- line[i - 2] = '\0';
- if (isboundary(&line[2], boundaries) >= 0)
- type = MBT_FINAL;
- line[i - 2] = '-';
- }
-
- line[i] = savec;
- if (tTd(43, 5))
- sm_dprintf("%s\n", MimeBoundaryNames[type]);
- return type;
-}
-/*
-** DEFCHARSET -- return default character set for message
-**
-** The first choice for character set is for the mailer
-** corresponding to the envelope sender. If neither that
-** nor the global configuration file has a default character
-** set defined, return "unknown-8bit" as recommended by
-** RFC 1428 section 3.
-**
-** Parameters:
-** e -- the envelope for this message.
-**
-** Returns:
-** The default character set for that mailer.
-*/
-
-char *
-defcharset(e)
- register ENVELOPE *e;
-{
- if (e != NULL && e->e_from.q_mailer != NULL &&
- e->e_from.q_mailer->m_defcharset != NULL)
- return e->e_from.q_mailer->m_defcharset;
- if (DefaultCharSet != NULL)
- return DefaultCharSet;
- return "unknown-8bit";
-}
-/*
-** ISBOUNDARY -- is a given string a currently valid boundary?
-**
-** Parameters:
-** line -- the current input line.
-** boundaries -- the list of valid boundaries.
-**
-** Returns:
-** The index number in boundaries if the line is found.
-** -1 -- otherwise.
-**
-*/
-
-static int
-isboundary(line, boundaries)
- char *line;
- char **boundaries;
-{
- register int i;
-
- for (i = 0; i <= MAXMIMENESTING && boundaries[i] != NULL; i++)
- {
- if (strcmp(line, boundaries[i]) == 0)
- return i;
- }
- return -1;
-}
-#endif /* MIME8TO7 */
-
-#if MIME7TO8
-static int mime_fromqp __P((unsigned char *, unsigned char **, int));
-
-/*
-** MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format
-**
-** This is a hack. Supports translating the two 7-bit body-encodings
-** (quoted-printable and base64) to 8-bit coded bodies.
-**
-** There is not much point in supporting multipart here, as the UA
-** will be able to deal with encoded MIME bodies if it can parse MIME
-** multipart messages.
-**
-** Note also that we won't be called unless it is a text/plain MIME
-** message, encoded base64 or QP and mailer flag '9' has been defined
-** on mailer.
-**
-** Contributed by Marius Olaffson <marius@rhi.hi.is>.
-**
-** Parameters:
-** mci -- mailer connection information.
-** header -- the header for this body part.
-** e -- envelope.
-**
-** Returns:
-** true iff body was written successfully
-*/
-
-static char index_64[128] =
-{
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
- 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
- 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
- -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
- 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)])
-
-bool
-mime7to8(mci, header, e)
- register MCI *mci;
- HDR *header;
- register ENVELOPE *e;
-{
- int pxflags;
- register char *p;
- char *cte;
- char **pvp;
- unsigned char *fbufp;
- char buf[MAXLINE];
- unsigned char fbuf[MAXLINE + 1];
- char pvpbuf[MAXLINE];
- extern unsigned char MimeTokenTab[256];
-
- p = hvalue("Content-Transfer-Encoding", header);
- if (p == NULL ||
- (pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL,
- MimeTokenTab, false)) == NULL ||
- pvp[0] == NULL)
- {
- /* "can't happen" -- upper level should have caught this */
- syserr("mime7to8: unparsable CTE %s", p == NULL ? "<NULL>" : p);
-
- /* avoid bounce loops */
- e->e_flags |= EF_DONT_MIME;
-
- /* cheap failsafe algorithm -- should work on text/plain */
- if (p != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Content-Transfer-Encoding: %s", p);
- if (!putline(buf, mci))
- goto writeerr;
- }
- if (!putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
- while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf, sizeof(buf))
- != NULL)
- {
- if (!putline(buf, mci))
- goto writeerr;
- }
- return true;
- }
- cataddr(pvp, NULL, buf, sizeof(buf), '\0', false);
- cte = sm_rpool_strdup_x(e->e_rpool, buf);
-
- mci->mci_flags |= MCIF_INHEADER;
- if (!putline("Content-Transfer-Encoding: 8bit", mci))
- goto writeerr;
- (void) sm_snprintf(buf, sizeof(buf),
- "X-MIME-Autoconverted: from %.200s to 8bit by %s id %s",
- cte, MyHostName, e->e_id);
- if (!putline(buf, mci) || !putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
-
- /*
- ** Translate body encoding to 8-bit. Supports two types of
- ** encodings; "base64" and "quoted-printable". Assume qp if
- ** it is not base64.
- */
-
- pxflags = PXLF_MAPFROM;
- if (sm_strcasecmp(cte, "base64") == 0)
- {
- int c1, c2, c3, c4;
-
- fbufp = fbuf;
- while ((c1 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) !=
- SM_IO_EOF)
- {
- if (isascii(c1) && isspace(c1))
- continue;
-
- do
- {
- c2 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT);
- } while (isascii(c2) && isspace(c2));
- if (c2 == SM_IO_EOF)
- break;
-
- do
- {
- c3 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT);
- } while (isascii(c3) && isspace(c3));
- if (c3 == SM_IO_EOF)
- break;
-
- do
- {
- c4 = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT);
- } while (isascii(c4) && isspace(c4));
- if (c4 == SM_IO_EOF)
- break;
-
- if (c1 == '=' || c2 == '=')
- continue;
- c1 = CHAR64(c1);
- c2 = CHAR64(c2);
-
-#if MIME7TO8_OLD
-#define CHK_EOL if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) \
- ++fbufp;
-#else /* MIME7TO8_OLD */
-#define CHK_EOL if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) \
- { \
- ++fbufp; \
- pxflags |= PXLF_NOADDEOL; \
- }
-#endif /* MIME7TO8_OLD */
-
-#define PUTLINE64 \
- do \
- { \
- if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE]) \
- { \
- CHK_EOL; \
- if (!putxline((char *) fbuf, fbufp - fbuf, mci, pxflags)) \
- goto writeerr; \
- pxflags &= ~PXLF_NOADDEOL; \
- fbufp = fbuf; \
- } \
- } while (0)
-
- *fbufp = (c1 << 2) | ((c2 & 0x30) >> 4);
- PUTLINE64;
- if (c3 == '=')
- continue;
- c3 = CHAR64(c3);
- *fbufp = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
- PUTLINE64;
- if (c4 == '=')
- continue;
- c4 = CHAR64(c4);
- *fbufp = ((c3 & 0x03) << 6) | c4;
- PUTLINE64;
- }
- }
- else
- {
- int off;
-
- /* quoted-printable */
- pxflags |= PXLF_NOADDEOL;
- fbufp = fbuf;
- while (sm_io_fgets(e->e_dfp, SM_TIME_DEFAULT, buf,
- sizeof(buf)) != NULL)
- {
- off = mime_fromqp((unsigned char *) buf, &fbufp,
- &fbuf[MAXLINE] - fbufp);
-again:
- if (off < -1)
- continue;
-
- if (fbufp - fbuf > 0)
- {
- if (!putxline((char *) fbuf, fbufp - fbuf - 1,
- mci, pxflags))
- goto writeerr;
- }
- fbufp = fbuf;
- if (off >= 0 && buf[off] != '\0')
- {
- off = mime_fromqp((unsigned char *) (buf + off),
- &fbufp,
- &fbuf[MAXLINE] - fbufp);
- goto again;
- }
- }
- }
-
- /* force out partial last line */
- if (fbufp > fbuf)
- {
- *fbufp = '\0';
- if (!putxline((char *) fbuf, fbufp - fbuf, mci, pxflags))
- goto writeerr;
- }
-
- /*
- ** The decoded text may end without an EOL. Since this function
- ** is only called for text/plain MIME messages, it is safe to
- ** add an extra one at the end just in case. This is a hack,
- ** but so is auto-converting MIME in the first place.
- */
-
- if (!putline("", mci))
- goto writeerr;
-
- if (tTd(43, 3))
- sm_dprintf("\t\t\tmime7to8 => %s to 8bit done\n", cte);
- return true;
-
- writeerr:
- return false;
-}
-/*
-** The following is based on Borenstein's "codes.c" module, with simplifying
-** changes as we do not deal with multipart, and to do the translation in-core,
-** with an attempt to prevent overrun of output buffers.
-**
-** What is needed here are changes to defend this code better against
-** bad encodings. Questionable to always return 0xFF for bad mappings.
-*/
-
-static char index_hex[128] =
-{
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
- -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
- -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)])
-
-/*
-** MIME_FROMQP -- decode quoted printable string
-**
-** Parameters:
-** infile -- input (encoded) string
-** outfile -- output string
-** maxlen -- size of output buffer
-**
-** Returns:
-** -2 if decoding failure
-** -1 if infile completely decoded into outfile
-** >= 0 is the position in infile decoding
-** reached before maxlen was reached
-*/
-
-static int
-mime_fromqp(infile, outfile, maxlen)
- unsigned char *infile;
- unsigned char **outfile;
- int maxlen; /* Max # of chars allowed in outfile */
-{
- int c1, c2;
- int nchar = 0;
- unsigned char *b;
-
- /* decrement by one for trailing '\0', at least one other char */
- if (--maxlen < 1)
- return 0;
-
- b = infile;
- while ((c1 = *infile++) != '\0' && nchar < maxlen)
- {
- if (c1 == '=')
- {
- if ((c1 = *infile++) == '\0')
- break;
-
- if (c1 == '\n' || (c1 = HEXCHAR(c1)) == -1)
- {
- /* ignore it and the rest of the buffer */
- return -2;
- }
- else
- {
- do
- {
- if ((c2 = *infile++) == '\0')
- {
- c2 = -1;
- break;
- }
- } while ((c2 = HEXCHAR(c2)) == -1);
-
- if (c2 == -1)
- break;
- nchar++;
- *(*outfile)++ = c1 << 4 | c2;
- }
- }
- else
- {
- nchar++;
- *(*outfile)++ = c1;
- if (c1 == '\n')
- break;
- }
- }
- *(*outfile)++ = '\0';
- if (nchar >= maxlen)
- return (infile - b - 1);
- return -1;
-}
-#endif /* MIME7TO8 */
diff --git a/contrib/sendmail/src/newaliases.1 b/contrib/sendmail/src/newaliases.1
deleted file mode 100644
index 20fd0e7..0000000
--- a/contrib/sendmail/src/newaliases.1
+++ /dev/null
@@ -1,50 +0,0 @@
-.\" Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
-.\" All rights reserved.
-.\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved.
-.\" Copyright (c) 1985, 1990, 1993
-.\" 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.
-.\"
-.\"
-.\" $Id: newaliases.1,v 8.19 2001/10/10 03:23:17 ca Exp $
-.\"
-.TH NEWALIASES 1 "$Date: 2001/10/10 03:23:17 $"
-.SH NAME
-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
-/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
-.B newaliases
-utility exits 0 on success, and >0 if an error occurs.
-.PP
-Notice: do
-.B not
-use
-.B makemap
-to create the aliases data base, because
-.B newaliases
-puts a special token into the data base that is required by
-.B sendmail.
-.SH FILES
-.TP 2i
-/etc/mail/aliases
-The mail aliases file
-.SH SEE ALSO
-aliases(5), sendmail(8)
-.SH HISTORY
-The
-.B newaliases
-command appeared in 4.0BSD.
diff --git a/contrib/sendmail/src/parseaddr.c b/contrib/sendmail/src/parseaddr.c
deleted file mode 100644
index eca60f9..0000000
--- a/contrib/sendmail/src/parseaddr.c
+++ /dev/null
@@ -1,3360 +0,0 @@
-/*
- * Copyright (c) 1998-2006 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: parseaddr.c,v 8.401 2007/09/27 23:33:59 ca Exp $")
-
-#include <sm/sendmail.h>
-#include "map.h"
-
-static void allocaddr __P((ADDRESS *, int, char *, ENVELOPE *));
-static int callsubr __P((char**, int, ENVELOPE *));
-static char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *));
-static ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
-static bool hasctrlchar __P((register char *, bool, bool));
-
-/* replacement for illegal characters in addresses */
-#define BAD_CHAR_REPLACEMENT '?'
-
-/*
-** PARSEADDR -- Parse an address
-**
-** Parses an address and breaks it up into three parts: a
-** net to transmit the message on, the host to transmit it
-** to, and a user on that host. These are loaded into an
-** ADDRESS header with the values squirreled away if necessary.
-** The "user" part may not be a real user; the process may
-** just reoccur on that machine. For example, on a machine
-** with an arpanet connection, the address
-** csvax.bill@berkeley
-** will break up to a "user" of 'csvax.bill' and a host
-** of 'berkeley' -- to be transmitted over the arpanet.
-**
-** Parameters:
-** addr -- the address to parse.
-** a -- a pointer to the address descriptor buffer.
-** If NULL, an address will be created.
-** flags -- describe detail for parsing. See RF_ definitions
-** in sendmail.h.
-** delim -- the character to terminate the address, passed
-** to prescan.
-** delimptr -- if non-NULL, set to the location of the
-** delim character that was found.
-** e -- the envelope that will contain this address.
-** isrcpt -- true if the address denotes a recipient; false
-** indicates a sender.
-**
-** Returns:
-** A pointer to the address descriptor header (`a' if
-** `a' is non-NULL).
-** NULL on error.
-**
-** Side Effects:
-** e->e_to = addr
-*/
-
-/* following delimiters are inherent to the internal algorithms */
-#define DELIMCHARS "()<>,;\r\n" /* default word delimiters */
-
-ADDRESS *
-parseaddr(addr, a, flags, delim, delimptr, e, isrcpt)
- char *addr;
- register ADDRESS *a;
- int flags;
- int delim;
- char **delimptr;
- register ENVELOPE *e;
- bool isrcpt;
-{
- char **pvp;
- auto char *delimptrbuf;
- bool qup;
- char pvpbuf[PSBUFSIZE];
-
- /*
- ** Initialize and prescan address.
- */
-
- e->e_to = addr;
- if (tTd(20, 1))
- sm_dprintf("\n--parseaddr(%s)\n", addr);
-
- if (delimptr == NULL)
- delimptr = &delimptrbuf;
-
- pvp = prescan(addr, delim, pvpbuf, sizeof(pvpbuf), delimptr,
- ExtTokenTab, false);
- if (pvp == NULL)
- {
- if (tTd(20, 1))
- sm_dprintf("parseaddr-->NULL\n");
- return NULL;
- }
-
- if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr, isrcpt))
- {
- if (tTd(20, 1))
- sm_dprintf("parseaddr-->bad address\n");
- return NULL;
- }
-
- /*
- ** Save addr if we are going to have to.
- **
- ** We have to do this early because there is a chance that
- ** the map lookups in the rewriting rules could clobber
- ** static memory somewhere.
- */
-
- if (bitset(RF_COPYPADDR, flags) && addr != NULL)
- {
- char savec = **delimptr;
-
- if (savec != '\0')
- **delimptr = '\0';
- e->e_to = addr = sm_rpool_strdup_x(e->e_rpool, addr);
- if (savec != '\0')
- **delimptr = savec;
- }
-
- /*
- ** Apply rewriting rules.
- ** Ruleset 0 does basic parsing. It must resolve.
- */
-
- qup = false;
- if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
- qup = true;
- if (REWRITE(pvp, 0, e) == EX_TEMPFAIL)
- qup = true;
-
- /*
- ** Build canonical address from pvp.
- */
-
- a = buildaddr(pvp, a, flags, e);
-
- if (hasctrlchar(a->q_user, isrcpt, true))
- {
- if (tTd(20, 1))
- sm_dprintf("parseaddr-->bad q_user\n");
-
- /*
- ** Just mark the address as bad so DSNs work.
- ** hasctrlchar() has to make sure that the address
- ** has been sanitized, e.g., shortened.
- */
-
- a->q_state = QS_BADADDR;
- }
-
- /*
- ** Make local copies of the host & user and then
- ** transport them out.
- */
-
- allocaddr(a, flags, addr, e);
- if (QS_IS_BADADDR(a->q_state))
- {
- /* weed out bad characters in the printable address too */
- (void) hasctrlchar(a->q_paddr, isrcpt, false);
- return a;
- }
-
- /*
- ** Select a queue directory for recipient addresses.
- ** This is done here and in split_across_queue_groups(),
- ** but the latter applies to addresses after aliasing,
- ** and only if splitting is done.
- */
-
- if ((a->q_qgrp == NOAQGRP || a->q_qgrp == ENVQGRP) &&
- !bitset(RF_SENDERADDR|RF_HEADERADDR|RF_RM_ADDR, flags) &&
- OpMode != MD_INITALIAS)
- {
- int r;
-
- /* call ruleset which should return a queue group name */
- r = rscap(RS_QUEUEGROUP, a->q_user, NULL, e, &pvp, pvpbuf,
- sizeof(pvpbuf));
- if (r == EX_OK &&
- pvp != NULL && pvp[0] != NULL &&
- (pvp[0][0] & 0377) == CANONNET &&
- pvp[1] != NULL && pvp[1][0] != '\0')
- {
- r = name2qid(pvp[1]);
- if (r == NOQGRP && LogLevel > 10)
- sm_syslog(LOG_INFO, NOQID,
- "can't find queue group name %s, selection ignored",
- pvp[1]);
- if (tTd(20, 4) && r != NOQGRP)
- sm_syslog(LOG_INFO, NOQID,
- "queue group name %s -> %d",
- pvp[1], r);
- a->q_qgrp = r == NOQGRP ? ENVQGRP : r;
- }
- }
-
- /*
- ** If there was a parsing failure, mark it for queueing.
- */
-
- 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))
- sm_dprintf("parseaddr: queueing message\n");
- message(msg);
- if (e->e_message == NULL && e->e_sendmode != SM_DEFER)
- e->e_message = sm_rpool_strdup_x(e->e_rpool, msg);
- a->q_state = QS_QUEUEUP;
- a->q_status = "4.4.3";
- }
-
- /*
- ** Compute return value.
- */
-
- if (tTd(20, 1))
- {
- sm_dprintf("parseaddr-->");
- printaddr(sm_debug_file(), a, false);
- }
-
- return a;
-}
-/*
-** INVALIDADDR -- check for address containing characters used for macros
-**
-** Parameters:
-** addr -- the address to check.
-** delimptr -- if non-NULL: end of address to check, i.e.,
-** a pointer in the address string.
-** isrcpt -- true iff the address is for a recipient.
-**
-** Returns:
-** true -- if the address has characters that are reservered
-** for macros or is too long.
-** false -- otherwise.
-*/
-
-bool
-invalidaddr(addr, delimptr, isrcpt)
- register char *addr;
- char *delimptr;
- bool isrcpt;
-{
- bool result = false;
- char savedelim = '\0';
- char *b = addr;
- int len = 0;
-
- if (delimptr != NULL)
- {
- /* delimptr points to the end of the address to test */
- savedelim = *delimptr;
- if (savedelim != '\0') /* if that isn't '\0' already: */
- *delimptr = '\0'; /* set it */
- }
- for (; *addr != '\0'; addr++)
- {
- if (!EightBitAddrOK && (*addr & 0340) == 0200)
- {
- setstat(EX_USAGE);
- result = true;
- *addr = BAD_CHAR_REPLACEMENT;
- }
- if (++len > MAXNAME - 1)
- {
- char saved = *addr;
-
- *addr = '\0';
- usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",
- b, MAXNAME - 1);
- *addr = saved;
- result = true;
- goto delim;
- }
- }
- if (result)
- {
- if (isrcpt)
- usrerr("501 5.1.3 8-bit character in mailbox address \"%s\"",
- b);
- else
- usrerr("501 5.1.7 8-bit character in mailbox address \"%s\"",
- b);
- }
-delim:
- if (delimptr != NULL && savedelim != '\0')
- *delimptr = savedelim; /* restore old character at delimptr */
- return result;
-}
-/*
-** HASCTRLCHAR -- check for address containing meta-characters
-**
-** Checks that the address contains no meta-characters, and contains
-** no "non-printable" characters unless they are quoted or escaped.
-** Quoted or escaped characters are literals.
-**
-** Parameters:
-** addr -- the address to check.
-** isrcpt -- true if the address is for a recipient; false
-** indicates a from.
-** complain -- true if an error should issued if the address
-** is invalid and should be "repaired".
-**
-** Returns:
-** true -- if the address has any "wierd" characters or
-** non-printable characters or if a quote is unbalanced.
-** false -- otherwise.
-*/
-
-static bool
-hasctrlchar(addr, isrcpt, complain)
- register char *addr;
- bool isrcpt, complain;
-{
- bool quoted = false;
- int len = 0;
- char *result = NULL;
- char *b = addr;
-
- if (addr == NULL)
- return false;
- for (; *addr != '\0'; addr++)
- {
- if (++len > MAXNAME - 1)
- {
- if (complain)
- {
- (void) shorten_rfc822_string(b, MAXNAME - 1);
- usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",
- b, MAXNAME - 1);
- return true;
- }
- result = "too long";
- }
- if (!EightBitAddrOK && !quoted && (*addr < 32 || *addr == 127))
- {
- result = "non-printable character";
- *addr = BAD_CHAR_REPLACEMENT;
- continue;
- }
- if (*addr == '"')
- quoted = !quoted;
- else if (*addr == '\\')
- {
- /* XXX Generic problem: no '\0' in strings. */
- if (*++addr == '\0')
- {
- result = "trailing \\ character";
- *--addr = BAD_CHAR_REPLACEMENT;
- break;
- }
- }
- if (!EightBitAddrOK && (*addr & 0340) == 0200)
- {
- setstat(EX_USAGE);
- result = "8-bit character";
- *addr = BAD_CHAR_REPLACEMENT;
- continue;
- }
- }
- if (quoted)
- result = "unbalanced quote"; /* unbalanced quote */
- if (result != NULL && complain)
- {
- if (isrcpt)
- usrerr("501 5.1.3 Syntax error in mailbox address \"%s\" (%s)",
- b, result);
- else
- usrerr("501 5.1.7 Syntax error in mailbox address \"%s\" (%s)",
- b, result);
- }
- return result != NULL;
-}
-/*
-** ALLOCADDR -- do local allocations of address on demand.
-**
-** Also lowercases the host name if requested.
-**
-** Parameters:
-** a -- the address to reallocate.
-** flags -- the copy flag (see RF_ definitions in sendmail.h
-** for a description).
-** paddr -- the printname of the address.
-** e -- envelope
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Copies portions of a into local buffers as requested.
-*/
-
-static void
-allocaddr(a, flags, paddr, e)
- register ADDRESS *a;
- int flags;
- char *paddr;
- ENVELOPE *e;
-{
- if (tTd(24, 4))
- sm_dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
-
- a->q_paddr = paddr;
-
- if (a->q_user == NULL)
- a->q_user = "";
- if (a->q_host == NULL)
- a->q_host = "";
-
- if (bitset(RF_COPYPARSE, flags))
- {
- a->q_host = sm_rpool_strdup_x(e->e_rpool, a->q_host);
- if (a->q_user != a->q_paddr)
- a->q_user = sm_rpool_strdup_x(e->e_rpool, a->q_user);
- }
-
- if (a->q_paddr == NULL)
- a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
- a->q_qgrp = NOAQGRP;
-}
-
-/*
-** PRESCAN -- Prescan name and make it canonical
-**
-** Scans a name and turns it into a set of tokens. This process
-** deletes blanks and comments (in parentheses) (if the token type
-** for left paren is SPC).
-**
-** This routine knows about quoted strings and angle brackets.
-**
-** There are certain subtleties to this routine. The one that
-** comes to mind now is that backslashes on the ends of names
-** are silently stripped off; this is intentional. The problem
-** is that some versions of sndmsg (like at LBL) set the kill
-** character to something other than @ when reading addresses;
-** so people type "csvax.eric\@berkeley" -- which screws up the
-** berknet mailer.
-**
-** Parameters:
-** addr -- the name to chomp.
-** delim -- the delimiter for the address, normally
-** '\0' or ','; \0 is accepted in any case.
-** If '\t' then we are reading the .cf file.
-** pvpbuf -- place to put the saved text -- note that
-** the pointers are static.
-** pvpbsize -- size of pvpbuf.
-** delimptr -- if non-NULL, set to the location of the
-** terminating delimiter.
-** toktab -- if set, a token table to use for parsing.
-** If NULL, use the default table.
-** ignore -- if true, ignore unbalanced addresses
-**
-** Returns:
-** A pointer to a vector of tokens.
-** NULL on error.
-*/
-
-/* 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 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 */
-
-static short StateTab[NSTATES][NSTATES] =
-{
- /* oldst chtype> OPR ATM QST SPC ONE ILL */
- /*OPR*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB },
- /*ATM*/ { OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB },
- /*QST*/ { QST, QST, OPR, QST, QST, QST },
- /*SPC*/ { OPR, ATM, QST, SPC|M, ONE, ILL|MB },
- /*ONE*/ { OPR, OPR, OPR, OPR, OPR, ILL|MB },
- /*ILL*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M }
-};
-
-/* these all get modified with the OperatorChars */
-
-/* token type table for external strings */
-unsigned char ExtTokenTab[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, 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 */
- 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 */
- ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,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 ! " # $ % & ' ( ) * + , - . / */
- 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
-};
-
-/* token type table for internal strings */
-unsigned char IntTokenTab[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, 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 */
- 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,ONE
-};
-
-/* token type table for MIME parsing */
-unsigned char MimeTokenTab[256] =
-{
- /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
- ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
- /* 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, 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 */
- OPR,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,OPR,OPR,OPR,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 */
- ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
- /* 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 ! " # $ % & ' ( ) * + , - . / */
- ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
- /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
- ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
- /* @ A B C D E F G H I J K L M N O */
- ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
- /* P Q R S T U V W X Y Z [ \ ] ^ _ */
- ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
- /* ` a b c d e f g h i j k l m n o */
- ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
- /* p q r s t u v w x y z { | } ~ del */
- ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ONE
-};
-
-/* token type table: don't strip comments */
-unsigned char TokTypeNoC[256] =
-{
- /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
- ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
- /* 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,ONE
-};
-
-
-#define NOCHAR (-1) /* signal nothing in lookahead token */
-
-char **
-prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
- char *addr;
- int delim;
- char pvpbuf[];
- int pvpbsize;
- char **delimptr;
- unsigned char *toktab;
- bool ignore;
-{
- register char *p;
- register char *q;
- register int c;
- char **avp;
- bool bslashmode;
- bool route_syntax;
- int cmntcnt;
- int anglecnt;
- char *tok;
- int state;
- int newstate;
- char *saveto = CurEnv->e_to;
- static char *av[MAXATOM + 1];
- static bool firsttime = true;
-
- if (firsttime)
- {
- /* initialize the token type table */
- char obuf[50];
-
- firsttime = false;
- if (OperatorChars == NULL)
- {
- if (ConfigLevel < 7)
- OperatorChars = macvalue('o', CurEnv);
- if (OperatorChars == NULL)
- OperatorChars = ".:@[]";
- }
- expand(OperatorChars, obuf, sizeof(obuf) - sizeof(DELIMCHARS),
- CurEnv);
- (void) sm_strlcat(obuf, DELIMCHARS, sizeof(obuf));
- for (p = obuf; *p != '\0'; p++)
- {
- if (IntTokenTab[*p & 0xff] == ATM)
- IntTokenTab[*p & 0xff] = OPR;
- if (ExtTokenTab[*p & 0xff] == ATM)
- ExtTokenTab[*p & 0xff] = OPR;
- if (TokTypeNoC[*p & 0xff] == ATM)
- TokTypeNoC[*p & 0xff] = OPR;
- }
- }
- if (toktab == NULL)
- toktab = ExtTokenTab;
-
- /* make sure error messages don't have garbage on them */
- errno = 0;
-
- q = pvpbuf;
- bslashmode = false;
- route_syntax = false;
- cmntcnt = 0;
- anglecnt = 0;
- avp = av;
- state = ATM;
- c = NOCHAR;
- p = addr;
- CurEnv->e_to = p;
- if (tTd(22, 11))
- {
- sm_dprintf("prescan: ");
- xputs(sm_debug_file(), p);
- sm_dprintf("\n");
- }
-
- do
- {
- /* read a token */
- tok = q;
- for (;;)
- {
- /* store away any old lookahead character */
- if (c != NOCHAR && !bslashmode)
- {
- /* see if there is room */
- if (q >= &pvpbuf[pvpbsize - 5])
- {
- addrtoolong:
- usrerr("553 5.1.1 Address too long");
- if (strlen(addr) > MAXNAME)
- addr[MAXNAME] = '\0';
- returnnull:
- if (delimptr != NULL)
- {
- if (p > addr)
- --p;
- *delimptr = p;
- }
- CurEnv->e_to = saveto;
- return NULL;
- }
-
- /* squirrel it away */
-#if !ALLOW_255
- if ((char) c == (char) -1 && !tTd(82, 101) &&
- !EightBitAddrOK)
- c &= 0x7f;
-#endif /* !ALLOW_255 */
- *q++ = c;
- }
-
- /* read a new input character */
- c = (*p++) & 0x00ff;
- if (c == '\0')
- {
- /* diagnose and patch up bad syntax */
- if (ignore)
- break;
- else if (state == QST)
- {
- usrerr("553 Unbalanced '\"'");
- c = '"';
- }
- else if (cmntcnt > 0)
- {
- usrerr("553 Unbalanced '('");
- c = ')';
- }
- else if (anglecnt > 0)
- {
- c = '>';
- usrerr("553 Unbalanced '<'");
- }
- else
- break;
-
- p--;
- }
- else if (c == delim && cmntcnt <= 0 && state != QST)
- {
- if (anglecnt <= 0)
- break;
-
- /* special case for better error management */
- if (delim == ',' && !route_syntax && !ignore)
- {
- usrerr("553 Unbalanced '<'");
- c = '>';
- p--;
- }
- }
-
- if (tTd(22, 101))
- sm_dprintf("c=%c, s=%d; ", c, state);
-
- /* chew up special characters */
- *q = '\0';
- if (bslashmode)
- {
- bslashmode = false;
-
- /* kludge \! for naive users */
- if (cmntcnt > 0)
- {
- c = NOCHAR;
- continue;
- }
- else if (c != '!' || state == QST)
- {
- /* see if there is room */
- if (q >= &pvpbuf[pvpbsize - 5])
- goto addrtoolong;
- *q++ = '\\';
- continue;
- }
- }
-
- if (c == '\\')
- {
- bslashmode = true;
- }
- else if (state == QST)
- {
- /* EMPTY */
- /* do nothing, just avoid next clauses */
- }
- else if (c == '(' && toktab['('] == SPC)
- {
- cmntcnt++;
- c = NOCHAR;
- }
- else if (c == ')' && toktab['('] == SPC)
- {
- if (cmntcnt <= 0)
- {
- if (!ignore)
- {
- usrerr("553 Unbalanced ')'");
- c = NOCHAR;
- }
- }
- else
- cmntcnt--;
- }
- else if (cmntcnt > 0)
- {
- c = NOCHAR;
- }
- else if (c == '<')
- {
- char *ptr = p;
-
- anglecnt++;
- while (isascii(*ptr) && isspace(*ptr))
- ptr++;
- if (*ptr == '@')
- route_syntax = true;
- }
- else if (c == '>')
- {
- if (anglecnt <= 0)
- {
- if (!ignore)
- {
- usrerr("553 Unbalanced '>'");
- c = NOCHAR;
- }
- }
- else
- anglecnt--;
- route_syntax = false;
- }
- else if (delim == ' ' && isascii(c) && isspace(c))
- c = ' ';
-
- if (c == NOCHAR)
- continue;
-
- /* see if this is end of input */
- if (c == delim && anglecnt <= 0 && state != QST)
- break;
-
- newstate = StateTab[state][toktab[c & 0xff]];
- if (tTd(22, 101))
- sm_dprintf("ns=%02o\n", newstate);
- state = newstate & TYPE;
- if (state == ILL)
- {
- if (isascii(c) && isprint(c))
- usrerr("553 Illegal character %c", c);
- else
- usrerr("553 Illegal character 0x%02x",
- c & 0x0ff);
- }
- if (bitset(M, newstate))
- c = NOCHAR;
- if (bitset(B, newstate))
- break;
- }
-
- /* new token */
- if (tok != q)
- {
- /* see if there is room */
- if (q >= &pvpbuf[pvpbsize - 5])
- goto addrtoolong;
- *q++ = '\0';
- if (tTd(22, 36))
- {
- sm_dprintf("tok=");
- xputs(sm_debug_file(), tok);
- sm_dprintf("\n");
- }
- if (avp >= &av[MAXATOM])
- {
- usrerr("553 5.1.0 prescan: too many tokens");
- goto returnnull;
- }
- if (q - tok > MAXNAME)
- {
- usrerr("553 5.1.0 prescan: token too long");
- goto returnnull;
- }
- *avp++ = tok;
- }
- } while (c != '\0' && (c != delim || anglecnt > 0));
- *avp = NULL;
- if (delimptr != NULL)
- {
- if (p > addr)
- p--;
- *delimptr = p;
- }
- if (tTd(22, 12))
- {
- sm_dprintf("prescan==>");
- printav(sm_debug_file(), av);
- }
- CurEnv->e_to = saveto;
- if (av[0] == NULL)
- {
- if (tTd(22, 1))
- sm_dprintf("prescan: null leading token\n");
- return NULL;
- }
- return av;
-}
-/*
-** REWRITE -- apply rewrite rules to token vector.
-**
-** This routine is an ordered production system. Each rewrite
-** rule has a LHS (called the pattern) and a RHS (called the
-** rewrite); 'rwr' points the the current rewrite rule.
-**
-** For each rewrite rule, 'avp' points the address vector we
-** are trying to match against, and 'pvp' points to the pattern.
-** If pvp points to a special match value (MATCHZANY, MATCHANY,
-** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
-** matched is saved away in the match vector (pointed to by 'mvp').
-**
-** When a match between avp & pvp does not match, we try to
-** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
-** we must also back out the match in mvp. If we reach a
-** MATCHANY or MATCHZANY we just extend the match and start
-** over again.
-**
-** When we finally match, we rewrite the address vector
-** and try over again.
-**
-** Parameters:
-** pvp -- pointer to token vector.
-** ruleset -- the ruleset to use for rewriting.
-** reclevel -- recursion level (to catch loops).
-** e -- the current envelope.
-** maxatom -- maximum length of buffer (usually MAXATOM)
-**
-** Returns:
-** A status code. If EX_TEMPFAIL, higher level code should
-** attempt recovery.
-**
-** Side Effects:
-** pvp is modified.
-*/
-
-struct match
-{
- char **match_first; /* first token matched */
- char **match_last; /* last token matched */
- char **match_pattern; /* pointer to pattern */
-};
-
-int
-rewrite(pvp, ruleset, reclevel, e, maxatom)
- char **pvp;
- int ruleset;
- int reclevel;
- register ENVELOPE *e;
- int maxatom;
-{
- register char *ap; /* address pointer */
- register char *rp; /* rewrite pointer */
- 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 */
- register struct rewrite *rwr; /* pointer to current rewrite rule */
- int ruleno; /* current rule number */
- 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 buf[MAXLINE];
- char name[6];
-
- /*
- ** mlp will not exceed mlist[] because readcf enforces
- ** the upper limit of entries when reading rulesets.
- */
-
- 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)
- {
- (void) sm_snprintf(name, sizeof(name), "%d", ruleset);
- rulename = name;
- }
- if (OpMode == MD_TEST)
- prefix = "";
- else
- prefix = "rewrite: ruleset ";
- if (OpMode == MD_TEST)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "%s%-16.16s input:", prefix, rulename);
- printav(smioout, pvp);
- }
- else if (tTd(21, 1))
- {
- sm_dprintf("%s%-16.16s input:", prefix, rulename);
- printav(sm_debug_file(), pvp);
- }
- if (reclevel++ > MaxRuleRecursion)
- {
- syserr("rewrite: excessive recursion (max %d), ruleset %s",
- MaxRuleRecursion, rulename);
- return EX_CONFIG;
- }
- if (pvp == NULL)
- return EX_USAGE;
- if (maxatom <= 0)
- return EX_USAGE;
-
- /*
- ** Run through the list of rewrite rules, applying
- ** any that match.
- */
-
- ruleno = 1;
- loopcount = 0;
- for (rwr = RewriteRules[ruleset]; rwr != NULL; )
- {
- int status;
-
- /* if already canonical, quit now */
- if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
- break;
-
- if (tTd(21, 12))
- {
- if (tTd(21, 15))
- sm_dprintf("-----trying rule (line %d):",
- rwr->r_line);
- else
- sm_dprintf("-----trying rule:");
- printav(sm_debug_file(), rwr->r_lhs);
- }
-
- /* try to match on this rule */
- mlp = mlist;
- rvp = rwr->r_lhs;
- avp = pvp;
- if (++loopcount > 100)
- {
- syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d",
- rulename, ruleno);
- if (tTd(21, 1))
- {
- sm_dprintf("workspace: ");
- printav(sm_debug_file(), pvp);
- }
- break;
- }
-
- while ((ap = *avp) != NULL || *rvp != NULL)
- {
- rp = *rvp;
- if (tTd(21, 35))
- {
- sm_dprintf("ADVANCE rp=");
- xputs(sm_debug_file(), rp);
- sm_dprintf(", ap=");
- xputs(sm_debug_file(), ap);
- sm_dprintf("\n");
- }
- if (rp == NULL)
- {
- /* end-of-pattern before end-of-address */
- goto backup;
- }
- if (ap == NULL &&
- (rp[0] & 0377) != MATCHZANY &&
- (rp[0] & 0377) != MATCHZERO)
- {
- /* end-of-input with patterns left */
- goto backup;
- }
-
- switch (rp[0] & 0377)
- {
- case MATCHCLASS:
- /* match any phrase in a class */
- mlp->match_pattern = rvp;
- mlp->match_first = avp;
- extendclass:
- ap = *avp;
- if (ap == NULL)
- goto backup;
- mlp->match_last = avp++;
- cataddr(mlp->match_first, mlp->match_last,
- buf, sizeof(buf), '\0', true);
- if (!wordinclass(buf, rp[1]))
- {
- if (tTd(21, 36))
- {
- sm_dprintf("EXTEND rp=");
- xputs(sm_debug_file(), rp);
- sm_dprintf(", ap=");
- xputs(sm_debug_file(), ap);
- sm_dprintf("\n");
- }
- goto extendclass;
- }
- if (tTd(21, 36))
- sm_dprintf("CLMATCH\n");
- mlp++;
- break;
-
- case MATCHNCLASS:
- /* match any token not in a class */
- if (wordinclass(ap, rp[1]))
- goto backup;
-
- /* FALLTHROUGH */
-
- case MATCHONE:
- case MATCHANY:
- /* match exactly one token */
- mlp->match_pattern = rvp;
- mlp->match_first = avp;
- mlp->match_last = avp++;
- mlp++;
- break;
-
- case MATCHZANY:
- /* match zero or more tokens */
- mlp->match_pattern = rvp;
- mlp->match_first = avp;
- mlp->match_last = avp - 1;
- mlp++;
- break;
-
- case MATCHZERO:
- /* match zero tokens */
- break;
-
- case MACRODEXPAND:
- /*
- ** Match against run-time macro.
- ** This algorithm is broken for the
- ** general case (no recursive macros,
- ** improper tokenization) but should
- ** work for the usual cases.
- */
-
- ap = macvalue(rp[1], e);
- mlp->match_first = avp;
- if (tTd(21, 2))
- sm_dprintf("rewrite: LHS $&{%s} => \"%s\"\n",
- macname(rp[1]),
- ap == NULL ? "(NULL)" : ap);
-
- if (ap == NULL)
- break;
- while (*ap != '\0')
- {
- if (*avp == NULL ||
- sm_strncasecmp(ap, *avp,
- strlen(*avp)) != 0)
- {
- /* no match */
- avp = mlp->match_first;
- goto backup;
- }
- ap += strlen(*avp++);
- }
-
- /* match */
- break;
-
- default:
- /* must have exact match */
- if (sm_strcasecmp(rp, ap))
- goto backup;
- avp++;
- break;
- }
-
- /* successful match on this token */
- rvp++;
- continue;
-
- backup:
- /* match failed -- back up */
- while (--mlp >= mlist)
- {
- rvp = mlp->match_pattern;
- rp = *rvp;
- avp = mlp->match_last + 1;
- ap = *avp;
-
- if (tTd(21, 36))
- {
- sm_dprintf("BACKUP rp=");
- xputs(sm_debug_file(), rp);
- sm_dprintf(", ap=");
- xputs(sm_debug_file(), ap);
- sm_dprintf("\n");
- }
-
- if (ap == NULL)
- {
- /* run off the end -- back up again */
- continue;
- }
-
- if ((rp[0] & 0377) == MATCHANY ||
- (rp[0] & 0377) == MATCHZANY)
- {
- /* extend binding and continue */
- mlp->match_last = avp++;
- rvp++;
- mlp++;
- break;
- }
- if ((rp[0] & 0377) == MATCHCLASS)
- {
- /* extend binding and try again */
- mlp->match_last = avp;
- goto extendclass;
- }
- }
-
- if (mlp < mlist)
- {
- /* total failure to match */
- break;
- }
- }
-
- /*
- ** See if we successfully matched
- */
-
- if (mlp < mlist || *rvp != NULL)
- {
- if (tTd(21, 10))
- sm_dprintf("----- rule fails\n");
- rwr = rwr->r_next;
- ruleno++;
- loopcount = 0;
- continue;
- }
-
- rvp = rwr->r_rhs;
- if (tTd(21, 12))
- {
- sm_dprintf("-----rule matches:");
- printav(sm_debug_file(), rvp);
- }
-
- rp = *rvp;
- if (rp != NULL)
- {
- if ((rp[0] & 0377) == CANONUSER)
- {
- rvp++;
- rwr = rwr->r_next;
- ruleno++;
- loopcount = 0;
- }
- else if ((rp[0] & 0377) == CANONHOST)
- {
- rvp++;
- rwr = NULL;
- }
- }
-
- /* substitute */
- for (avp = npvp; *rvp != NULL; rvp++)
- {
- register struct match *m;
- register char **pp;
-
- rp = *rvp;
- if ((rp[0] & 0377) == MATCHREPL)
- {
- /* substitute from LHS */
- m = &mlist[rp[1] - '1'];
- if (m < mlist || m >= mlp)
- {
- syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds",
- rulename, rp[1]);
- return EX_CONFIG;
- }
- if (tTd(21, 15))
- {
- sm_dprintf("$%c:", rp[1]);
- pp = m->match_first;
- while (pp <= m->match_last)
- {
- sm_dprintf(" %p=\"", *pp);
- sm_dflush();
- sm_dprintf("%s\"", *pp++);
- }
- sm_dprintf("\n");
- }
- pp = m->match_first;
- while (pp <= m->match_last)
- {
- if (avp >= &npvp[maxatom])
- goto toolong;
- *avp++ = *pp++;
- }
- }
- else
- {
- /* some sort of replacement */
- if (avp >= &npvp[maxatom])
- {
- toolong:
- syserr("554 5.3.0 rewrite: expansion too long");
- if (LogLevel > 9)
- sm_syslog(LOG_ERR, e->e_id,
- "rewrite: expansion too long, ruleset=%s, ruleno=%d",
- rulename, ruleno);
- return EX_DATAERR;
- }
- if ((rp[0] & 0377) != MACRODEXPAND)
- {
- /* vanilla replacement from RHS */
- *avp++ = rp;
- }
- else
- {
- /* $&{x} replacement */
- char *mval = macvalue(rp[1], e);
- char **xpvp;
- size_t trsize = 0;
- static size_t pvpb1_size = 0;
- static char **pvpb1 = NULL;
- char pvpbuf[PSBUFSIZE];
-
- if (tTd(21, 2))
- sm_dprintf("rewrite: RHS $&{%s} => \"%s\"\n",
- macname(rp[1]),
- mval == NULL ? "(NULL)" : mval);
- if (mval == NULL || *mval == '\0')
- continue;
-
- /* save the remainder of the input */
- for (xpvp = pvp; *xpvp != NULL; xpvp++)
- trsize += sizeof(*xpvp);
- if (trsize > pvpb1_size)
- {
- if (pvpb1 != NULL)
- sm_free(pvpb1);
- pvpb1 = (char **)
- sm_pmalloc_x(trsize);
- pvpb1_size = trsize;
- }
-
- memmove((char *) pvpb1,
- (char *) pvp,
- trsize);
-
- /* scan the new replacement */
- xpvp = prescan(mval, '\0', pvpbuf,
- sizeof(pvpbuf), NULL,
- NULL, false);
- if (xpvp == NULL)
- {
- /* prescan pre-printed error */
- return EX_DATAERR;
- }
-
- /* insert it into the output stream */
- while (*xpvp != NULL)
- {
- if (tTd(21, 19))
- sm_dprintf(" ... %s\n",
- *xpvp);
- *avp++ = sm_rpool_strdup_x(
- e->e_rpool, *xpvp);
- if (avp >= &npvp[maxatom])
- goto toolong;
- xpvp++;
- }
- if (tTd(21, 19))
- sm_dprintf(" ... DONE\n");
-
- /* restore the old trailing input */
- memmove((char *) pvp,
- (char *) pvpb1,
- trsize);
- }
- }
- }
- *avp++ = NULL;
-
- /*
- ** Check for any hostname/keyword lookups.
- */
-
- for (rvp = npvp; *rvp != NULL; rvp++)
- {
- char **hbrvp;
- char **xpvp;
- size_t trsize;
- char *replac;
- int endtoken;
- STAB *map;
- char *mapname;
- char **key_rvp;
- char **arg_rvp;
- char **default_rvp;
- char cbuf[MAXKEY];
- char *pvpb1[MAXATOM + 1];
- char *argvect[MAX_MAP_ARGS];
- char pvpbuf[PSBUFSIZE];
- char *nullpvp[1];
-
- hbrvp = rvp;
- if ((rvp[0][0] & 0377) == HOSTBEGIN)
- {
- endtoken = HOSTEND;
- mapname = "host";
- }
- else if ((rvp[0][0] & 0377) == LOOKUPBEGIN)
- {
- endtoken = LOOKUPEND;
- mapname = *++rvp;
- if (mapname == NULL)
- {
- syserr("554 5.3.0 rewrite: missing mapname");
- /* NOTREACHED */
- SM_ASSERT(0);
- }
- }
- else
- continue;
-
- /*
- ** Got a hostname/keyword lookup.
- **
- ** This could be optimized fairly easily.
- */
-
- map = stab(mapname, ST_MAP, ST_FIND);
- if (map == NULL)
- syserr("554 5.3.0 rewrite: map %s not found",
- mapname);
-
- /* extract the match part */
- key_rvp = ++rvp;
- if (key_rvp == NULL)
- {
- syserr("554 5.3.0 rewrite: missing key for map %s",
- mapname);
- /* NOTREACHED */
- SM_ASSERT(0);
- }
- default_rvp = NULL;
- arg_rvp = argvect;
- xpvp = NULL;
- replac = pvpbuf;
- while (*rvp != NULL && ((rvp[0][0] & 0377) != endtoken))
- {
- int nodetype = rvp[0][0] & 0377;
-
- if (nodetype != CANONHOST &&
- nodetype != CANONUSER)
- {
- rvp++;
- continue;
- }
-
- *rvp++ = NULL;
-
- if (xpvp != NULL)
- {
- cataddr(xpvp, NULL, replac,
- &pvpbuf[sizeof(pvpbuf)] - replac,
- '\0', false);
- if (arg_rvp <
- &argvect[MAX_MAP_ARGS - 1])
- *++arg_rvp = replac;
- replac += strlen(replac) + 1;
- xpvp = NULL;
- }
- switch (nodetype)
- {
- case CANONHOST:
- xpvp = rvp;
- break;
-
- case CANONUSER:
- default_rvp = rvp;
- break;
- }
- }
- if (*rvp != NULL)
- *rvp++ = NULL;
- if (xpvp != NULL)
- {
- cataddr(xpvp, NULL, replac,
- &pvpbuf[sizeof(pvpbuf)] - replac,
- '\0', false);
- if (arg_rvp < &argvect[MAX_MAP_ARGS - 1])
- *++arg_rvp = replac;
- }
- if (arg_rvp >= &argvect[MAX_MAP_ARGS - 1])
- argvect[MAX_MAP_ARGS - 1] = NULL;
- else
- *++arg_rvp = NULL;
-
- /* save the remainder of the input string */
- trsize = (avp - rvp + 1) * sizeof(*rvp);
- memmove((char *) pvpb1, (char *) rvp, trsize);
-
- /* look it up */
- cataddr(key_rvp, NULL, cbuf, sizeof(cbuf),
- map == NULL ? '\0' : map->s_map.map_spacesub,
- true);
- 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, cbuf, sizeof(cbuf),
- '\0', false);
- replac = cbuf;
- }
-
- if (replac == NULL)
- {
- xpvp = key_rvp;
- }
- else if (*replac == '\0')
- {
- /* null replacement */
- nullpvp[0] = NULL;
- xpvp = nullpvp;
- }
- else
- {
- /* scan the new replacement */
- xpvp = prescan(replac, '\0', pvpbuf,
- sizeof(pvpbuf), NULL, NULL,
- false);
- if (xpvp == NULL)
- {
- /* prescan already printed error */
- return EX_DATAERR;
- }
- }
-
- /* append it to the token list */
- for (avp = hbrvp; *xpvp != NULL; xpvp++)
- {
- *avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp);
- if (avp >= &npvp[maxatom])
- goto toolong;
- }
-
- /* restore the old trailing information */
- rvp = avp - 1;
- for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
- if (avp >= &npvp[maxatom])
- goto toolong;
- }
-
- /*
- ** Check for subroutine calls.
- */
-
- 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;
- memmove((char *) pvp, (char *) npvp,
- (int) (avp - npvp) * sizeof(*avp));
-
- if (tTd(21, 4))
- {
- sm_dprintf("rewritten as:");
- printav(sm_debug_file(), pvp);
- }
- }
-
- if (OpMode == MD_TEST)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "%s%-16.16s returns:", prefix, rulename);
- printav(smioout, pvp);
- }
- else if (tTd(21, 1))
- {
- sm_dprintf("%s%-16.16s returns:", prefix, rulename);
- printav(sm_debug_file(), pvp);
- }
- return rstat;
-}
-/*
-** CALLSUBR -- call subroutines in rewrite vector
-**
-** Parameters:
-** pvp -- pointer to token vector.
-** reclevel -- the current recursion level.
-** e -- the current envelope.
-**
-** Returns:
-** The status from the subroutine call.
-**
-** Side Effects:
-** pvp is modified.
-*/
-
-static int
-callsubr(pvp, reclevel, e)
- char **pvp;
- int reclevel;
- ENVELOPE *e;
-{
- char **avp;
- register int i;
- int subr, j;
- int nsubr;
- int status;
- int rstat = EX_OK;
-#define MAX_SUBR 16
- int subrnumber[MAX_SUBR];
- int subrindex[MAX_SUBR];
-
- nsubr = 0;
-
- /*
- ** Look for subroutine calls in pvp, collect them into subr*[]
- ** We will perform the calls in the next loop, because we will
- ** call the "last" subroutine first to avoid recursive calls
- ** and too much copying.
- */
-
- for (avp = pvp, j = 0; *avp != NULL; avp++, j++)
- {
- if ((avp[0][0] & 0377) == CALLSUBR && avp[1] != NULL)
- {
- stripquotes(avp[1]);
- subr = strtorwset(avp[1], NULL, ST_FIND);
- if (subr < 0)
- {
- syserr("554 5.3.5 Unknown ruleset %s", avp[1]);
- return EX_CONFIG;
- }
-
- /*
- ** XXX instead of doing this we could optimize
- ** the rules after reading them: just remove
- ** calls to empty rulesets
- */
-
- /* subroutine is an empty ruleset? don't call it */
- if (RewriteRules[subr] == NULL)
- {
- if (tTd(21, 3))
- sm_dprintf("-----skip subr %s (%d)\n",
- avp[1], subr);
- for (i = 2; avp[i] != NULL; i++)
- avp[i - 2] = avp[i];
- avp[i - 2] = NULL;
- continue;
- }
- if (++nsubr >= MAX_SUBR)
- {
- syserr("554 5.3.0 Too many subroutine calls (%d max)",
- MAX_SUBR);
- return EX_CONFIG;
- }
- subrnumber[nsubr] = subr;
- subrindex[nsubr] = j;
- }
- }
-
- /*
- ** Perform the actual subroutines calls, "last" one first, i.e.,
- ** go from the right to the left through all calls,
- ** do the rewriting in place.
- */
-
- for (; nsubr > 0; nsubr--)
- {
- subr = subrnumber[nsubr];
- avp = pvp + subrindex[nsubr];
-
- /* remove the subroutine call and name */
- for (i = 2; avp[i] != NULL; i++)
- avp[i - 2] = avp[i];
- avp[i - 2] = NULL;
-
- /*
- ** Now we need to call the ruleset specified for
- ** the subroutine. We can do this in place since
- ** we call the "last" subroutine first.
- */
-
- status = rewrite(avp, subr, reclevel, e,
- MAXATOM - subrindex[nsubr]);
- if (status != EX_OK && status != EX_TEMPFAIL)
- return status;
- if (rstat == EX_OK || status == EX_TEMPFAIL)
- rstat = status;
- }
- return rstat;
-}
-/*
-** MAP_LOOKUP -- do lookup in map
-**
-** Parameters:
-** smap -- the map to use for the lookup.
-** key -- the key to look up.
-** argvect -- arguments to pass to the map lookup.
-** pstat -- a pointer to an integer in which to store the
-** status from the lookup.
-** e -- the current envelope.
-**
-** Returns:
-** The result of the lookup.
-** NULL -- if there was no data for the given key.
-*/
-
-static char *
-map_lookup(smap, key, argvect, pstat, e)
- STAB *smap;
- char key[];
- char **argvect;
- int *pstat;
- ENVELOPE *e;
-{
- auto int status = EX_OK;
- MAP *map;
- char *replac;
-
- 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))
- sm_dprintf("map_lookup(%s, %s) => DEFERRED\n",
- smap->s_name, key);
- *pstat = EX_TEMPFAIL;
- return NULL;
- }
-
- if (!bitset(MF_KEEPQUOTES, map->map_mflags))
- stripquotes(key);
-
- if (tTd(60, 1))
- {
- sm_dprintf("map_lookup(%s, ", smap->s_name);
- xputs(sm_debug_file(), key);
- if (tTd(60, 5))
- {
- int i;
-
- for (i = 0; argvect[i] != NULL; i++)
- sm_dprintf(", %%%d=%s", i, argvect[i]);
- }
- sm_dprintf(") => ");
- }
- replac = (*map->map_class->map_lookup)(map, key, argvect, &status);
- if (tTd(60, 1))
- sm_dprintf("%s (%d)\n",
- replac != NULL ? replac : "NOT FOUND",
- status);
-
- /* should recover if status == EX_TEMPFAIL */
- if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags))
- {
- *pstat = EX_TEMPFAIL;
- if (tTd(60, 1))
- sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",
- smap->s_name, key, errno);
- if (e->e_message == NULL)
- {
- char mbuf[320];
-
- (void) sm_snprintf(mbuf, sizeof(mbuf),
- "%.80s map: lookup (%s): deferred",
- smap->s_name,
- shortenstring(key, MAXSHORTSTR));
- e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf);
- }
- }
- if (status == EX_TEMPFAIL && map->map_tapp != NULL)
- {
- size_t i = strlen(key) + strlen(map->map_tapp) + 1;
- static char *rwbuf = NULL;
- static size_t rwbuflen = 0;
-
- if (i > rwbuflen)
- {
- if (rwbuf != NULL)
- sm_free(rwbuf);
- rwbuflen = i;
- rwbuf = (char *) sm_pmalloc_x(rwbuflen);
- }
- (void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp);
- if (tTd(60, 4))
- sm_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:
-** tv -- token vector.
-** a -- pointer to address descriptor to fill.
-** If NULL, one will be allocated.
-** flags -- info regarding whether this is a sender or
-** a recipient.
-** e -- the current envelope.
-**
-** Returns:
-** NULL if there was an error.
-** 'a' otherwise.
-**
-** Side Effects:
-** fills in 'a'
-*/
-
-static struct errcodes
-{
- char *ec_name; /* name of error code */
- int ec_code; /* numeric code */
-} ErrorCodes[] =
-{
- { "usage", EX_USAGE },
- { "nouser", EX_NOUSER },
- { "nohost", EX_NOHOST },
- { "unavailable", EX_UNAVAILABLE },
- { "software", EX_SOFTWARE },
- { "tempfail", EX_TEMPFAIL },
- { "protocol", EX_PROTOCOL },
- { "config", EX_CONFIG },
- { NULL, EX_UNAVAILABLE }
-};
-
-static ADDRESS *
-buildaddr(tv, a, flags, e)
- register char **tv;
- register ADDRESS *a;
- int flags;
- register ENVELOPE *e;
-{
- bool tempfail = false;
- int maxatom;
- struct mailer **mp;
- register struct mailer *m;
- register char *p;
- char *mname;
- char **hostp;
- char hbuf[MAXNAME + 1];
- static char ubuf[MAXNAME + 2];
-
- if (tTd(24, 5))
- {
- sm_dprintf("buildaddr, flags=%x, tv=", flags);
- printav(sm_debug_file(), tv);
- }
-
- maxatom = MAXATOM;
- if (a == NULL)
- a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a));
- memset((char *) a, '\0', sizeof(*a));
- hbuf[0] = '\0';
-
- /* set up default error return flags */
- a->q_flags |= DefaultNotify;
-
- /* figure out what net/mailer to use */
- if (*tv == NULL || (**tv & 0377) != CANONNET)
- {
- syserr("554 5.3.5 buildaddr: no mailer in parsed address");
-badaddr:
- /*
- ** ExitStat may have been set by an earlier map open
- ** failure (to a permanent error (EX_OSERR) in syserr())
- ** so we also need to check if this particular $#error
- ** return wanted a 4XX failure.
- **
- ** XXX the real fix is probably to set ExitStat correctly,
- ** i.e., to EX_TEMPFAIL if the map open is just a temporary
- ** error.
- */
-
- if (ExitStat == EX_TEMPFAIL || tempfail)
- a->q_state = QS_QUEUEUP;
- else
- {
- a->q_state = QS_BADADDR;
- a->q_mailer = &errormailer;
- }
- return a;
- }
- mname = *++tv;
- --maxatom;
-
- /* extract host and user portions */
- if (*++tv != NULL && (**tv & 0377) == CANONHOST)
- {
- hostp = ++tv;
- --maxatom;
- }
- else
- hostp = NULL;
- --maxatom;
- while (*tv != NULL && (**tv & 0377) != CANONUSER)
- {
- tv++;
- --maxatom;
- }
- if (*tv == NULL)
- {
- syserr("554 5.3.5 buildaddr: no user");
- goto badaddr;
- }
- if (tv == hostp)
- hostp = NULL;
- else if (hostp != NULL)
- cataddr(hostp, tv - 1, hbuf, sizeof(hbuf), '\0', false);
- cataddr(++tv, NULL, ubuf, sizeof(ubuf), ' ', false);
- --maxatom;
-
- /* save away the host name */
- if (sm_strcasecmp(mname, "error") == 0)
- {
- /* Set up triplet for use by -bv */
- a->q_mailer = &errormailer;
- a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
- /* XXX wrong place? */
-
- if (hostp != NULL)
- {
- register struct errcodes *ep;
-
- a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
- if (strchr(hbuf, '.') != NULL)
- {
- a->q_status = sm_rpool_strdup_x(e->e_rpool,
- hbuf);
- setstat(dsntoexitstat(hbuf));
- }
- else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
- {
- setstat(atoi(hbuf));
- }
- else
- {
- for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
- if (sm_strcasecmp(ep->ec_name, hbuf) == 0)
- break;
- setstat(ep->ec_code);
- }
- }
- else
- {
- a->q_host = NULL;
- setstat(EX_UNAVAILABLE);
- }
- stripquotes(ubuf);
- if (ISSMTPCODE(ubuf) && ubuf[3] == ' ')
- {
- char fmt[16];
- int off;
-
- if ((off = isenhsc(ubuf + 4, ' ')) > 0)
- {
- ubuf[off + 4] = '\0';
- off += 5;
- }
- else
- {
- off = 4;
- ubuf[3] = '\0';
- }
- (void) sm_strlcpyn(fmt, sizeof(fmt), 2, ubuf, " %s");
- 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] = ' '; */
- if (ubuf[0] == '4')
- tempfail = true;
- }
- else
- {
- usrerr("553 5.3.0 %s", ubuf);
- }
- goto badaddr;
- }
-
- for (mp = Mailer; (m = *mp++) != NULL; )
- {
- if (sm_strcasecmp(m->m_name, mname) == 0)
- break;
- }
- if (m == NULL)
- {
- syserr("554 5.3.5 buildaddr: unknown mailer %s", mname);
- goto badaddr;
- }
- a->q_mailer = m;
-
- /* figure out what host (if any) */
- if (hostp == NULL)
- {
- if (!bitnset(M_LOCALMAILER, m->m_flags))
- {
- syserr("554 5.3.5 buildaddr: no host");
- goto badaddr;
- }
- a->q_host = NULL;
- }
- else
- a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
-
- /* figure out the user */
- p = ubuf;
- if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
- {
- p++;
- tv++;
- --maxatom;
- a->q_flags |= QNOTREMOTE;
- }
-
- /* do special mapping for local mailer */
- if (*p == '"')
- p++;
- if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
- a->q_mailer = m = ProgMailer;
- else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
- a->q_mailer = m = FileMailer;
- else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
- {
- /* may be :include: */
- stripquotes(ubuf);
- if (sm_strncasecmp(ubuf, ":include:", 9) == 0)
- {
- /* if :include:, don't need further rewriting */
- a->q_mailer = m = InclMailer;
- a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]);
- return a;
- }
- }
-
- /* rewrite according recipient mailer rewriting rules */
- macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
-
- if (ConfigLevel >= 10 ||
- !bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
- {
- /* sender addresses done later */
- (void) rewrite(tv, 2, 0, e, maxatom);
- if (m->m_re_rwset > 0)
- (void) rewrite(tv, m->m_re_rwset, 0, e, maxatom);
- }
- (void) rewrite(tv, 4, 0, e, maxatom);
-
- /* save the result for the command line/RCPT argument */
- cataddr(tv, NULL, ubuf, sizeof(ubuf), '\0', true);
- a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
-
- /*
- ** Do mapping to lower case as requested by mailer
- */
-
- if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
- makelower(a->q_host);
- if (!bitnset(M_USR_UPPER, m->m_flags))
- makelower(a->q_user);
-
- if (tTd(24, 6))
- {
- sm_dprintf("buildaddr => ");
- printaddr(sm_debug_file(), a, false);
- }
- return a;
-}
-
-/*
-** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
-**
-** Parameters:
-** pvp -- parameter vector to rebuild.
-** evp -- last parameter to include. Can be NULL to
-** use entire pvp.
-** buf -- buffer to build the string into.
-** sz -- size of buf.
-** spacesub -- the space separator character; if '\0',
-** use SpaceSub.
-** external -- convert to external form?
-** (no metacharacters; METAQUOTEs removed, see below)
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Destroys buf.
-**
-** Notes:
-** There are two formats for strings: internal and external.
-** The external format is just an eight-bit clean string (no
-** null bytes, everything else OK). The internal format can
-** include sendmail metacharacters. The special character
-** METAQUOTE essentially quotes the character following, stripping
-** it of all special semantics.
-**
-** The cataddr routine needs to be aware of whether it is producing
-** an internal or external form as output (it only takes internal
-** form as input).
-**
-** The parseaddr routine has a similar issue on input, but that
-** is flagged on the basis of which token table is passed in.
-*/
-
-void
-cataddr(pvp, evp, buf, sz, spacesub, external)
- char **pvp;
- char **evp;
- char *buf;
- register int sz;
- int spacesub;
- bool external;
-{
- bool oatomtok, natomtok;
- char *p;
-
- oatomtok = natomtok = false;
- if (tTd(59, 14))
- {
- sm_dprintf("cataddr(%d) <==", external);
- printav(sm_debug_file(), pvp);
- }
-
- if (sz <= 0)
- return;
-
- if (spacesub == '\0')
- spacesub = SpaceSub;
-
- if (pvp == NULL)
- {
- *buf = '\0';
- return;
- }
- p = buf;
- sz -= 2;
- while (*pvp != NULL && sz > 0)
- {
- char *q;
-
- natomtok = (ExtTokenTab[**pvp & 0xff] == ATM);
- if (oatomtok && natomtok)
- {
- *p++ = spacesub;
- if (--sz <= 0)
- break;
- }
- for (q = *pvp; *q != '\0'; )
- {
- int c;
-
- if (--sz <= 0)
- break;
- *p++ = c = *q++;
-
- /*
- ** If the current character (c) is METAQUOTE and we
- ** want the "external" form and the next character
- ** is not NUL, then overwrite METAQUOTE with that
- ** character (i.e., METAQUOTE ch is changed to
- ** ch). p[-1] is used because p is advanced (above).
- */
-
- if ((c & 0377) == METAQUOTE && external && *q != '\0')
- p[-1] = *q++;
- }
- if (sz <= 0)
- break;
- oatomtok = natomtok;
- if (pvp++ == evp)
- break;
- }
-
-#if 0
- /*
- ** Silently truncate long strings: even though this doesn't
- ** seem like a good idea it is necessary because header checks
- ** send the whole header value to rscheck() and hence rewrite().
- ** The latter however sometimes uses a "short" buffer (e.g.,
- ** cbuf[MAXNAME + 1]) to call cataddr() which then triggers this
- ** error function. One possible fix to the problem is to pass
- ** flags to rscheck() and rewrite() to distinguish the various
- ** calls and only trigger the error if necessary. For now just
- ** undo the change from 8.13.0.
- */
-
- if (sz <= 0)
- usrerr("cataddr: string too long");
-#endif
- *p = '\0';
-
- if (tTd(59, 14))
- sm_dprintf(" cataddr => %s\n", str2prt(buf));
-}
-
-/*
-** SAMEADDR -- Determine if two addresses are the same
-**
-** This is not just a straight comparison -- if the mailer doesn't
-** care about the host we just ignore it, etc.
-**
-** Parameters:
-** a, b -- pointers to the internal forms to compare.
-**
-** Returns:
-** true -- they represent the same mailbox.
-** false -- they don't.
-**
-** Side Effects:
-** none.
-*/
-
-bool
-sameaddr(a, b)
- register ADDRESS *a;
- register ADDRESS *b;
-{
- register ADDRESS *ca, *cb;
-
- /* if they don't have the same mailer, forget it */
- if (a->q_mailer != b->q_mailer)
- return false;
-
- /* if the user isn't the same, we can drop out */
- if (strcmp(a->q_user, b->q_user) != 0)
- return false;
-
- /* if we have good uids for both but they differ, these are different */
- if (a->q_mailer == ProgMailer)
- {
- ca = getctladdr(a);
- cb = getctladdr(b);
- if (ca != NULL && cb != NULL &&
- bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
- ca->q_uid != cb->q_uid)
- return false;
- }
-
- /* otherwise compare hosts (but be careful for NULL ptrs) */
- if (a->q_host == b->q_host)
- {
- /* probably both null pointers */
- return true;
- }
- if (a->q_host == NULL || b->q_host == NULL)
- {
- /* only one is a null pointer */
- return false;
- }
- if (strcmp(a->q_host, b->q_host) != 0)
- return false;
-
- return true;
-}
-/*
-** PRINTADDR -- print address (for debugging)
-**
-** Parameters:
-** a -- the address to print
-** follow -- follow the q_next chain.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** none.
-*/
-
-struct qflags
-{
- char *qf_name;
- unsigned long qf_bit;
-};
-
-static struct qflags AddressFlags[] =
-{
- { "QGOODUID", QGOODUID },
- { "QPRIMARY", QPRIMARY },
- { "QNOTREMOTE", QNOTREMOTE },
- { "QSELFREF", QSELFREF },
- { "QBOGUSSHELL", QBOGUSSHELL },
- { "QUNSAFEADDR", QUNSAFEADDR },
- { "QPINGONSUCCESS", QPINGONSUCCESS },
- { "QPINGONFAILURE", QPINGONFAILURE },
- { "QPINGONDELAY", QPINGONDELAY },
- { "QHASNOTIFY", QHASNOTIFY },
- { "QRELAYED", QRELAYED },
- { "QEXPANDED", QEXPANDED },
- { "QDELIVERED", QDELIVERED },
- { "QDELAYED", QDELAYED },
- { "QTHISPASS", QTHISPASS },
- { "QRCPTOK", QRCPTOK },
- { NULL, 0 }
-};
-
-void
-printaddr(fp, a, follow)
- SM_FILE_T *fp;
- register ADDRESS *a;
- bool follow;
-{
- register MAILER *m;
- MAILER pseudomailer;
- register struct qflags *qfp;
- bool firstone;
-
- if (a == NULL)
- {
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "[NULL]\n");
- return;
- }
-
- while (a != NULL)
- {
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%p=", a);
- (void) sm_io_flush(fp, SM_TIME_DEFAULT);
-
- /* find the mailer -- carefully */
- m = a->q_mailer;
- if (m == NULL)
- {
- m = &pseudomailer;
- m->m_mno = -1;
- m->m_name = "NULL";
- }
-
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "%s:\n\tmailer %d (%s), host `%s'\n",
- a->q_paddr == NULL ? "<null>" : a->q_paddr,
- m->m_mno, m->m_name,
- a->q_host == NULL ? "<null>" : a->q_host);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "\tuser `%s', ruser `%s'\n",
- a->q_user,
- a->q_ruser == NULL ? "<null>" : a->q_ruser);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tstate=");
- switch (a->q_state)
- {
- case QS_OK:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "OK");
- break;
-
- case QS_DONTSEND:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "DONTSEND");
- break;
-
- case QS_BADADDR:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "BADADDR");
- break;
-
- case QS_QUEUEUP:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "QUEUEUP");
- break;
-
- case QS_RETRY:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "RETRY");
- break;
-
- case QS_SENT:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "SENT");
- break;
-
- case QS_VERIFIED:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "VERIFIED");
- break;
-
- case QS_EXPANDED:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "EXPANDED");
- break;
-
- case QS_SENDER:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "SENDER");
- break;
-
- case QS_CLONED:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "CLONED");
- break;
-
- case QS_DISCARDED:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "DISCARDED");
- break;
-
- case QS_REPLACED:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "REPLACED");
- break;
-
- case QS_REMOVED:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "REMOVED");
- break;
-
- case QS_DUPLICATE:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "DUPLICATE");
- break;
-
- case QS_INCLUDED:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "INCLUDED");
- break;
-
- default:
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "%d", a->q_state);
- break;
- }
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- ", next=%p, alias %p, uid %d, gid %d\n",
- a->q_next, a->q_alias,
- (int) a->q_uid, (int) a->q_gid);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tflags=%lx<",
- a->q_flags);
- firstone = true;
- for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
- {
- if (!bitset(qfp->qf_bit, a->q_flags))
- continue;
- if (!firstone)
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- ",");
- firstone = false;
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
- qfp->qf_name);
- }
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, ">\n");
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "\towner=%s, home=\"%s\", fullname=\"%s\"\n",
- a->q_owner == NULL ? "(none)" : a->q_owner,
- a->q_home == NULL ? "(none)" : a->q_home,
- a->q_fullname == NULL ? "(none)" : a->q_fullname);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "\torcpt=\"%s\", statmta=%s, status=%s\n",
- a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
- a->q_statmta == NULL ? "(none)" : a->q_statmta,
- a->q_status == NULL ? "(none)" : a->q_status);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "\tfinalrcpt=\"%s\"\n",
- a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "\trstatus=\"%s\"\n",
- a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "\tstatdate=%s\n",
- a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
-
- if (!follow)
- return;
- a = a->q_next;
- }
-}
-/*
-** EMPTYADDR -- return true if this address is empty (``<>'')
-**
-** Parameters:
-** a -- pointer to the address
-**
-** Returns:
-** true -- if this address is "empty" (i.e., no one should
-** ever generate replies to it.
-** false -- if it is a "regular" (read: replyable) address.
-*/
-
-bool
-emptyaddr(a)
- register ADDRESS *a;
-{
- return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
- a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
-}
-/*
-** REMOTENAME -- return the name relative to the current mailer
-**
-** Parameters:
-** name -- the name to translate.
-** m -- the mailer that we want to do rewriting relative to.
-** flags -- fine tune operations.
-** pstat -- pointer to status word.
-** e -- the current envelope.
-**
-** Returns:
-** the text string representing this address relative to
-** the receiving mailer.
-**
-** Side Effects:
-** none.
-**
-** Warnings:
-** The text string returned is tucked away locally;
-** copy it if you intend to save it.
-*/
-
-char *
-remotename(name, m, flags, pstat, e)
- char *name;
- struct mailer *m;
- int flags;
- int *pstat;
- register ENVELOPE *e;
-{
- register char **pvp;
- char *SM_NONVOLATILE fancy;
- char *oldg;
- int rwset;
- static char buf[MAXNAME + 1];
- char lbuf[MAXNAME + 1];
- char pvpbuf[PSBUFSIZE];
- char addrtype[4];
-
- if (tTd(12, 1))
- {
- sm_dprintf("remotename(");
- xputs(sm_debug_file(), name);
- sm_dprintf(")\n");
- }
-
- /* 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;
- addrtype[2] = 's';
- }
- else
- {
- rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
- : m->m_re_rwset;
- addrtype[2] = 'r';
- }
- if (rwset < 0)
- return name;
- addrtype[1] = ' ';
- addrtype[3] = '\0';
- addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e';
- macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype);
-
- /*
- ** Do a heuristic crack of this name to extract any comment info.
- ** This will leave the name as a comment and a $g macro.
- */
-
- if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
- fancy = "\201g";
- else
- fancy = crackaddr(name, e);
-
- /*
- ** Turn the name into canonical form.
- ** Normally this will be RFC 822 style, i.e., "user@domain".
- ** If this only resolves to "user", and the "C" flag is
- ** specified in the sending mailer, then the sender's
- ** domain will be appended.
- */
-
- pvp = prescan(name, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL, false);
- if (pvp == NULL)
- return name;
- if (REWRITE(pvp, 3, 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)
- {
- if (--l <= 0)
- {
- *--pxp = NULL;
- usrerr("553 5.1.0 remotename: too many tokens");
- *pstat = EX_UNAVAILABLE;
- break;
- }
- }
- if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
- *pstat = EX_TEMPFAIL;
- }
- }
-
- /*
- ** Do more specific rewriting.
- ** Rewrite using ruleset 1 or 2 depending on whether this is
- ** a sender address or not.
- ** Then run it through any receiving-mailer-specific rulesets.
- */
-
- if (bitset(RF_SENDERADDR, flags))
- {
- if (REWRITE(pvp, 1, e) == EX_TEMPFAIL)
- *pstat = EX_TEMPFAIL;
- }
- else
- {
- if (REWRITE(pvp, 2, e) == EX_TEMPFAIL)
- *pstat = EX_TEMPFAIL;
- }
- if (rwset > 0)
- {
- if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL)
- *pstat = EX_TEMPFAIL;
- }
-
- /*
- ** Do any final sanitation the address may require.
- ** This will normally be used to turn internal forms
- ** (e.g., user@host.LOCAL) into external form. This
- ** may be used as a default to the above rules.
- */
-
- if (REWRITE(pvp, 4, e) == EX_TEMPFAIL)
- *pstat = EX_TEMPFAIL;
-
- /*
- ** Now restore the comment information we had at the beginning.
- */
-
- cataddr(pvp, NULL, lbuf, sizeof(lbuf), '\0', false);
- oldg = macget(&e->e_macro, 'g');
- macset(&e->e_macro, 'g', lbuf);
-
- SM_TRY
- /* need to make sure route-addrs have <angle brackets> */
- if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
- expand("<\201g>", buf, sizeof(buf), e);
- else
- expand(fancy, buf, sizeof(buf), e);
- SM_FINALLY
- macset(&e->e_macro, 'g', oldg);
- SM_END_TRY
-
- if (tTd(12, 1))
- {
- sm_dprintf("remotename => `");
- xputs(sm_debug_file(), buf);
- sm_dprintf("'\n");
- }
- return buf;
-}
-/*
-** MAPLOCALUSER -- run local username through ruleset 5 for final redirection
-**
-** Parameters:
-** a -- the address to map (but just the user name part).
-** sendq -- the sendq in which to install any replacement
-** addresses.
-** aliaslevel -- the alias nesting depth.
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-#define Q_COPYFLAGS (QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
- Q_PINGFLAGS|QHASNOTIFY|\
- QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\
- QBYTRACE|QBYNDELAY|QBYNRELAY)
-
-void
-maplocaluser(a, sendq, aliaslevel, e)
- register ADDRESS *a;
- ADDRESS **sendq;
- int aliaslevel;
- ENVELOPE *e;
-{
- register char **pvp;
- register ADDRESS *SM_NONVOLATILE a1 = NULL;
- char pvpbuf[PSBUFSIZE];
-
- if (tTd(29, 1))
- {
- sm_dprintf("maplocaluser: ");
- printaddr(sm_debug_file(), a, false);
- }
- pvp = prescan(a->q_user, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL,
- false);
- if (pvp == NULL)
- {
- if (tTd(29, 9))
- sm_dprintf("maplocaluser: cannot prescan %s\n",
- a->q_user);
- return;
- }
-
- macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
- macdefine(&e->e_macro, A_PERM, 'u', a->q_user);
- macdefine(&e->e_macro, A_PERM, 'z', a->q_home);
-
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
- if (REWRITE(pvp, 5, e) == EX_TEMPFAIL)
- {
- if (tTd(29, 9))
- sm_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))
- sm_dprintf("maplocaluser: doesn't resolve\n");
- return;
- }
-
- SM_TRY
- a1 = buildaddr(pvp, NULL, 0, e);
- SM_EXCEPT(exc, "E:mta.quickabort")
-
- /*
- ** mark address as bad, S5 returned an error
- ** and we gave that back to the SMTP client.
- */
-
- a->q_state = QS_DONTSEND;
- sm_exc_raisenew_x(&EtypeQuickAbort, 2);
- SM_END_TRY
-
- /* if non-null, mailer destination specified -- has it changed? */
- if (a1 == NULL || sameaddr(a, a1))
- {
- if (tTd(29, 9))
- sm_dprintf("maplocaluser: address unchanged\n");
- return;
- }
-
- /* make new address take on flags and print attributes of old */
- a1->q_flags &= ~Q_COPYFLAGS;
- a1->q_flags |= a->q_flags & Q_COPYFLAGS;
- a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr);
- a1->q_finalrcpt = a->q_finalrcpt;
- a1->q_orcpt = a->q_orcpt;
-
- /* mark old address as dead; insert new address */
- a->q_state = QS_REPLACED;
- if (tTd(29, 5))
- {
- sm_dprintf("maplocaluser: QS_REPLACED ");
- printaddr(sm_debug_file(), a, false);
- }
- a1->q_alias = a;
- allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e);
- (void) recipient(a1, sendq, aliaslevel, e);
-}
-/*
-** DEQUOTE_INIT -- initialize dequote map
-**
-** Parameters:
-** map -- the internal map structure.
-** args -- arguments.
-**
-** Returns:
-** true.
-*/
-
-bool
-dequote_init(map, args)
- MAP *map;
- char *args;
-{
- register char *p = args;
-
- /* there is no check whether there is really an argument */
- map->map_mflags |= MF_KEEPQUOTES;
- for (;;)
- {
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p != '-')
- break;
- switch (*++p)
- {
- case 'a':
- map->map_app = ++p;
- break;
-
- case 'D':
- map->map_mflags |= MF_DEFER;
- break;
-
- case 'S':
- case 's':
- map->map_spacesub = *++p;
- break;
- }
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p = '\0';
- }
- if (map->map_app != NULL)
- map->map_app = newstr(map->map_app);
-
- return true;
-}
-/*
-** DEQUOTE_MAP -- unquote an address
-**
-** Parameters:
-** map -- the internal map structure (ignored).
-** name -- the name to dequote.
-** av -- arguments (ignored).
-** statp -- pointer to status out-parameter.
-**
-** Returns:
-** NULL -- if there were no quotes, or if the resulting
-** unquoted buffer would not be acceptable to prescan.
-** else -- The dequoted buffer.
-*/
-
-/* ARGSUSED2 */
-char *
-dequote_map(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- register char *p;
- register char *q;
- register char c;
- int anglecnt = 0;
- int cmntcnt = 0;
- int quotecnt = 0;
- int spacecnt = 0;
- bool quotemode = false;
- bool bslashmode = false;
- char spacesub = map->map_spacesub;
-
- for (p = q = name; (c = *p++) != '\0'; )
- {
- if (bslashmode)
- {
- bslashmode = false;
- *q++ = c;
- continue;
- }
-
- if (c == ' ' && spacesub != '\0')
- c = spacesub;
-
- switch (c)
- {
- case '\\':
- bslashmode = true;
- break;
-
- case '(':
- cmntcnt++;
- break;
-
- case ')':
- if (cmntcnt-- <= 0)
- return NULL;
- break;
-
- case ' ':
- case '\t':
- spacecnt++;
- break;
- }
-
- if (cmntcnt > 0)
- {
- *q++ = c;
- continue;
- }
-
- switch (c)
- {
- case '"':
- quotemode = !quotemode;
- quotecnt++;
- continue;
-
- case '<':
- anglecnt++;
- break;
-
- case '>':
- if (anglecnt-- <= 0)
- return NULL;
- break;
- }
- *q++ = c;
- }
-
- if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
- quotemode || quotecnt <= 0 || spacecnt != 0)
- return NULL;
- *q++ = '\0';
- return map_rewrite(map, name, strlen(name), NULL);
-}
-/*
-** RSCHECK -- check string(s) for validity using rewriting sets
-**
-** Parameters:
-** rwset -- the rewriting set to use.
-** p1 -- the first string to check.
-** p2 -- the second string to check -- may be null.
-** e -- the current envelope.
-** flags -- control some behavior, see RSF_ in sendmail.h
-** logl -- logging level.
-** host -- NULL or relay host.
-** logid -- id for sm_syslog.
-** addr -- if not NULL and ruleset returns $#error:
-** store mailer triple here.
-**
-** Returns:
-** EX_OK -- if the rwset doesn't resolve to $#error
-** else -- the failure status (message printed)
-*/
-
-int
-rscheck(rwset, p1, p2, e, flags, logl, host, logid, addr)
- char *rwset;
- char *p1;
- char *p2;
- ENVELOPE *e;
- int flags;
- int logl;
- char *host;
- char *logid;
- ADDRESS *addr;
-{
- char *volatile buf;
- size_t bufsize;
- int saveexitstat;
- int volatile rstat = EX_OK;
- char **pvp;
- int rsno;
- bool volatile discard = false;
- bool saveQuickAbort = QuickAbort;
- bool saveSuprErrs = SuprErrs;
- bool quarantine = false;
- char ubuf[BUFSIZ * 2];
- char buf0[MAXLINE];
- char pvpbuf[PSBUFSIZE];
- extern char MsgBuf[];
-
- if (tTd(48, 2))
- sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
- p2 == NULL ? "(NULL)" : p2);
-
- rsno = strtorwset(rwset, NULL, ST_FIND);
- if (rsno < 0)
- return EX_OK;
-
- if (p2 != NULL)
- {
- bufsize = strlen(p1) + strlen(p2) + 2;
- if (bufsize > sizeof(buf0))
- buf = sm_malloc_x(bufsize);
- else
- {
- buf = buf0;
- bufsize = sizeof(buf0);
- }
- (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
- }
- else
- {
- bufsize = strlen(p1) + 1;
- if (bufsize > sizeof(buf0))
- buf = sm_malloc_x(bufsize);
- else
- {
- buf = buf0;
- bufsize = sizeof(buf0);
- }
- (void) sm_strlcpy(buf, p1, bufsize);
- }
- SM_TRY
- {
- SuprErrs = true;
- QuickAbort = false;
- pvp = prescan(buf, '\0', pvpbuf, sizeof(pvpbuf), NULL,
- bitset(RSF_RMCOMM, flags) ?
- IntTokenTab : TokTypeNoC,
- bitset(RSF_RMCOMM, flags) ? false : true);
- SuprErrs = saveSuprErrs;
- if (pvp == NULL)
- {
- if (tTd(48, 2))
- sm_dprintf("rscheck: cannot prescan input\n");
- /*
- syserr("rscheck: cannot prescan input: \"%s\"",
- shortenstring(buf, MAXSHORTSTR));
- rstat = EX_DATAERR;
- */
- goto finis;
- }
- if (bitset(RSF_UNSTRUCTURED, flags))
- SuprErrs = true;
- (void) REWRITE(pvp, rsno, e);
- if (bitset(RSF_UNSTRUCTURED, flags))
- SuprErrs = saveSuprErrs;
- if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
- pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
- strcmp(pvp[1], "discard") != 0))
- {
- goto finis;
- }
-
- if (strcmp(pvp[1], "discard") == 0)
- {
- if (tTd(48, 2))
- sm_dprintf("rscheck: discard mailer selected\n");
- e->e_flags |= EF_DISCARD;
- discard = true;
- }
- else if (strcmp(pvp[1], "error") == 0 &&
- pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST &&
- pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0)
- {
- if (pvp[4] == NULL ||
- (pvp[4][0] & 0377) != CANONUSER ||
- pvp[5] == NULL)
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
- rwset);
- else
- {
- cataddr(&(pvp[5]), NULL, ubuf,
- sizeof(ubuf), ' ', true);
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
- ubuf);
- }
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), e->e_quarmsg);
- quarantine = true;
- }
- else
- {
- auto ADDRESS a1;
- int savelogusrerrs = LogUsrErrs;
- static bool logged = false;
-
- /* got an error -- process it */
- saveexitstat = ExitStat;
- LogUsrErrs = false;
- (void) buildaddr(pvp, &a1, 0, e);
- if (addr != NULL)
- {
- addr->q_mailer = a1.q_mailer;
- addr->q_user = a1.q_user;
- addr->q_host = a1.q_host;
- }
- LogUsrErrs = savelogusrerrs;
- rstat = ExitStat;
- ExitStat = saveexitstat;
- if (!logged)
- {
- if (bitset(RSF_COUNT, flags))
- markstats(e, &a1, STATS_REJECT);
- logged = true;
- }
- }
-
- if (LogLevel > logl)
- {
- char *relay;
- char *p;
- char lbuf[MAXLINE];
-
- p = lbuf;
- if (p2 != NULL)
- {
- (void) sm_snprintf(p, SPACELEFT(lbuf, p),
- ", arg2=%s",
- p2);
- p += strlen(p);
- }
-
- if (host != NULL)
- relay = host;
- else
- relay = macvalue('_', e);
- if (relay != NULL)
- {
- (void) sm_snprintf(p, SPACELEFT(lbuf, p),
- ", relay=%s", relay);
- p += strlen(p);
- }
- *p = '\0';
- if (discard)
- sm_syslog(LOG_NOTICE, logid,
- "ruleset=%s, arg1=%s%s, discard",
- rwset, p1, lbuf);
- else if (quarantine)
- sm_syslog(LOG_NOTICE, logid,
- "ruleset=%s, arg1=%s%s, quarantine=%s",
- rwset, p1, lbuf, ubuf);
- else
- sm_syslog(LOG_NOTICE, logid,
- "ruleset=%s, arg1=%s%s, reject=%s",
- rwset, p1, lbuf, MsgBuf);
- }
-
- finis: ;
- }
- SM_FINALLY
- {
- /* clean up */
- if (buf != buf0)
- sm_free(buf);
- QuickAbort = saveQuickAbort;
- }
- SM_END_TRY
-
- setstat(rstat);
-
- /* rulesets don't set errno */
- errno = 0;
- if (rstat != EX_OK && QuickAbort)
- sm_exc_raisenew_x(&EtypeQuickAbort, 2);
- return rstat;
-}
-/*
-** RSCAP -- call rewriting set to return capabilities
-**
-** Parameters:
-** rwset -- the rewriting set to use.
-** p1 -- the first string to check.
-** p2 -- the second string to check -- may be null.
-** e -- the current envelope.
-** pvp -- pointer to token vector.
-** pvpbuf -- buffer space.
-** size -- size of buffer space.
-**
-** Returns:
-** EX_UNAVAILABLE -- ruleset doesn't exist.
-** EX_DATAERR -- prescan() failed.
-** EX_OK -- rewrite() was successful.
-** else -- return status from rewrite().
-*/
-
-int
-rscap(rwset, p1, p2, e, pvp, pvpbuf, size)
- char *rwset;
- char *p1;
- char *p2;
- ENVELOPE *e;
- char ***pvp;
- char *pvpbuf;
- int size;
-{
- char *volatile buf;
- size_t bufsize;
- int volatile rstat = EX_OK;
- int rsno;
- bool saveQuickAbort = QuickAbort;
- bool saveSuprErrs = SuprErrs;
- char buf0[MAXLINE];
- extern char MsgBuf[];
-
- if (tTd(48, 2))
- sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1,
- p2 == NULL ? "(NULL)" : p2);
-
- SM_REQUIRE(pvp != NULL);
- rsno = strtorwset(rwset, NULL, ST_FIND);
- if (rsno < 0)
- return EX_UNAVAILABLE;
-
- if (p2 != NULL)
- {
- bufsize = strlen(p1) + strlen(p2) + 2;
- if (bufsize > sizeof(buf0))
- buf = sm_malloc_x(bufsize);
- else
- {
- buf = buf0;
- bufsize = sizeof(buf0);
- }
- (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
- }
- else
- {
- bufsize = strlen(p1) + 1;
- if (bufsize > sizeof(buf0))
- buf = sm_malloc_x(bufsize);
- else
- {
- buf = buf0;
- bufsize = sizeof(buf0);
- }
- (void) sm_strlcpy(buf, p1, bufsize);
- }
- SM_TRY
- {
- SuprErrs = true;
- QuickAbort = false;
- *pvp = prescan(buf, '\0', pvpbuf, size, NULL, IntTokenTab,
- false);
- if (*pvp != NULL)
- rstat = rewrite(*pvp, rsno, 0, e, size);
- else
- {
- if (tTd(48, 2))
- sm_dprintf("rscap: cannot prescan input\n");
- rstat = EX_DATAERR;
- }
- }
- SM_FINALLY
- {
- /* clean up */
- if (buf != buf0)
- sm_free(buf);
- SuprErrs = saveSuprErrs;
- QuickAbort = saveQuickAbort;
-
- /* prevent information leak, this may contain rewrite error */
- MsgBuf[0] = '\0';
- }
- SM_END_TRY
- return rstat;
-}
diff --git a/contrib/sendmail/src/queue.c b/contrib/sendmail/src/queue.c
deleted file mode 100644
index e80a035..0000000
--- a/contrib/sendmail/src/queue.c
+++ /dev/null
@@ -1,8895 +0,0 @@
-/*
- * Copyright (c) 1998-2007 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.
- *
- */
-
-#include <sendmail.h>
-#include <sm/sem.h>
-
-SM_RCSID("@(#)$Id: queue.c,v 8.975 2007/06/18 20:08:40 ca Exp $")
-
-#include <dirent.h>
-
-# define RELEASE_QUEUE (void) 0
-# define ST_INODE(st) (st).st_ino
-
-# define sm_file_exists(errno) ((errno) == EEXIST)
-
-# if HASFLOCK && defined(O_EXLOCK)
-# define SM_OPEN_EXLOCK 1
-# define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK)
-# else /* HASFLOCK && defined(O_EXLOCK) */
-# define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL)
-# endif /* HASFLOCK && defined(O_EXLOCK) */
-
-#ifndef SM_OPEN_EXLOCK
-# define SM_OPEN_EXLOCK 0
-#endif /* ! SM_OPEN_EXLOCK */
-
-/*
-** Historical notes:
-** QF_VERSION == 4 was sendmail 8.10/8.11 without _FFR_QUEUEDELAY
-** QF_VERSION == 5 was sendmail 8.10/8.11 with _FFR_QUEUEDELAY
-** QF_VERSION == 6 was sendmail 8.12 without _FFR_QUEUEDELAY
-** QF_VERSION == 7 was sendmail 8.12 with _FFR_QUEUEDELAY
-** QF_VERSION == 8 is sendmail 8.13
-*/
-
-#define QF_VERSION 8 /* version number of this queue format */
-
-static char queue_letter __P((ENVELOPE *, int));
-static bool quarantine_queue_item __P((int, int, ENVELOPE *, char *));
-
-/* Naming convention: qgrp: index of queue group, qg: QUEUEGROUP */
-
-/*
-** Work queue.
-*/
-
-struct work
-{
- char *w_name; /* name of control file */
- char *w_host; /* name of recipient host */
- bool w_lock; /* is message locked? */
- bool w_tooyoung; /* is it too young to run? */
- long w_pri; /* priority of message, see below */
- time_t w_ctime; /* creation time */
- time_t w_mtime; /* modification time */
- int w_qgrp; /* queue group located in */
- int w_qdir; /* queue directory located in */
- struct work *w_next; /* next in queue */
-};
-
-typedef struct work WORK;
-
-static WORK *WorkQ; /* queue of things to be done */
-static int NumWorkGroups; /* number of work groups */
-static time_t Current_LA_time = 0;
-
-/* Get new load average every 30 seconds. */
-#define GET_NEW_LA_TIME 30
-
-#define SM_GET_LA(now) \
- do \
- { \
- now = curtime(); \
- if (Current_LA_time < now - GET_NEW_LA_TIME) \
- { \
- sm_getla(); \
- Current_LA_time = now; \
- } \
- } while (0)
-
-/*
-** DoQueueRun indicates that a queue run is needed.
-** Notice: DoQueueRun is modified in a signal handler!
-*/
-
-static bool volatile DoQueueRun; /* non-interrupt time queue run needed */
-
-/*
-** Work group definition structure.
-** Each work group contains one or more queue groups. This is done
-** to manage the number of queue group runners active at the same time
-** to be within the constraints of MaxQueueChildren (if it is set).
-** The number of queue groups that can be run on the next work run
-** is kept track of. The queue groups are run in a round robin.
-*/
-
-struct workgrp
-{
- int wg_numqgrp; /* number of queue groups in work grp */
- int wg_runners; /* total runners */
- int wg_curqgrp; /* current queue group */
- QUEUEGRP **wg_qgs; /* array of queue groups */
- int wg_maxact; /* max # of active runners */
- time_t wg_lowqintvl; /* lowest queue interval */
- int wg_restart; /* needs restarting? */
- int wg_restartcnt; /* count of times restarted */
-};
-
-typedef struct workgrp WORKGRP;
-
-static WORKGRP volatile WorkGrp[MAXWORKGROUPS + 1]; /* work groups */
-
-#if SM_HEAP_CHECK
-static SM_DEBUG_T DebugLeakQ = SM_DEBUG_INITIALIZER("leak_q",
- "@(#)$Debug: leak_q - trace memory leaks during queue processing $");
-#endif /* SM_HEAP_CHECK */
-
-/*
-** We use EmptyString instead of "" to avoid
-** 'zero-length format string' warnings from gcc
-*/
-
-static const char EmptyString[] = "";
-
-static void grow_wlist __P((int, int));
-static int multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *));
-static int gatherq __P((int, int, bool, bool *, bool *));
-static int sortq __P((int));
-static void printctladdr __P((ADDRESS *, SM_FILE_T *));
-static bool readqf __P((ENVELOPE *, bool));
-static void restart_work_group __P((int));
-static void runner_work __P((ENVELOPE *, int, bool, int, int));
-static void schedule_queue_runs __P((bool, int, bool));
-static char *strrev __P((char *));
-static ADDRESS *setctluser __P((char *, int, ENVELOPE *));
-#if _FFR_RHS
-static int sm_strshufflecmp __P((char *, char *));
-static void init_shuffle_alphabet __P(());
-#endif /* _FFR_RHS */
-
-/*
-** Note: workcmpf?() don't use a prototype because it will cause a conflict
-** with the qsort() call (which expects something like
-** int (*compar)(const void *, const void *), not (WORK *, WORK *))
-*/
-
-static int workcmpf0();
-static int workcmpf1();
-static int workcmpf2();
-static int workcmpf3();
-static int workcmpf4();
-static int randi = 3; /* index for workcmpf5() */
-static int workcmpf5();
-static int workcmpf6();
-#if _FFR_RHS
-static int workcmpf7();
-#endif /* _FFR_RHS */
-
-#if RANDOMSHIFT
-# define get_rand_mod(m) ((get_random() >> RANDOMSHIFT) % (m))
-#else /* RANDOMSHIFT */
-# define get_rand_mod(m) (get_random() % (m))
-#endif /* RANDOMSHIFT */
-
-/*
-** File system definition.
-** Used to keep track of how much free space is available
-** on a file system in which one or more queue directories reside.
-*/
-
-typedef struct filesys_shared FILESYS;
-
-struct filesys_shared
-{
- dev_t fs_dev; /* unique device id */
- long fs_avail; /* number of free blocks available */
- long fs_blksize; /* block size, in bytes */
-};
-
-/* probably kept in shared memory */
-static FILESYS FileSys[MAXFILESYS]; /* queue file systems */
-static const char *FSPath[MAXFILESYS]; /* pathnames for file systems */
-
-#if SM_CONF_SHM
-
-/*
-** Shared memory data
-**
-** Current layout:
-** size -- size of shared memory segment
-** pid -- pid of owner, should be a unique id to avoid misinterpretations
-** by other processes.
-** tag -- should be a unique id to avoid misinterpretations by others.
-** idea: hash over configuration data that will be stored here.
-** NumFileSys -- number of file systems.
-** FileSys -- (arrary of) structure for used file systems.
-** RSATmpCnt -- counter for number of uses of ephemeral RSA key.
-** QShm -- (array of) structure for information about queue directories.
-*/
-
-/*
-** Queue data in shared memory
-*/
-
-typedef struct queue_shared QUEUE_SHM_T;
-
-struct queue_shared
-{
- int qs_entries; /* number of entries */
- /* XXX more to follow? */
-};
-
-static void *Pshm; /* pointer to shared memory */
-static FILESYS *PtrFileSys; /* pointer to queue file system array */
-int ShmId = SM_SHM_NO_ID; /* shared memory id */
-static QUEUE_SHM_T *QShm; /* pointer to shared queue data */
-static size_t shms;
-
-# define SHM_OFF_PID(p) (((char *) (p)) + sizeof(int))
-# define SHM_OFF_TAG(p) (((char *) (p)) + sizeof(pid_t) + sizeof(int))
-# define SHM_OFF_HEAD (sizeof(pid_t) + sizeof(int) * 2)
-
-/* how to access FileSys */
-# define FILE_SYS(i) (PtrFileSys[i])
-
-/* first entry is a tag, for now just the size */
-# define OFF_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD)
-
-/* offset for PNumFileSys */
-# define OFF_NUM_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys))
-
-/* offset for PRSATmpCnt */
-# define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int))
-int *PRSATmpCnt;
-
-/* offset for queue_shm */
-# define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
-
-# define QSHM_ENTRIES(i) QShm[i].qs_entries
-
-/* basic size of shared memory segment */
-# define SM_T_SIZE (SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
-
-static unsigned int hash_q __P((char *, unsigned int));
-
-/*
-** HASH_Q -- simple hash function
-**
-** Parameters:
-** p -- string to hash.
-** h -- hash start value (from previous run).
-**
-** Returns:
-** hash value.
-*/
-
-static unsigned int
-hash_q(p, h)
- char *p;
- unsigned int h;
-{
- int c, d;
-
- while (*p != '\0')
- {
- d = *p++;
- c = d;
- c ^= c<<6;
- h += (c<<11) ^ (c>>1);
- h ^= (d<<14) + (d<<7) + (d<<4) + d;
- }
- return h;
-}
-
-
-#else /* SM_CONF_SHM */
-# define FILE_SYS(i) FileSys[i]
-#endif /* SM_CONF_SHM */
-
-/* access to the various components of file system data */
-#define FILE_SYS_NAME(i) FSPath[i]
-#define FILE_SYS_AVAIL(i) FILE_SYS(i).fs_avail
-#define FILE_SYS_BLKSIZE(i) FILE_SYS(i).fs_blksize
-#define FILE_SYS_DEV(i) FILE_SYS(i).fs_dev
-
-
-/*
-** Current qf file field assignments:
-**
-** A AUTH= parameter
-** B body type
-** C controlling user
-** D data file name
-** d data file directory name (added in 8.12)
-** E error recipient
-** F flag bits
-** G free (was: queue delay algorithm if _FFR_QUEUEDELAY)
-** H header
-** I data file's inode number
-** K time of last delivery attempt
-** L Solaris Content-Length: header (obsolete)
-** M message
-** N number of delivery attempts
-** P message priority
-** q quarantine reason
-** Q original recipient (ORCPT=)
-** r final recipient (Final-Recipient: DSN field)
-** R recipient
-** S sender
-** T init time
-** V queue file version
-** X free (was: character set if _FFR_SAVE_CHARSET)
-** Y free (was: current delay if _FFR_QUEUEDELAY)
-** Z original envelope id from ESMTP
-** ! deliver by (added in 8.12)
-** $ define macro
-** . terminate file
-*/
-
-/*
-** QUEUEUP -- queue a message up for future transmission.
-**
-** Parameters:
-** e -- the envelope to queue up.
-** announce -- if true, tell when you are queueing up.
-** msync -- if true, then fsync() if SuperSafe interactive mode.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** The current request is saved in a control file.
-** The queue file is left locked.
-*/
-
-void
-queueup(e, announce, msync)
- register ENVELOPE *e;
- bool announce;
- bool msync;
-{
- register SM_FILE_T *tfp;
- register HDR *h;
- register ADDRESS *q;
- int tfd = -1;
- int i;
- bool newid;
- register char *p;
- MAILER nullmailer;
- MCI mcibuf;
- char qf[MAXPATHLEN];
- char tf[MAXPATHLEN];
- char df[MAXPATHLEN];
- char buf[MAXLINE];
-
- /*
- ** Create control file.
- */
-
-#define OPEN_TF do \
- { \
- MODE_T oldumask = 0; \
- \
- if (bitset(S_IWGRP, QueueFileMode)) \
- oldumask = umask(002); \
- tfd = open(tf, TF_OPEN_FLAGS, QueueFileMode); \
- if (bitset(S_IWGRP, QueueFileMode)) \
- (void) umask(oldumask); \
- } while (0)
-
-
- newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
- (void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof(tf));
- tfp = e->e_lockfp;
- if (tfp == NULL && newid)
- {
- /*
- ** open qf file directly: this will give an error if the file
- ** already exists and hence prevent problems if a queue-id
- ** is reused (e.g., because the clock is set back).
- */
-
- (void) sm_strlcpy(tf, queuename(e, ANYQFL_LETTER), sizeof(tf));
- OPEN_TF;
- if (tfd < 0 ||
-#if !SM_OPEN_EXLOCK
- !lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB) ||
-#endif /* !SM_OPEN_EXLOCK */
- (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &tfd, SM_IO_WRONLY,
- NULL)) == NULL)
- {
- int save_errno = errno;
-
- printopenfds(true);
- errno = save_errno;
- syserr("!queueup: cannot create queue file %s, euid=%d, fd=%d, fp=%p",
- tf, (int) geteuid(), tfd, tfp);
- /* NOTREACHED */
- }
- e->e_lockfp = tfp;
- upd_qs(e, 1, 0, "queueup");
- }
-
- /* if newid, write the queue file directly (instead of temp file) */
- if (!newid)
- {
- /* get a locked tf file */
- for (i = 0; i < 128; i++)
- {
- if (tfd < 0)
- {
- OPEN_TF;
- if (tfd < 0)
- {
- if (errno != EEXIST)
- break;
- if (LogLevel > 0 && (i % 32) == 0)
- sm_syslog(LOG_ALERT, e->e_id,
- "queueup: cannot create %s, euid=%d: %s",
- tf, (int) geteuid(),
- sm_errstring(errno));
- }
-#if SM_OPEN_EXLOCK
- else
- break;
-#endif /* SM_OPEN_EXLOCK */
- }
- if (tfd >= 0)
- {
-#if SM_OPEN_EXLOCK
- /* file is locked by open() */
- break;
-#else /* SM_OPEN_EXLOCK */
- if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB))
- break;
- else
-#endif /* SM_OPEN_EXLOCK */
- if (LogLevel > 0 && (i % 32) == 0)
- sm_syslog(LOG_ALERT, e->e_id,
- "queueup: cannot lock %s: %s",
- tf, sm_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, TEMPQF_LETTER));
- }
- else
- (void) sleep(i % 32);
- }
- if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &tfd, SM_IO_WRONLY_B,
- NULL)) == NULL)
- {
- int save_errno = errno;
-
- printopenfds(true);
- errno = save_errno;
- syserr("!queueup: cannot create queue temp file %s, uid=%d",
- tf, (int) geteuid());
- }
- }
-
- if (tTd(40, 1))
- sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n",
- qid_printqueue(e->e_qgrp, e->e_qdir),
- queuename(e, ANYQFL_LETTER),
- newid ? " (new id)" : "");
- if (tTd(40, 3))
- {
- sm_dprintf(" e_flags=");
- printenvflags(e);
- }
- if (tTd(40, 32))
- {
- sm_dprintf(" sendq=");
- printaddr(sm_debug_file(), e->e_sendqueue, true);
- }
- if (tTd(40, 9))
- {
- sm_dprintf(" tfp=");
- dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false);
- sm_dprintf(" lockfp=");
- if (e->e_lockfp == NULL)
- sm_dprintf("NULL\n");
- else
- dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL),
- true, false);
- }
-
- /*
- ** If there is no data file yet, create one.
- */
-
- (void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof(df));
- if (bitset(EF_HAS_DF, e->e_flags))
- {
- if (e->e_dfp != NULL &&
- SuperSafe != SAFE_REALLY &&
- SuperSafe != SAFE_REALLY_POSTMILTER &&
- sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
- errno != EINVAL)
- {
- syserr("!queueup: cannot commit data file %s, uid=%d",
- queuename(e, DATAFL_LETTER), (int) geteuid());
- }
- if (e->e_dfp != NULL &&
- SuperSafe == SAFE_INTERACTIVE && msync)
- {
- if (tTd(40,32))
- sm_syslog(LOG_INFO, e->e_id,
- "queueup: fsync(e->e_dfp)");
-
- if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD,
- NULL)) < 0)
- {
- if (newid)
- syserr("!552 Error writing data file %s",
- df);
- else
- syserr("!452 Error writing data file %s",
- df);
- }
- }
- }
- else
- {
- int dfd;
- MODE_T oldumask = 0;
- register SM_FILE_T *dfp = NULL;
- struct stat stbuf;
-
- if (e->e_dfp != NULL &&
- sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
- syserr("committing over bf file");
-
- if (bitset(S_IWGRP, QueueFileMode))
- oldumask = umask(002);
- dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC|QF_O_EXTRA,
- QueueFileMode);
- if (bitset(S_IWGRP, QueueFileMode))
- (void) umask(oldumask);
- if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &dfd, SM_IO_WRONLY_B,
- NULL)) == NULL)
- syserr("!queueup: cannot create data temp file %s, uid=%d",
- df, (int) geteuid());
- if (fstat(dfd, &stbuf) < 0)
- e->e_dfino = -1;
- else
- {
- e->e_dfdev = stbuf.st_dev;
- e->e_dfino = ST_INODE(stbuf);
- }
- e->e_flags |= EF_HAS_DF;
- memset(&mcibuf, '\0', sizeof(mcibuf));
- mcibuf.mci_out = dfp;
- mcibuf.mci_mailer = FileMailer;
- (*e->e_putbody)(&mcibuf, e, NULL);
-
- if (SuperSafe == SAFE_REALLY ||
- SuperSafe == SAFE_REALLY_POSTMILTER ||
- (SuperSafe == SAFE_INTERACTIVE && msync))
- {
- if (tTd(40,32))
- sm_syslog(LOG_INFO, e->e_id,
- "queueup: fsync(dfp)");
-
- if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0)
- {
- if (newid)
- syserr("!552 Error writing data file %s",
- df);
- else
- syserr("!452 Error writing data file %s",
- df);
- }
- }
-
- if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0)
- syserr("!queueup: cannot save data temp file %s, uid=%d",
- df, (int) geteuid());
- e->e_putbody = putbody;
- }
-
- /*
- ** Output future work requests.
- ** Priority and creation time should be first, since
- ** they are required by gatherq.
- */
-
- /* output queue version number (must be first!) */
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION);
-
- /* output creation time */
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime);
-
- /* output last delivery time */
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime);
-
- /* output number of delivery attempts */
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries);
-
- /* output message priority */
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority);
-
- /*
- ** If data file is in a different directory than the queue file,
- ** output a "d" record naming the directory of the data file.
- */
-
- if (e->e_dfqgrp != e->e_qgrp)
- {
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n",
- Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name);
- }
-
- /* output inode number of data file */
- /* XXX should probably include device major/minor too */
- if (e->e_dfino != -1)
- {
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n",
- (long) major(e->e_dfdev),
- (long) minor(e->e_dfdev),
- (ULONGLONG_T) e->e_dfino);
- }
-
- /* output body type */
- if (e->e_bodytype != NULL)
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n",
- denlstring(e->e_bodytype, true, false));
-
- /* quarantine reason */
- if (e->e_quarmsg != NULL)
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n",
- denlstring(e->e_quarmsg, true, false));
-
- /* message from envelope, if it exists */
- if (e->e_message != NULL)
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
- denlstring(e->e_message, true, false));
-
- /* send various flag bits through */
- p = buf;
- if (bitset(EF_WARNING, e->e_flags))
- *p++ = 'w';
- if (bitset(EF_RESPONSE, e->e_flags))
- *p++ = 'r';
- if (bitset(EF_HAS8BIT, e->e_flags))
- *p++ = '8';
- if (bitset(EF_DELETE_BCC, e->e_flags))
- *p++ = 'b';
- if (bitset(EF_RET_PARAM, e->e_flags))
- *p++ = 'd';
- if (bitset(EF_NO_BODY_RETN, e->e_flags))
- *p++ = 'n';
- if (bitset(EF_SPLIT, e->e_flags))
- *p++ = 's';
- *p++ = '\0';
- if (buf[0] != '\0')
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf);
-
- /* save $={persistentMacros} macro values */
- queueup_macros(macid("{persistentMacros}"), tfp, e);
-
- /* output name of sender */
- if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
- p = e->e_sender;
- else
- p = e->e_from.q_paddr;
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n",
- denlstring(p, true, false));
-
- /* output ESMTP-supplied "original" information */
- if (e->e_envid != NULL)
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n",
- denlstring(e->e_envid, true, false));
-
- /* output AUTH= parameter */
- if (e->e_auth_param != NULL)
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n",
- denlstring(e->e_auth_param, true, false));
- if (e->e_dlvr_flag != 0)
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n",
- (char) e->e_dlvr_flag, e->e_deliver_by);
-
- /* output list of recipient addresses */
- printctladdr(NULL, NULL);
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (!QS_IS_UNDELIVERED(q->q_state))
- continue;
-
- /* message for this recipient, if it exists */
- if (q->q_message != NULL)
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
- denlstring(q->q_message, true,
- false));
-
- printctladdr(q, tfp);
- if (q->q_orcpt != NULL)
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n",
- denlstring(q->q_orcpt, true,
- false));
- if (q->q_finalrcpt != NULL)
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n",
- denlstring(q->q_finalrcpt, true,
- false));
- (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R');
- if (bitset(QPRIMARY, q->q_flags))
- (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P');
- if (bitset(QHASNOTIFY, q->q_flags))
- (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N');
- if (bitset(QPINGONSUCCESS, q->q_flags))
- (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S');
- if (bitset(QPINGONFAILURE, q->q_flags))
- (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F');
- if (bitset(QPINGONDELAY, q->q_flags))
- (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D');
- if (q->q_alias != NULL &&
- bitset(QALIAS, q->q_alias->q_flags))
- (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A');
- (void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':');
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n",
- denlstring(q->q_paddr, true, false));
- if (announce)
- {
- char *tag = "queued";
-
- if (e->e_quarmsg != NULL)
- tag = "quarantined";
-
- e->e_to = q->q_paddr;
- message(tag);
- if (LogLevel > 8)
- logdelivery(q->q_mailer, NULL, q->q_status,
- tag, NULL, (time_t) 0, e);
- e->e_to = NULL;
- }
- if (tTd(40, 1))
- {
- sm_dprintf("queueing ");
- printaddr(sm_debug_file(), q, false);
- }
- }
-
- /*
- ** Output headers for this message.
- ** Expand macros completely here. Queue run will deal with
- ** everything as absolute headers.
- ** All headers that must be relative to the recipient
- ** can be cracked later.
- ** We set up a "null mailer" -- i.e., a mailer that will have
- ** no effect on the addresses as they are output.
- */
-
- 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";
- memset(&mcibuf, '\0', sizeof(mcibuf));
- mcibuf.mci_mailer = &nullmailer;
- mcibuf.mci_out = tfp;
-
- macdefine(&e->e_macro, A_PERM, 'g', "\201f");
- for (h = e->e_header; h != NULL; h = h->h_link)
- {
- 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))
- continue;
-
- /* expand macros; if null, don't output header at all */
- if (bitset(H_DEFAULT, h->h_flags))
- {
- (void) expand(h->h_value, buf, sizeof(buf), e);
- if (buf[0] == '\0')
- continue;
- if (buf[0] == ' ' && buf[1] == '\0')
- continue;
- }
-
- /* output this header */
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?");
-
- /* output conditional macro if present */
- if (h->h_macro != '\0')
- {
- if (bitset(0200, h->h_macro))
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
- "${%s}",
- macname(bitidx(h->h_macro)));
- else
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
- "$%c", h->h_macro);
- }
- else if (!bitzerop(h->h_mflags) &&
- bitset(H_CHECK|H_ACHECK, h->h_flags))
- {
- int j;
-
- /* if conditional, output the set of conditions */
- for (j = '\0'; j <= '\177'; j++)
- if (bitnset(j, h->h_mflags))
- (void) sm_io_putc(tfp, SM_TIME_DEFAULT,
- j);
- }
- (void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?');
-
- /* output the header: expand macros, convert addresses */
- if (bitset(H_DEFAULT, h->h_flags) &&
- !bitset(H_BINDLATE, h->h_flags))
- {
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n",
- h->h_field,
- denlstring(buf, false, true));
- }
- else if (bitset(H_FROM|H_RCPT, h->h_flags) &&
- !bitset(H_BINDLATE, h->h_flags))
- {
- bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
- SM_FILE_T *savetrace = TrafficLogFile;
-
- TrafficLogFile = NULL;
-
- if (bitset(H_FROM, h->h_flags))
- oldstyle = false;
- commaize(h, h->h_value, oldstyle, &mcibuf, e,
- PXLF_HEADER);
-
- TrafficLogFile = savetrace;
- }
- else
- {
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n",
- h->h_field,
- denlstring(h->h_value, false,
- true));
- }
- }
-
- /*
- ** Clean up.
- **
- ** Write a terminator record -- this is to prevent
- ** scurrilous crackers from appending any data.
- */
-
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n");
-
- if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 ||
- ((SuperSafe == SAFE_REALLY ||
- SuperSafe == SAFE_REALLY_POSTMILTER ||
- (SuperSafe == SAFE_INTERACTIVE && msync)) &&
- fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) ||
- sm_io_error(tfp))
- {
- if (newid)
- syserr("!552 Error writing control file %s", tf);
- else
- syserr("!452 Error writing control file %s", tf);
- }
-
- if (!newid)
- {
- char new = queue_letter(e, ANYQFL_LETTER);
-
- /* rename (locked) tf to be (locked) [qh]f */
- (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER),
- sizeof(qf));
- if (rename(tf, qf) < 0)
- syserr("cannot rename(%s, %s), uid=%d",
- tf, qf, (int) geteuid());
- else
- {
- /*
- ** Check if type has changed and only
- ** remove the old item if the rename above
- ** succeeded.
- */
-
- if (e->e_qfletter != '\0' &&
- e->e_qfletter != new)
- {
- if (tTd(40, 5))
- {
- sm_dprintf("type changed from %c to %c\n",
- e->e_qfletter, new);
- }
-
- if (unlink(queuename(e, e->e_qfletter)) < 0)
- {
- /* XXX: something more drastic? */
- if (LogLevel > 0)
- sm_syslog(LOG_ERR, e->e_id,
- "queueup: unlink(%s) failed: %s",
- queuename(e, e->e_qfletter),
- sm_errstring(errno));
- }
- }
- }
- e->e_qfletter = new;
-
- /*
- ** fsync() after renaming to make sure metadata is
- ** written to disk on filesystems in which renames are
- ** not guaranteed.
- */
-
- if (SuperSafe != SAFE_NO)
- {
- /* for softupdates */
- if (tfd >= 0 && fsync(tfd) < 0)
- {
- syserr("!queueup: cannot fsync queue temp file %s",
- tf);
- }
- SYNC_DIR(qf, true);
- }
-
- /* close and unlock old (locked) queue file */
- if (e->e_lockfp != NULL)
- (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
- e->e_lockfp = tfp;
-
- /* save log info */
- if (LogLevel > 79)
- sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf);
- }
- else
- {
- /* save log info */
- if (LogLevel > 79)
- sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf);
-
- e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
- }
-
- errno = 0;
- e->e_flags |= EF_INQUEUE;
-
- if (tTd(40, 1))
- sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
- return;
-}
-
-/*
-** PRINTCTLADDR -- print control address to file.
-**
-** Parameters:
-** a -- address.
-** tfp -- file pointer.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** The control address (if changed) is printed to the file.
-** The last control address and uid are saved.
-*/
-
-static void
-printctladdr(a, tfp)
- register ADDRESS *a;
- SM_FILE_T *tfp;
-{
- char *user;
- register ADDRESS *q;
- uid_t uid;
- gid_t gid;
- static ADDRESS *lastctladdr = NULL;
- static uid_t lastuid;
-
- /* initialization */
- if (a == NULL || a->q_alias == NULL || tfp == NULL)
- {
- if (lastctladdr != NULL && tfp != NULL)
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n");
- lastctladdr = NULL;
- lastuid = 0;
- return;
- }
-
- /* find the active uid */
- q = getctladdr(a);
- if (q == NULL)
- {
- user = NULL;
- uid = 0;
- gid = 0;
- }
- else
- {
- user = q->q_ruser != NULL ? q->q_ruser : q->q_user;
- uid = q->q_uid;
- gid = q->q_gid;
- }
- a = a->q_alias;
-
- /* check to see if this is the same as last time */
- if (lastctladdr != NULL && uid == lastuid &&
- strcmp(lastctladdr->q_paddr, a->q_paddr) == 0)
- return;
- lastuid = uid;
- lastctladdr = a;
-
- if (uid == 0 || user == NULL || user[0] == '\0')
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C");
- else
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld",
- denlstring(user, true, false), (long) uid,
- (long) gid);
- (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n",
- denlstring(a->q_paddr, true, false));
-}
-
-/*
-** RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process
-**
-** This propagates the signal to the child processes that are queue
-** runners. This is for a queue runner "cleanup". After all of the
-** child queue runner processes are signaled (it should be SIGTERM
-** being the sig) then the old signal handler (Oldsh) is called
-** to handle any cleanup set for this process (provided it is not
-** SIG_DFL or SIG_IGN). The signal may not be handled immediately
-** if the BlockOldsh flag is set. If the current process doesn't
-** have a parent then handle the signal immediately, regardless of
-** BlockOldsh.
-**
-** Parameters:
-** sig -- the signal number being sent
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets the NoMoreRunners boolean to true to stop more runners
-** from being started in runqueue().
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-static bool volatile NoMoreRunners = false;
-static sigfunc_t Oldsh_term = SIG_DFL;
-static sigfunc_t Oldsh_hup = SIG_DFL;
-static sigfunc_t volatile Oldsh = SIG_DFL;
-static bool BlockOldsh = false;
-static int volatile Oldsig = 0;
-static SIGFUNC_DECL runners_sigterm __P((int));
-static SIGFUNC_DECL runners_sighup __P((int));
-
-static SIGFUNC_DECL
-runners_sigterm(sig)
- int sig;
-{
- int save_errno = errno;
-
- FIX_SYSV_SIGNAL(sig, runners_sigterm);
- errno = save_errno;
- CHECK_CRITICAL(sig);
- NoMoreRunners = true;
- Oldsh = Oldsh_term;
- Oldsig = sig;
- proc_list_signal(PROC_QUEUE, sig);
-
- if (!BlockOldsh || getppid() <= 1)
- {
- /* Check that a valid 'old signal handler' is callable */
- if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN &&
- Oldsh_term != runners_sigterm)
- (*Oldsh_term)(sig);
- }
- errno = save_errno;
- return SIGFUNC_RETURN;
-}
-/*
-** RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process
-**
-** This propagates the signal to the child processes that are queue
-** runners. This is for a queue runner "cleanup". After all of the
-** child queue runner processes are signaled (it should be SIGHUP
-** being the sig) then the old signal handler (Oldsh) is called to
-** handle any cleanup set for this process (provided it is not SIG_DFL
-** or SIG_IGN). The signal may not be handled immediately if the
-** BlockOldsh flag is set. If the current process doesn't have
-** a parent then handle the signal immediately, regardless of
-** BlockOldsh.
-**
-** Parameters:
-** sig -- the signal number being sent
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets the NoMoreRunners boolean to true to stop more runners
-** from being started in runqueue().
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-static SIGFUNC_DECL
-runners_sighup(sig)
- int sig;
-{
- int save_errno = errno;
-
- FIX_SYSV_SIGNAL(sig, runners_sighup);
- errno = save_errno;
- CHECK_CRITICAL(sig);
- NoMoreRunners = true;
- Oldsh = Oldsh_hup;
- Oldsig = sig;
- proc_list_signal(PROC_QUEUE, sig);
-
- if (!BlockOldsh || getppid() <= 1)
- {
- /* Check that a valid 'old signal handler' is callable */
- if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN &&
- Oldsh_hup != runners_sighup)
- (*Oldsh_hup)(sig);
- }
- errno = save_errno;
- return SIGFUNC_RETURN;
-}
-/*
-** MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart
-**
-** Sets a workgroup for restarting.
-**
-** Parameters:
-** wgrp -- the work group id to restart.
-** reason -- why (signal?), -1 to turn off restart
-**
-** Returns:
-** none.
-**
-** Side effects:
-** May set global RestartWorkGroup to true.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-void
-mark_work_group_restart(wgrp, reason)
- int wgrp;
- int reason;
-{
- if (wgrp < 0 || wgrp > NumWorkGroups)
- return;
-
- WorkGrp[wgrp].wg_restart = reason;
- if (reason >= 0)
- RestartWorkGroup = true;
-}
-/*
-** RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart
-**
-** Restart any workgroup marked as needing a restart provided more
-** runners are allowed.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side effects:
-** Sets global RestartWorkGroup to false.
-*/
-
-void
-restart_marked_work_groups()
-{
- int i;
- int wasblocked;
-
- if (NoMoreRunners)
- return;
-
- /* Block SIGCHLD so reapchild() doesn't mess with us */
- wasblocked = sm_blocksignal(SIGCHLD);
-
- for (i = 0; i < NumWorkGroups; i++)
- {
- if (WorkGrp[i].wg_restart >= 0)
- {
- if (LogLevel > 8)
- sm_syslog(LOG_ERR, NOQID,
- "restart queue runner=%d due to signal 0x%x",
- i, WorkGrp[i].wg_restart);
- restart_work_group(i);
- }
- }
- RestartWorkGroup = false;
-
- if (wasblocked == 0)
- (void) sm_releasesignal(SIGCHLD);
-}
-/*
-** RESTART_WORK_GROUP -- restart a specific work group
-**
-** Restart a specific workgroup provided more runners are allowed.
-** If the requested work group has been restarted too many times log
-** this and refuse to restart.
-**
-** Parameters:
-** wgrp -- the work group id to restart
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** starts another process doing the work of wgrp
-*/
-
-#define MAX_PERSIST_RESTART 10 /* max allowed number of restarts */
-
-static void
-restart_work_group(wgrp)
- int wgrp;
-{
- if (NoMoreRunners ||
- wgrp < 0 || wgrp > NumWorkGroups)
- return;
-
- WorkGrp[wgrp].wg_restart = -1;
- if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART)
- {
- /* avoid overflow; increment here */
- WorkGrp[wgrp].wg_restartcnt++;
- (void) run_work_group(wgrp, RWG_FORK|RWG_PERSISTENT|RWG_RUNALL);
- }
- else
- {
- sm_syslog(LOG_ERR, NOQID,
- "ERROR: persistent queue runner=%d restarted too many times, queue runner lost",
- wgrp);
- }
-}
-/*
-** SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group.
-**
-** Parameters:
-** runall -- schedule even if individual bit is not set.
-** wgrp -- the work group id to schedule.
-** didit -- the queue run was performed for this work group.
-**
-** Returns:
-** nothing
-*/
-
-#define INCR_MOD(v, m) if (++v >= m) \
- v = 0; \
- else
-
-static void
-schedule_queue_runs(runall, wgrp, didit)
- bool runall;
- int wgrp;
- bool didit;
-{
- int qgrp, cgrp, endgrp;
-#if _FFR_QUEUE_SCHED_DBG
- time_t lastsched;
- bool sched;
-#endif /* _FFR_QUEUE_SCHED_DBG */
- time_t now;
- time_t minqintvl;
-
- /*
- ** This is a bit ugly since we have to duplicate the
- ** code that "walks" through a work queue group.
- */
-
- now = curtime();
- minqintvl = 0;
- cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp;
- do
- {
- time_t qintvl;
-
-#if _FFR_QUEUE_SCHED_DBG
- lastsched = 0;
- sched = false;
-#endif /* _FFR_QUEUE_SCHED_DBG */
- qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index;
- if (Queue[qgrp]->qg_queueintvl > 0)
- qintvl = Queue[qgrp]->qg_queueintvl;
- else if (QueueIntvl > 0)
- qintvl = QueueIntvl;
- else
- qintvl = (time_t) 0;
-#if _FFR_QUEUE_SCHED_DBG
- lastsched = Queue[qgrp]->qg_nextrun;
-#endif /* _FFR_QUEUE_SCHED_DBG */
- if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0)
- {
-#if _FFR_QUEUE_SCHED_DBG
- sched = true;
-#endif /* _FFR_QUEUE_SCHED_DBG */
- if (minqintvl == 0 || qintvl < minqintvl)
- minqintvl = qintvl;
-
- /*
- ** Only set a new time if a queue run was performed
- ** for this queue group. If the queue was not run,
- ** we could starve it by setting a new time on each
- ** call.
- */
-
- if (didit)
- Queue[qgrp]->qg_nextrun += qintvl;
- }
-#if _FFR_QUEUE_SCHED_DBG
- if (tTd(69, 10))
- sm_syslog(LOG_INFO, NOQID,
- "sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d",
- wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl,
- QueueIntvl, runall, lastsched,
- Queue[qgrp]->qg_nextrun, sched);
-#endif /* _FFR_QUEUE_SCHED_DBG */
- INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp);
- } while (endgrp != cgrp);
- if (minqintvl > 0)
- (void) sm_setevent(minqintvl, runqueueevent, 0);
-}
-
-#if _FFR_QUEUE_RUN_PARANOIA
-/*
-** CHECKQUEUERUNNER -- check whether a queue group hasn't been run.
-**
-** Use this if events may get lost and hence queue runners may not
-** be started and mail will pile up in a queue.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** true if a queue run is necessary.
-**
-** Side Effects:
-** may schedule a queue run.
-*/
-
-bool
-checkqueuerunner()
-{
- int qgrp;
- time_t now, minqintvl;
-
- now = curtime();
- minqintvl = 0;
- for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
- {
- time_t qintvl;
-
- if (Queue[qgrp]->qg_queueintvl > 0)
- qintvl = Queue[qgrp]->qg_queueintvl;
- else if (QueueIntvl > 0)
- qintvl = QueueIntvl;
- else
- qintvl = (time_t) 0;
- if (Queue[qgrp]->qg_nextrun <= now - qintvl)
- {
- if (minqintvl == 0 || qintvl < minqintvl)
- minqintvl = qintvl;
- if (LogLevel > 1)
- sm_syslog(LOG_WARNING, NOQID,
- "checkqueuerunner: queue %d should have been run at %s, queue interval %ld",
- qgrp,
- arpadate(ctime(&Queue[qgrp]->qg_nextrun)),
- qintvl);
- }
- }
- if (minqintvl > 0)
- {
- (void) sm_setevent(minqintvl, runqueueevent, 0);
- return true;
- }
- return false;
-}
-#endif /* _FFR_QUEUE_RUN_PARANOIA */
-
-/*
-** RUNQUEUE -- run the jobs in the queue.
-**
-** Gets the stuff out of the queue in some presumably logical
-** order and processes them.
-**
-** Parameters:
-** forkflag -- true if the queue scanning should be done in
-** 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.
-** persistent -- persistent queue runner?
-** runall -- run all groups or only a subset (DoQueueRun)?
-**
-** Returns:
-** true if the queue run successfully began.
-**
-** Side Effects:
-** runs things in the mail queue using run_work_group().
-** maybe schedules next queue run.
-*/
-
-static ENVELOPE QueueEnvelope; /* the queue run envelope */
-static time_t LastQueueTime = 0; /* last time a queue ID assigned */
-static pid_t LastQueuePid = -1; /* last PID which had a queue ID */
-
-/* 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 */
-
-bool
-runqueue(forkflag, verbose, persistent, runall)
- bool forkflag;
- bool verbose;
- bool persistent;
- bool runall;
-{
- int i;
- bool ret = true;
- static int curnum = 0;
- sigfunc_t cursh;
-#if SM_HEAP_CHECK
- SM_NONVOLATILE int oldgroup = 0;
-
- if (sm_debug_active(&DebugLeakQ, 1))
- {
- oldgroup = sm_heap_group();
- sm_heap_newgroup();
- sm_dprintf("runqueue() heap group #%d\n", sm_heap_group());
- }
-#endif /* SM_HEAP_CHECK */
-
- /* queue run has been started, don't do any more this time */
- DoQueueRun = false;
-
- /* more than one queue or more than one directory per queue */
- if (!forkflag && !verbose &&
- (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 ||
- WorkGrp[0].wg_numqgrp > 1))
- forkflag = true;
-
- /*
- ** For controlling queue runners via signals sent to this process.
- ** Oldsh* will get called too by runners_sig* (if it is not SIG_IGN
- ** or SIG_DFL) to preserve cleanup behavior. Now that this process
- ** will have children (and perhaps grandchildren) this handler will
- ** be left in place. This is because this process, once it has
- ** finished spinning off queue runners, may go back to doing something
- ** else (like being a daemon). And we still want on a SIG{TERM,HUP} to
- ** clean up the child queue runners. Only install 'runners_sig*' once
- ** else we'll get stuck looping forever.
- */
-
- cursh = sm_signal(SIGTERM, runners_sigterm);
- if (cursh != runners_sigterm)
- Oldsh_term = cursh;
- cursh = sm_signal(SIGHUP, runners_sighup);
- if (cursh != runners_sighup)
- Oldsh_hup = cursh;
-
- for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++)
- {
- int rwgflags = RWG_NONE;
-
- /*
- ** If MaxQueueChildren active then test whether the start
- ** of the next queue group's additional queue runners (maximum)
- ** will result in MaxQueueChildren being exceeded.
- **
- ** Note: do not use continue; even though another workgroup
- ** may have fewer queue runners, this would be "unfair",
- ** i.e., this work group might "starve" then.
- */
-
-#if _FFR_QUEUE_SCHED_DBG
- if (tTd(69, 10))
- sm_syslog(LOG_INFO, NOQID,
- "rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d",
- curnum, MaxQueueChildren, CurRunners,
- WorkGrp[curnum].wg_maxact);
-#endif /* _FFR_QUEUE_SCHED_DBG */
- if (MaxQueueChildren > 0 &&
- CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren)
- break;
-
- /*
- ** Pick up where we left off (curnum), in case we
- ** used up all the children last time without finishing.
- ** This give a round-robin fairness to queue runs.
- **
- ** Increment CurRunners before calling run_work_group()
- ** to avoid a "race condition" with proc_list_drop() which
- ** decrements CurRunners if the queue runners terminate.
- ** Notice: CurRunners is an upper limit, in some cases
- ** (too few jobs in the queue) this value is larger than
- ** the actual number of queue runners. The discrepancy can
- ** increase if some queue runners "hang" for a long time.
- */
-
- CurRunners += WorkGrp[curnum].wg_maxact;
- if (forkflag)
- rwgflags |= RWG_FORK;
- if (verbose)
- rwgflags |= RWG_VERBOSE;
- if (persistent)
- rwgflags |= RWG_PERSISTENT;
- if (runall)
- rwgflags |= RWG_RUNALL;
- ret = run_work_group(curnum, rwgflags);
-
- /*
- ** Failure means a message was printed for ETRN
- ** and subsequent queues are likely to fail as well.
- ** Decrement CurRunners in that case because
- ** none have been started.
- */
-
- if (!ret)
- {
- CurRunners -= WorkGrp[curnum].wg_maxact;
- break;
- }
-
- if (!persistent)
- schedule_queue_runs(runall, curnum, true);
- INCR_MOD(curnum, NumWorkGroups);
- }
-
- /* schedule left over queue runs */
- if (i < NumWorkGroups && !NoMoreRunners && !persistent)
- {
- int h;
-
- for (h = curnum; i < NumWorkGroups; i++)
- {
- schedule_queue_runs(runall, h, false);
- INCR_MOD(h, NumWorkGroups);
- }
- }
-
-
-#if SM_HEAP_CHECK
- if (sm_debug_active(&DebugLeakQ, 1))
- sm_heap_setgroup(oldgroup);
-#endif /* SM_HEAP_CHECK */
- return ret;
-}
-
-#if _FFR_SKIP_DOMAINS
-/*
-** SKIP_DOMAINS -- Skip 'skip' number of domains in the WorkQ.
-**
-** Added by Stephen Frost <sfrost@snowman.net> to support
-** having each runner process every N'th domain instead of
-** every N'th message.
-**
-** Parameters:
-** skip -- number of domains in WorkQ to skip.
-**
-** Returns:
-** total number of messages skipped.
-**
-** Side Effects:
-** may change WorkQ
-*/
-
-static int
-skip_domains(skip)
- int skip;
-{
- int n, seqjump;
-
- for (n = 0, seqjump = 0; n < skip && WorkQ != NULL; seqjump++)
- {
- if (WorkQ->w_next != NULL)
- {
- if (WorkQ->w_host != NULL &&
- WorkQ->w_next->w_host != NULL)
- {
- if (sm_strcasecmp(WorkQ->w_host,
- WorkQ->w_next->w_host) != 0)
- n++;
- }
- else
- {
- if ((WorkQ->w_host != NULL &&
- WorkQ->w_next->w_host == NULL) ||
- (WorkQ->w_host == NULL &&
- WorkQ->w_next->w_host != NULL))
- n++;
- }
- }
- WorkQ = WorkQ->w_next;
- }
- return seqjump;
-}
-#endif /* _FFR_SKIP_DOMAINS */
-
-/*
-** RUNNER_WORK -- have a queue runner do its work
-**
-** Have a queue runner do its work a list of entries.
-** When work isn't directly being done then this process can take a signal
-** and terminate immediately (in a clean fashion of course).
-** When work is directly being done, it's not to be interrupted
-** immediately: the work should be allowed to finish at a clean point
-** before termination (in a clean fashion of course).
-**
-** Parameters:
-** e -- envelope.
-** sequenceno -- 'th process to run WorkQ.
-** didfork -- did the calling process fork()?
-** skip -- process only each skip'th item.
-** njobs -- number of jobs in WorkQ.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** runs things in the mail queue.
-*/
-
-static void
-runner_work(e, sequenceno, didfork, skip, njobs)
- register ENVELOPE *e;
- int sequenceno;
- bool didfork;
- int skip;
- int njobs;
-{
- int n, seqjump;
- WORK *w;
- time_t now;
-
- SM_GET_LA(now);
-
- /*
- ** Here we temporarily block the second calling of the handlers.
- ** This allows us to handle the signal without terminating in the
- ** middle of direct work. If a signal does come, the test for
- ** NoMoreRunners will find it.
- */
-
- BlockOldsh = true;
- seqjump = skip;
-
- /* process them once at a time */
- while (WorkQ != NULL)
- {
-#if SM_HEAP_CHECK
- SM_NONVOLATILE int oldgroup = 0;
-
- if (sm_debug_active(&DebugLeakQ, 1))
- {
- oldgroup = sm_heap_group();
- sm_heap_newgroup();
- sm_dprintf("run_queue_group() heap group #%d\n",
- sm_heap_group());
- }
-#endif /* SM_HEAP_CHECK */
-
- /* do no more work */
- if (NoMoreRunners)
- {
- /* Check that a valid signal handler is callable */
- if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
- Oldsh != runners_sighup &&
- Oldsh != runners_sigterm)
- (*Oldsh)(Oldsig);
- break;
- }
-
- w = WorkQ; /* assign current work item */
-
- /*
- ** Set the head of the WorkQ to the next work item.
- ** It is set 'skip' ahead (the number of parallel queue
- ** runners working on WorkQ together) since each runner
- ** works on every 'skip'th (N-th) item.
-#if _FFR_SKIP_DOMAINS
- ** In the case of the BYHOST Queue Sort Order, the 'item'
- ** is a domain, so we work on every 'skip'th (N-th) domain.
-#endif * _FFR_SKIP_DOMAINS *
- */
-
-#if _FFR_SKIP_DOMAINS
- if (QueueSortOrder == QSO_BYHOST)
- {
- seqjump = 1;
- if (WorkQ->w_next != NULL)
- {
- if (WorkQ->w_host != NULL &&
- WorkQ->w_next->w_host != NULL)
- {
- if (sm_strcasecmp(WorkQ->w_host,
- WorkQ->w_next->w_host)
- != 0)
- seqjump = skip_domains(skip);
- else
- WorkQ = WorkQ->w_next;
- }
- else
- {
- if ((WorkQ->w_host != NULL &&
- WorkQ->w_next->w_host == NULL) ||
- (WorkQ->w_host == NULL &&
- WorkQ->w_next->w_host != NULL))
- seqjump = skip_domains(skip);
- else
- WorkQ = WorkQ->w_next;
- }
- }
- else
- WorkQ = WorkQ->w_next;
- }
- else
-#endif /* _FFR_SKIP_DOMAINS */
- {
- for (n = 0; n < skip && WorkQ != NULL; n++)
- WorkQ = WorkQ->w_next;
- }
-
- e->e_to = NULL;
-
- /*
- ** Ignore jobs that are too expensive for the moment.
- **
- ** Get new load average every GET_NEW_LA_TIME seconds.
- */
-
- SM_GET_LA(now);
- if (shouldqueue(WkRecipFact, Current_LA_time))
- {
- char *msg = "Aborting queue run: load average too high";
-
- if (Verbose)
- message("%s", msg);
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
- break;
- }
- if (shouldqueue(w->w_pri, w->w_ctime))
- {
- if (Verbose)
- message(EmptyString);
- if (QueueSortOrder == QSO_BYPRIORITY)
- {
- if (Verbose)
- message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue",
- qid_printqueue(w->w_qgrp,
- w->w_qdir),
- w->w_name + 2, sequenceno,
- njobs);
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)",
- qid_printqueue(w->w_qgrp,
- w->w_qdir),
- w->w_name + 2, w->w_pri,
- CurrentLA, sequenceno,
- njobs);
- break;
- }
- else if (Verbose)
- message("Skipping %s/%s (sequence %d of %d)",
- qid_printqueue(w->w_qgrp, w->w_qdir),
- w->w_name + 2, sequenceno, njobs);
- }
- else
- {
- if (Verbose)
- {
- message(EmptyString);
- message("Running %s/%s (sequence %d of %d)",
- qid_printqueue(w->w_qgrp, w->w_qdir),
- w->w_name + 2, sequenceno, njobs);
- }
- if (didfork && MaxQueueChildren > 0)
- {
- sm_blocksignal(SIGCHLD);
- (void) sm_signal(SIGCHLD, reapchild);
- }
- if (tTd(63, 100))
- sm_syslog(LOG_DEBUG, NOQID,
- "runqueue %s dowork(%s)",
- qid_printqueue(w->w_qgrp, w->w_qdir),
- w->w_name + 2);
-
- (void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2,
- ForkQueueRuns, false, e);
- errno = 0;
- }
- sm_free(w->w_name); /* XXX */
- if (w->w_host != NULL)
- sm_free(w->w_host); /* XXX */
- sm_free((char *) w); /* XXX */
- sequenceno += seqjump; /* next sequence number */
-#if SM_HEAP_CHECK
- if (sm_debug_active(&DebugLeakQ, 1))
- sm_heap_setgroup(oldgroup);
-#endif /* SM_HEAP_CHECK */
- }
-
- BlockOldsh = false;
-
- /* check the signals didn't happen during the revert */
- if (NoMoreRunners)
- {
- /* Check that a valid signal handler is callable */
- if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
- Oldsh != runners_sighup && Oldsh != runners_sigterm)
- (*Oldsh)(Oldsig);
- }
-
- Oldsh = SIG_DFL; /* after the NoMoreRunners check */
-}
-/*
-** RUN_WORK_GROUP -- run the jobs in a queue group from a work group.
-**
-** Gets the stuff out of the queue in some presumably logical
-** order and processes them.
-**
-** Parameters:
-** wgrp -- work group to process.
-** flags -- RWG_* flags
-**
-** Returns:
-** true if the queue run successfully began.
-**
-** Side Effects:
-** runs things in the mail queue.
-*/
-
-/* Minimum sleep time for persistent queue runners */
-#define MIN_SLEEP_TIME 5
-
-bool
-run_work_group(wgrp, flags)
- int wgrp;
- int flags;
-{
- register ENVELOPE *e;
- int njobs, qdir;
- int sequenceno = 1;
- int qgrp, endgrp, h, i;
- time_t now;
- bool full, more;
- SM_RPOOL_T *rpool;
- extern ENVELOPE BlankEnvelope;
- extern SIGFUNC_DECL reapchild __P((int));
-
- if (wgrp < 0)
- return false;
-
- /*
- ** If no work will ever be selected, don't even bother reading
- ** the queue.
- */
-
- SM_GET_LA(now);
-
- if (!bitset(RWG_PERSISTENT, flags) &&
- shouldqueue(WkRecipFact, Current_LA_time))
- {
- char *msg = "Skipping queue run -- load average too high";
-
- if (bitset(RWG_VERBOSE, flags))
- message("458 %s\n", msg);
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
- return false;
- }
-
- /*
- ** See if we already have too many children.
- */
-
- if (bitset(RWG_FORK, flags) &&
- WorkGrp[wgrp].wg_lowqintvl > 0 &&
- !bitset(RWG_PERSISTENT, flags) &&
- MaxChildren > 0 && CurChildren >= MaxChildren)
- {
- char *msg = "Skipping queue run -- too many children";
-
- if (bitset(RWG_VERBOSE, flags))
- message("458 %s (%d)\n", msg, CurChildren);
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)",
- msg, CurChildren);
- return false;
- }
-
- /*
- ** See if we want to go off and do other useful work.
- */
-
- if (bitset(RWG_FORK, flags))
- {
- pid_t pid;
-
- (void) sm_blocksignal(SIGCHLD);
- (void) sm_signal(SIGCHLD, reapchild);
-
- pid = dofork();
- if (pid == -1)
- {
- const char *msg = "Skipping queue run -- fork() failed";
- const char *err = sm_errstring(errno);
-
- if (bitset(RWG_VERBOSE, flags))
- message("458 %s: %s\n", msg, err);
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s",
- msg, err);
- (void) sm_releasesignal(SIGCHLD);
- return false;
- }
- if (pid != 0)
- {
- /* parent -- pick up intermediate zombie */
- (void) sm_blocksignal(SIGALRM);
-
- /* wgrp only used when queue runners are persistent */
- proc_list_add(pid, "Queue runner", PROC_QUEUE,
- WorkGrp[wgrp].wg_maxact,
- bitset(RWG_PERSISTENT, flags) ? wgrp : -1,
- NULL);
- (void) sm_releasesignal(SIGALRM);
- (void) sm_releasesignal(SIGCHLD);
- return true;
- }
-
- /* child -- clean up signals */
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- CurrentPid = getpid();
- close_sendmail_pid();
-
- /*
- ** Initialize exception stack and default exception
- ** handler for child process.
- */
-
- sm_exc_newthread(fatal_error);
- clrcontrol();
- proc_list_clear();
-
- /* Add parent process as first child item */
- proc_list_add(CurrentPid, "Queue runner child process",
- PROC_QUEUE_CHILD, 0, -1, NULL);
- (void) sm_releasesignal(SIGCHLD);
- (void) sm_signal(SIGCHLD, SIG_DFL);
- (void) sm_signal(SIGHUP, SIG_DFL);
- (void) sm_signal(SIGTERM, intsig);
- }
-
- /*
- ** Release any resources used by the daemon code.
- */
-
- clrdaemon();
-
- /* force it to run expensive jobs */
- NoConnect = false;
-
- /* drop privileges */
- if (geteuid() == (uid_t) 0)
- (void) drop_privileges(false);
-
- /*
- ** Create ourselves an envelope
- */
-
- CurEnv = &QueueEnvelope;
- rpool = sm_rpool_new_x(NULL);
- e = newenvelope(&QueueEnvelope, CurEnv, rpool);
- e->e_flags = BlankEnvelope.e_flags;
- e->e_parent = NULL;
-
- /* make sure we have disconnected from parent */
- if (bitset(RWG_FORK, flags))
- {
- disconnect(1, e);
- QuickAbort = false;
- }
-
- /*
- ** If we are running part of the queue, always ignore stored
- ** host status.
- */
-
- if (QueueLimitId != NULL || QueueLimitSender != NULL ||
- QueueLimitQuarantine != NULL ||
- QueueLimitRecipient != NULL)
- {
- IgnoreHostStatus = true;
- MinQueueAge = 0;
- }
-
- /*
- ** Here is where we choose the queue group from the work group.
- ** The caller of the "domorework" label must setup a new envelope.
- */
-
- endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */
-
- domorework:
-
- /*
- ** Run a queue group if:
- ** RWG_RUNALL bit is set or the bit for this group is set.
- */
-
- now = curtime();
- for (;;)
- {
- /*
- ** Find the next queue group within the work group that
- ** has been marked as needing a run.
- */
-
- qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index;
- WorkGrp[wgrp].wg_curqgrp++; /* advance */
- WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */
- if (bitset(RWG_RUNALL, flags) ||
- (Queue[qgrp]->qg_nextrun <= now &&
- Queue[qgrp]->qg_nextrun != (time_t) -1))
- break;
- if (endgrp == WorkGrp[wgrp].wg_curqgrp)
- {
- e->e_id = NULL;
- if (bitset(RWG_FORK, flags))
- finis(true, true, ExitStat);
- return true; /* we're done */
- }
- }
-
- qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */
-#if _FFR_QUEUE_SCHED_DBG
- if (tTd(69, 12))
- sm_syslog(LOG_INFO, NOQID,
- "rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d",
- wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir),
- WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp);
-#endif /* _FFR_QUEUE_SCHED_DBG */
-
-#if HASNICE
- /* tweak niceness of queue runs */
- if (Queue[qgrp]->qg_nice > 0)
- (void) nice(Queue[qgrp]->qg_nice);
-#endif /* HASNICE */
-
- /* XXX running queue group... */
- sm_setproctitle(true, CurEnv, "running queue: %s",
- qid_printqueue(qgrp, qdir));
-
- if (LogLevel > 69 || tTd(63, 99))
- sm_syslog(LOG_DEBUG, NOQID,
- "runqueue %s, pid=%d, forkflag=%d",
- qid_printqueue(qgrp, qdir), (int) CurrentPid,
- bitset(RWG_FORK, flags));
-
- /*
- ** Start making passes through the queue.
- ** First, read and sort the entire queue.
- ** Then, process the work in that order.
- ** But if you take too long, start over.
- */
-
- for (i = 0; i < Queue[qgrp]->qg_numqueues; i++)
- {
- h = gatherq(qgrp, qdir, false, &full, &more);
-#if SM_CONF_SHM
- if (ShmId != SM_SHM_NO_ID)
- QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h;
-#endif /* SM_CONF_SHM */
- /* If there are no more items in this queue advance */
- if (!more)
- {
- /* A round-robin advance */
- qdir++;
- qdir %= Queue[qgrp]->qg_numqueues;
- }
-
- /* Has the WorkList reached the limit? */
- if (full)
- break; /* don't try to gather more */
- }
-
- /* order the existing work requests */
- njobs = sortq(Queue[qgrp]->qg_maxlist);
- Queue[qgrp]->qg_curnum = qdir; /* update */
-
-
- if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags))
- {
- int loop, maxrunners;
- pid_t pid;
-
- /*
- ** For this WorkQ we want to fork off N children (maxrunners)
- ** at this point. Each child has a copy of WorkQ. Each child
- ** will process every N-th item. The parent will wait for all
- ** of the children to finish before moving on to the next
- ** queue group within the work group. This saves us forking
- ** a new runner-child for each work item.
- ** It's valid for qg_maxqrun == 0 since this may be an
- ** explicit "don't run this queue" setting.
- */
-
- maxrunners = Queue[qgrp]->qg_maxqrun;
-
- /*
- ** If no runners are configured for this group but
- ** the queue is "forced" then lets use 1 runner.
- */
-
- if (maxrunners == 0 && bitset(RWG_FORCE, flags))
- maxrunners = 1;
-
- /* No need to have more runners then there are jobs */
- if (maxrunners > njobs)
- maxrunners = njobs;
- for (loop = 0; loop < maxrunners; loop++)
- {
- /*
- ** Since the delivery may happen in a child and the
- ** parent does not wait, the parent may close the
- ** maps thereby removing any shared memory used by
- ** the map. Therefore, close the maps now so the
- ** child will dynamically open them if necessary.
- */
-
- closemaps(false);
-
- pid = fork();
- if (pid < 0)
- {
- syserr("run_work_group: cannot fork");
- return false;
- }
- else if (pid > 0)
- {
- /* parent -- clean out connection cache */
- mci_flush(false, NULL);
-#if _FFR_SKIP_DOMAINS
- if (QueueSortOrder == QSO_BYHOST)
- {
- sequenceno += skip_domains(1);
- }
- else
-#endif /* _FFR_SKIP_DOMAINS */
- {
- /* for the skip */
- WorkQ = WorkQ->w_next;
- sequenceno++;
- }
- proc_list_add(pid, "Queue child runner process",
- PROC_QUEUE_CHILD, 0, -1, NULL);
-
- /* No additional work, no additional runners */
- if (WorkQ == NULL)
- break;
- }
- else
- {
- /* child -- Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- CurrentPid = getpid();
- close_sendmail_pid();
-
- /*
- ** Initialize exception stack and default
- ** exception handler for child process.
- ** When fork()'d the child now has a private
- ** copy of WorkQ at its current position.
- */
-
- sm_exc_newthread(fatal_error);
-
- /*
- ** SMTP processes (whether -bd or -bs) set
- ** SIGCHLD to reapchild to collect
- ** children status. However, at delivery
- ** time, that status must be collected
- ** by sm_wait() to be dealt with properly
- ** (check success of delivery based
- ** on status code, etc). Therefore, if we
- ** are an SMTP process, reset SIGCHLD
- ** back to the default so reapchild
- ** doesn't collect status before
- ** sm_wait().
- */
-
- if (OpMode == MD_SMTP ||
- OpMode == MD_DAEMON ||
- MaxQueueChildren > 0)
- {
- proc_list_clear();
- sm_releasesignal(SIGCHLD);
- (void) sm_signal(SIGCHLD, SIG_DFL);
- }
-
- /* child -- error messages to the transcript */
- QuickAbort = OnlyOneError = false;
- runner_work(e, sequenceno, true,
- maxrunners, njobs);
-
- /* This child is done */
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- }
-
- sm_releasesignal(SIGCHLD);
-
- /*
- ** Wait until all of the runners have completed before
- ** seeing if there is another queue group in the
- ** work group to process.
- ** XXX Future enhancement: don't wait() for all children
- ** here, just go ahead and make sure that overall the number
- ** of children is not exceeded.
- */
-
- while (CurChildren > 0)
- {
- int status;
- pid_t ret;
-
- while ((ret = sm_wait(&status)) <= 0)
- continue;
- proc_list_drop(ret, status, NULL);
- }
- }
- else if (Queue[qgrp]->qg_maxqrun > 0 || bitset(RWG_FORCE, flags))
- {
- /*
- ** When current process will not fork children to do the work,
- ** it will do the work itself. The 'skip' will be 1 since
- ** there are no child runners to divide the work across.
- */
-
- runner_work(e, sequenceno, false, 1, njobs);
- }
-
- /* free memory allocated by newenvelope() above */
- sm_rpool_free(rpool);
- QueueEnvelope.e_rpool = NULL;
-
- /* Are there still more queues in the work group to process? */
- if (endgrp != WorkGrp[wgrp].wg_curqgrp)
- {
- rpool = sm_rpool_new_x(NULL);
- e = newenvelope(&QueueEnvelope, CurEnv, rpool);
- e->e_flags = BlankEnvelope.e_flags;
- goto domorework;
- }
-
- /* No more queues in work group to process. Now check persistent. */
- if (bitset(RWG_PERSISTENT, flags))
- {
- sequenceno = 1;
- sm_setproctitle(true, CurEnv, "running queue: %s",
- qid_printqueue(qgrp, qdir));
-
- /*
- ** close bogus maps, i.e., maps which caused a tempfail,
- ** so we get fresh map connections on the next lookup.
- ** closemaps() is also called when children are started.
- */
-
- closemaps(true);
-
- /* Close any cached connections. */
- mci_flush(true, NULL);
-
- /* Clean out expired related entries. */
- rmexpstab();
-
-#if NAMED_BIND
- /* Update MX records for FallbackMX. */
- if (FallbackMX != NULL)
- (void) getfallbackmxrr(FallbackMX);
-#endif /* NAMED_BIND */
-
-#if USERDB
- /* close UserDatabase */
- _udbx_close();
-#endif /* USERDB */
-
-#if SM_HEAP_CHECK
- if (sm_debug_active(&SmHeapCheck, 2)
- && access("memdump", F_OK) == 0
- )
- {
- SM_FILE_T *out;
-
- remove("memdump");
- out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
- "memdump.out", SM_IO_APPEND, NULL);
- if (out != NULL)
- {
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
- sm_heap_report(out,
- sm_debug_level(&SmHeapCheck) - 1);
- (void) sm_io_close(out, SM_TIME_DEFAULT);
- }
- }
-#endif /* SM_HEAP_CHECK */
-
- /* let me rest for a second to catch my breath */
- if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME)
- sleep(MIN_SLEEP_TIME);
- else if (WorkGrp[wgrp].wg_lowqintvl <= 0)
- sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME);
- else
- sleep(WorkGrp[wgrp].wg_lowqintvl);
-
- /*
- ** Get the LA outside the WorkQ loop if necessary.
- ** In a persistent queue runner the code is repeated over
- ** and over but gatherq() may ignore entries due to
- ** shouldqueue() (do we really have to do this twice?).
- ** Hence the queue runners would just idle around when once
- ** CurrentLA caused all entries in a queue to be ignored.
- */
-
- if (njobs == 0)
- SM_GET_LA(now);
- rpool = sm_rpool_new_x(NULL);
- e = newenvelope(&QueueEnvelope, CurEnv, rpool);
- e->e_flags = BlankEnvelope.e_flags;
- goto domorework;
- }
-
- /* exit without the usual cleanup */
- e->e_id = NULL;
- if (bitset(RWG_FORK, flags))
- finis(true, true, ExitStat);
- /* NOTREACHED */
- return true;
-}
-
-/*
-** DOQUEUERUN -- do a queue run?
-*/
-
-bool
-doqueuerun()
-{
- return DoQueueRun;
-}
-
-/*
-** RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** The invocation of this function via an alarm may interrupt
-** a set of actions. Thus errno may be set in that context.
-** We need to restore errno at the end of this function to ensure
-** that any work done here that sets errno doesn't return a
-** misleading/false errno value. Errno may be EINTR upon entry to
-** this function because of non-restartable/continuable system
-** API was active. Iff this is true we will override errno as
-** a timeout (as a more accurate error message).
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-void
-runqueueevent(ignore)
- int ignore;
-{
- int save_errno = errno;
-
- /*
- ** Set the general bit that we want a queue run,
- ** tested in doqueuerun()
- */
-
- DoQueueRun = true;
-#if _FFR_QUEUE_SCHED_DBG
- if (tTd(69, 10))
- sm_syslog(LOG_INFO, NOQID, "rqe: done");
-#endif /* _FFR_QUEUE_SCHED_DBG */
-
- errno = save_errno;
- if (errno == EINTR)
- errno = ETIMEDOUT;
-}
-/*
-** GATHERQ -- gather messages from the message queue(s) the work queue.
-**
-** Parameters:
-** qgrp -- the index of the queue group.
-** qdir -- the index of the queue directory.
-** doall -- if set, include everything in the queue (even
-** the jobs that cannot be run because the load
-** average is too high, or MaxQueueRun is reached).
-** Otherwise, exclude those jobs.
-** full -- (optional) to be set 'true' if WorkList is full
-** more -- (optional) to be set 'true' if there are still more
-** messages in this queue not added to WorkList
-**
-** Returns:
-** The number of request in the queue (not necessarily
-** the number of requests in WorkList however).
-**
-** Side Effects:
-** prepares available work into WorkList
-*/
-
-#define NEED_P 0001 /* 'P': priority */
-#define NEED_T 0002 /* 'T': time */
-#define NEED_R 0004 /* 'R': recipient */
-#define NEED_S 0010 /* 'S': sender */
-#define NEED_H 0020 /* host */
-#define HAS_QUARANTINE 0040 /* has an unexpected 'q' line */
-#define NEED_QUARANTINE 0100 /* 'q': reason */
-
-static WORK *WorkList = NULL; /* list of unsort work */
-static int WorkListSize = 0; /* current max size of WorkList */
-static int WorkListCount = 0; /* # of work items in WorkList */
-
-static int
-gatherq(qgrp, qdir, doall, full, more)
- int qgrp;
- int qdir;
- bool doall;
- bool *full;
- bool *more;
-{
- register struct dirent *d;
- register WORK *w;
- register char *p;
- DIR *f;
- int i, num_ent;
- int wn;
- QUEUE_CHAR *check;
- char qd[MAXPATHLEN];
- char qf[MAXPATHLEN];
-
- wn = WorkListCount - 1;
- num_ent = 0;
- if (qdir == NOQDIR)
- (void) sm_strlcpy(qd, ".", sizeof(qd));
- else
- (void) sm_strlcpyn(qd, sizeof(qd), 2,
- Queue[qgrp]->qg_qpaths[qdir].qp_name,
- (bitset(QP_SUBQF,
- Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
- ? "/qf" : ""));
-
- if (tTd(41, 1))
- {
- sm_dprintf("gatherq:\n");
-
- check = QueueLimitId;
- while (check != NULL)
- {
- sm_dprintf("\tQueueLimitId = %s%s\n",
- check->queue_negate ? "!" : "",
- check->queue_match);
- check = check->queue_next;
- }
-
- check = QueueLimitSender;
- while (check != NULL)
- {
- sm_dprintf("\tQueueLimitSender = %s%s\n",
- check->queue_negate ? "!" : "",
- check->queue_match);
- check = check->queue_next;
- }
-
- check = QueueLimitRecipient;
- while (check != NULL)
- {
- sm_dprintf("\tQueueLimitRecipient = %s%s\n",
- check->queue_negate ? "!" : "",
- check->queue_match);
- check = check->queue_next;
- }
-
- if (QueueMode == QM_QUARANTINE)
- {
- check = QueueLimitQuarantine;
- while (check != NULL)
- {
- sm_dprintf("\tQueueLimitQuarantine = %s%s\n",
- check->queue_negate ? "!" : "",
- check->queue_match);
- check = check->queue_next;
- }
- }
- }
-
- /* open the queue directory */
- f = opendir(qd);
- if (f == NULL)
- {
- syserr("gatherq: cannot open \"%s\"",
- qid_printqueue(qgrp, qdir));
- if (full != NULL)
- *full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0;
- if (more != NULL)
- *more = false;
- return 0;
- }
-
- /*
- ** Read the work directory.
- */
-
- while ((d = readdir(f)) != NULL)
- {
- SM_FILE_T *cf;
- int qfver = 0;
- char lbuf[MAXNAME + 1];
- struct stat sbuf;
-
- if (tTd(41, 50))
- sm_dprintf("gatherq: checking %s..", d->d_name);
-
- /* is this an interesting entry? */
- if (!(((QueueMode == QM_NORMAL &&
- d->d_name[0] == NORMQF_LETTER) ||
- (QueueMode == QM_QUARANTINE &&
- d->d_name[0] == QUARQF_LETTER) ||
- (QueueMode == QM_LOST &&
- d->d_name[0] == LOSEQF_LETTER)) &&
- d->d_name[1] == 'f'))
- {
- if (tTd(41, 50))
- sm_dprintf(" skipping\n");
- continue;
- }
- if (tTd(41, 50))
- sm_dprintf("\n");
-
- if (strlen(d->d_name) >= MAXQFNAME)
- {
- if (Verbose)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "gatherq: %s too long, %d max characters\n",
- d->d_name, MAXQFNAME);
- if (LogLevel > 0)
- sm_syslog(LOG_ALERT, NOQID,
- "gatherq: %s too long, %d max characters",
- d->d_name, MAXQFNAME);
- continue;
- }
-
- check = QueueLimitId;
- while (check != NULL)
- {
- if (strcontainedin(false, check->queue_match,
- d->d_name) != check->queue_negate)
- break;
- else
- check = check->queue_next;
- }
- if (QueueLimitId != NULL && check == NULL)
- continue;
-
- /* grow work list if necessary */
- if (++wn >= MaxQueueRun && MaxQueueRun > 0)
- {
- if (wn == MaxQueueRun && LogLevel > 0)
- sm_syslog(LOG_WARNING, NOQID,
- "WorkList for %s maxed out at %d",
- qid_printqueue(qgrp, qdir),
- MaxQueueRun);
- if (doall)
- continue; /* just count entries */
- break;
- }
- if (wn >= WorkListSize)
- {
- grow_wlist(qgrp, qdir);
- if (wn >= WorkListSize)
- continue;
- }
- SM_ASSERT(wn >= 0);
- w = &WorkList[wn];
-
- (void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", d->d_name);
- if (stat(qf, &sbuf) < 0)
- {
- if (errno != ENOENT)
- sm_syslog(LOG_INFO, NOQID,
- "gatherq: can't stat %s/%s",
- qid_printqueue(qgrp, qdir),
- d->d_name);
- wn--;
- continue;
- }
- if (!bitset(S_IFREG, sbuf.st_mode))
- {
- /* Yikes! Skip it or we will hang on open! */
- if (!((d->d_name[0] == DATAFL_LETTER ||
- d->d_name[0] == NORMQF_LETTER ||
- d->d_name[0] == QUARQF_LETTER ||
- d->d_name[0] == LOSEQF_LETTER ||
- d->d_name[0] == XSCRPT_LETTER) &&
- d->d_name[1] == 'f' && d->d_name[2] == '\0'))
- syserr("gatherq: %s/%s is not a regular file",
- qid_printqueue(qgrp, qdir), d->d_name);
- wn--;
- continue;
- }
-
- /* avoid work if possible */
- if ((QueueSortOrder == QSO_BYFILENAME ||
- QueueSortOrder == QSO_BYMODTIME ||
- QueueSortOrder == QSO_NONE ||
- QueueSortOrder == QSO_RANDOM) &&
- QueueLimitQuarantine == NULL &&
- QueueLimitSender == NULL &&
- QueueLimitRecipient == NULL)
- {
- w->w_qgrp = qgrp;
- w->w_qdir = qdir;
- w->w_name = newstr(d->d_name);
- w->w_host = NULL;
- w->w_lock = w->w_tooyoung = false;
- w->w_pri = 0;
- w->w_ctime = 0;
- w->w_mtime = sbuf.st_mtime;
- ++num_ent;
- continue;
- }
-
- /* open control file */
- cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
- NULL);
- if (cf == NULL && OpMode != MD_PRINT)
- {
- /* this may be some random person sending hir msgs */
- if (tTd(41, 2))
- sm_dprintf("gatherq: cannot open %s: %s\n",
- d->d_name, sm_errstring(errno));
- errno = 0;
- wn--;
- continue;
- }
- w->w_qgrp = qgrp;
- w->w_qdir = qdir;
- w->w_name = newstr(d->d_name);
- w->w_host = NULL;
- if (cf != NULL)
- {
- w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD,
- NULL),
- w->w_name, NULL,
- LOCK_SH|LOCK_NB);
- }
- w->w_tooyoung = false;
-
- /* make sure jobs in creation don't clog queue */
- w->w_pri = 0x7fffffff;
- w->w_ctime = 0;
- w->w_mtime = sbuf.st_mtime;
-
- /* extract useful information */
- i = NEED_P|NEED_T;
- if (QueueSortOrder == QSO_BYHOST
-#if _FFR_RHS
- || QueueSortOrder == QSO_BYSHUFFLE
-#endif /* _FFR_RHS */
- )
- {
- /* need w_host set for host sort order */
- i |= NEED_H;
- }
- if (QueueLimitSender != NULL)
- i |= NEED_S;
- if (QueueLimitRecipient != NULL)
- i |= NEED_R;
- if (QueueLimitQuarantine != NULL)
- i |= NEED_QUARANTINE;
- while (cf != NULL && i != 0 &&
- sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf,
- sizeof(lbuf)) != NULL)
- {
- int c;
- time_t age;
-
- p = strchr(lbuf, '\n');
- if (p != NULL)
- *p = '\0';
- else
- {
- /* flush rest of overly long line */
- while ((c = sm_io_getc(cf, SM_TIME_DEFAULT))
- != SM_IO_EOF && c != '\n')
- continue;
- }
-
- switch (lbuf[0])
- {
- case 'V':
- qfver = atoi(&lbuf[1]);
- break;
-
- case 'P':
- w->w_pri = atol(&lbuf[1]);
- i &= ~NEED_P;
- break;
-
- case 'T':
- w->w_ctime = atol(&lbuf[1]);
- i &= ~NEED_T;
- break;
-
- case 'q':
- if (QueueMode != QM_QUARANTINE &&
- QueueMode != QM_LOST)
- {
- if (tTd(41, 49))
- sm_dprintf("%s not marked as quarantined but has a 'q' line\n",
- w->w_name);
- i |= HAS_QUARANTINE;
- }
- else if (QueueMode == QM_QUARANTINE)
- {
- if (QueueLimitQuarantine == NULL)
- {
- i &= ~NEED_QUARANTINE;
- break;
- }
- p = &lbuf[1];
- check = QueueLimitQuarantine;
- while (check != NULL)
- {
- if (strcontainedin(false,
- check->queue_match,
- p) !=
- check->queue_negate)
- break;
- else
- check = check->queue_next;
- }
- if (check != NULL)
- i &= ~NEED_QUARANTINE;
- }
- break;
-
- case 'R':
- if (w->w_host == NULL &&
- (p = strrchr(&lbuf[1], '@')) != NULL)
- {
-#if _FFR_RHS
- if (QueueSortOrder == QSO_BYSHUFFLE)
- w->w_host = newstr(&p[1]);
- else
-#endif /* _FFR_RHS */
- w->w_host = strrev(&p[1]);
- makelower(w->w_host);
- i &= ~NEED_H;
- }
- if (QueueLimitRecipient == NULL)
- {
- i &= ~NEED_R;
- break;
- }
- if (qfver > 0)
- {
- p = strchr(&lbuf[1], ':');
- if (p == NULL)
- p = &lbuf[1];
- else
- ++p; /* skip over ':' */
- }
- else
- p = &lbuf[1];
- check = QueueLimitRecipient;
- while (check != NULL)
- {
- if (strcontainedin(true,
- check->queue_match,
- p) !=
- check->queue_negate)
- break;
- else
- check = check->queue_next;
- }
- if (check != NULL)
- i &= ~NEED_R;
- break;
-
- case 'S':
- check = QueueLimitSender;
- while (check != NULL)
- {
- if (strcontainedin(true,
- check->queue_match,
- &lbuf[1]) !=
- check->queue_negate)
- break;
- else
- check = check->queue_next;
- }
- if (check != NULL)
- i &= ~NEED_S;
- break;
-
- case 'K':
- age = curtime() - (time_t) atol(&lbuf[1]);
- if (age >= 0 && MinQueueAge > 0 &&
- age < MinQueueAge)
- w->w_tooyoung = true;
- break;
-
- case 'N':
- if (atol(&lbuf[1]) == 0)
- w->w_tooyoung = false;
- break;
- }
- }
- if (cf != NULL)
- (void) sm_io_close(cf, SM_TIME_DEFAULT);
-
- if ((!doall && (shouldqueue(w->w_pri, w->w_ctime) ||
- w->w_tooyoung)) ||
- bitset(HAS_QUARANTINE, i) ||
- bitset(NEED_QUARANTINE, i) ||
- bitset(NEED_R|NEED_S, i))
- {
- /* don't even bother sorting this job in */
- if (tTd(41, 49))
- sm_dprintf("skipping %s (%x)\n", w->w_name, i);
- sm_free(w->w_name); /* XXX */
- if (w->w_host != NULL)
- sm_free(w->w_host); /* XXX */
- wn--;
- }
- else
- ++num_ent;
- }
- (void) closedir(f);
- wn++;
-
- i = wn - WorkListCount;
- WorkListCount += SM_MIN(num_ent, WorkListSize);
-
- if (more != NULL)
- *more = WorkListCount < wn;
-
- if (full != NULL)
- *full = (wn >= MaxQueueRun && MaxQueueRun > 0) ||
- (WorkList == NULL && wn > 0);
-
- return i;
-}
-/*
-** SORTQ -- sort the work list
-**
-** First the old WorkQ is cleared away. Then the WorkList is sorted
-** for all items so that important (higher sorting value) items are not
-** trunctated off. Then the most important items are moved from
-** WorkList to WorkQ. The lower count of 'max' or MaxListCount items
-** are moved.
-**
-** Parameters:
-** max -- maximum number of items to be placed in WorkQ
-**
-** Returns:
-** the number of items in WorkQ
-**
-** Side Effects:
-** WorkQ gets released and filled with new work. WorkList
-** gets released. Work items get sorted in order.
-*/
-
-static int
-sortq(max)
- int max;
-{
- register int i; /* local counter */
- register WORK *w; /* tmp item pointer */
- int wc = WorkListCount; /* trim size for WorkQ */
-
- if (WorkQ != NULL)
- {
- WORK *nw;
-
- /* Clear out old WorkQ. */
- for (w = WorkQ; w != NULL; w = nw)
- {
- nw = w->w_next;
- sm_free(w->w_name); /* XXX */
- if (w->w_host != NULL)
- sm_free(w->w_host); /* XXX */
- sm_free((char *) w); /* XXX */
- }
- WorkQ = NULL;
- }
-
- if (WorkList == NULL || wc <= 0)
- return 0;
-
- /*
- ** The sort now takes place using all of the items in WorkList.
- ** The list gets trimmed to the most important items after the sort.
- ** If the trim were to happen before the sort then one or more
- ** important items might get truncated off -- not what we want.
- */
-
- if (QueueSortOrder == QSO_BYHOST)
- {
- /*
- ** Sort the work directory for the first time,
- ** based on host name, lock status, and priority.
- */
-
- qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf1);
-
- /*
- ** If one message to host is locked, "lock" all messages
- ** to that host.
- */
-
- i = 0;
- while (i < wc)
- {
- if (!WorkList[i].w_lock)
- {
- i++;
- continue;
- }
- w = &WorkList[i];
- while (++i < wc)
- {
- if (WorkList[i].w_host == NULL &&
- w->w_host == NULL)
- WorkList[i].w_lock = true;
- else if (WorkList[i].w_host != NULL &&
- w->w_host != NULL &&
- sm_strcasecmp(WorkList[i].w_host,
- w->w_host) == 0)
- WorkList[i].w_lock = true;
- else
- break;
- }
- }
-
- /*
- ** Sort the work directory for the second time,
- ** based on lock status, host name, and priority.
- */
-
- qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf2);
- }
- else if (QueueSortOrder == QSO_BYTIME)
- {
- /*
- ** Simple sort based on submission time only.
- */
-
- qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf3);
- }
- else if (QueueSortOrder == QSO_BYFILENAME)
- {
- /*
- ** Sort based on queue filename.
- */
-
- qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf4);
- }
- else if (QueueSortOrder == QSO_RANDOM)
- {
- /*
- ** Sort randomly. To avoid problems with an instable sort,
- ** use a random index into the queue file name to start
- ** comparison.
- */
-
- randi = get_rand_mod(MAXQFNAME);
- if (randi < 2)
- randi = 3;
- qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf5);
- }
- else if (QueueSortOrder == QSO_BYMODTIME)
- {
- /*
- ** Simple sort based on modification time of queue file.
- ** This puts the oldest items first.
- */
-
- qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf6);
- }
-#if _FFR_RHS
- else if (QueueSortOrder == QSO_BYSHUFFLE)
- {
- /*
- ** Simple sort based on shuffled host name.
- */
-
- init_shuffle_alphabet();
- qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf7);
- }
-#endif /* _FFR_RHS */
- else if (QueueSortOrder == QSO_BYPRIORITY)
- {
- /*
- ** Simple sort based on queue priority only.
- */
-
- qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf0);
- }
- /* else don't sort at all */
-
- /* Check if the per queue group item limit will be exceeded */
- if (wc > max && max > 0)
- wc = max;
-
- /*
- ** Convert the work list into canonical form.
- ** Should be turning it into a list of envelopes here perhaps.
- ** Only take the most important items up to the per queue group
- ** maximum.
- */
-
- for (i = wc; --i >= 0; )
- {
- w = (WORK *) xalloc(sizeof(*w));
- w->w_qgrp = WorkList[i].w_qgrp;
- w->w_qdir = WorkList[i].w_qdir;
- w->w_name = WorkList[i].w_name;
- w->w_host = WorkList[i].w_host;
- w->w_lock = WorkList[i].w_lock;
- w->w_tooyoung = WorkList[i].w_tooyoung;
- w->w_pri = WorkList[i].w_pri;
- w->w_ctime = WorkList[i].w_ctime;
- w->w_mtime = WorkList[i].w_mtime;
- w->w_next = WorkQ;
- WorkQ = w;
- }
-
- /* free the rest of the list */
- for (i = WorkListCount; --i >= wc; )
- {
- sm_free(WorkList[i].w_name);
- if (WorkList[i].w_host != NULL)
- sm_free(WorkList[i].w_host);
- }
-
- if (WorkList != NULL)
- sm_free(WorkList); /* XXX */
- WorkList = NULL;
- WorkListSize = 0;
- WorkListCount = 0;
-
- if (tTd(40, 1))
- {
- for (w = WorkQ; w != NULL; w = w->w_next)
- {
- if (w->w_host != NULL)
- sm_dprintf("%22s: pri=%ld %s\n",
- w->w_name, w->w_pri, w->w_host);
- else
- sm_dprintf("%32s: pri=%ld\n",
- w->w_name, w->w_pri);
- }
- }
-
- return wc; /* return number of WorkQ items */
-}
-/*
-** GROW_WLIST -- make the work list larger
-**
-** Parameters:
-** qgrp -- the index for the queue group.
-** qdir -- the index for the queue directory.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Adds another QUEUESEGSIZE entries to WorkList if possible.
-** It can fail if there isn't enough memory, so WorkListSize
-** should be checked again upon return.
-*/
-
-static void
-grow_wlist(qgrp, qdir)
- int qgrp;
- int qdir;
-{
- if (tTd(41, 1))
- sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
- if (WorkList == NULL)
- {
- WorkList = (WORK *) xalloc((sizeof(*WorkList)) *
- (QUEUESEGSIZE + 1));
- WorkListSize = QUEUESEGSIZE;
- }
- else
- {
- int newsize = WorkListSize + QUEUESEGSIZE;
- WORK *newlist = (WORK *) sm_realloc((char *) WorkList,
- (unsigned) sizeof(WORK) * (newsize + 1));
-
- if (newlist != NULL)
- {
- WorkListSize = newsize;
- WorkList = newlist;
- if (LogLevel > 1)
- {
- sm_syslog(LOG_INFO, NOQID,
- "grew WorkList for %s to %d",
- qid_printqueue(qgrp, qdir),
- WorkListSize);
- }
- }
- else if (LogLevel > 0)
- {
- sm_syslog(LOG_ALERT, NOQID,
- "FAILED to grow WorkList for %s to %d",
- qid_printqueue(qgrp, qdir), newsize);
- }
- }
- if (tTd(41, 1))
- sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
-}
-/*
-** WORKCMPF0 -- simple priority-only compare function.
-**
-** Parameters:
-** a -- the first argument.
-** b -- the second argument.
-**
-** Returns:
-** -1 if a < b
-** 0 if a == b
-** +1 if a > b
-**
-*/
-
-static int
-workcmpf0(a, b)
- register WORK *a;
- register WORK *b;
-{
- long pa = a->w_pri;
- long pb = b->w_pri;
-
- if (pa == pb)
- return 0;
- else if (pa > pb)
- return 1;
- else
- return -1;
-}
-/*
-** WORKCMPF1 -- first compare function for ordering work based on host name.
-**
-** Sorts on host name, lock status, and priority in that order.
-**
-** Parameters:
-** a -- the first argument.
-** b -- the second argument.
-**
-** Returns:
-** <0 if a < b
-** 0 if a == b
-** >0 if a > b
-**
-*/
-
-static int
-workcmpf1(a, b)
- register WORK *a;
- register WORK *b;
-{
- int i;
-
- /* host name */
- if (a->w_host != NULL && b->w_host == NULL)
- return 1;
- else if (a->w_host == NULL && b->w_host != NULL)
- return -1;
- if (a->w_host != NULL && b->w_host != NULL &&
- (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
- return i;
-
- /* lock status */
- if (a->w_lock != b->w_lock)
- return b->w_lock - a->w_lock;
-
- /* job priority */
- return workcmpf0(a, b);
-}
-/*
-** WORKCMPF2 -- second compare function for ordering work based on host name.
-**
-** Sorts on lock status, host name, and priority in that order.
-**
-** Parameters:
-** a -- the first argument.
-** b -- the second argument.
-**
-** Returns:
-** <0 if a < b
-** 0 if a == b
-** >0 if a > b
-**
-*/
-
-static int
-workcmpf2(a, b)
- register WORK *a;
- register WORK *b;
-{
- int i;
-
- /* lock status */
- if (a->w_lock != b->w_lock)
- return a->w_lock - b->w_lock;
-
- /* host name */
- if (a->w_host != NULL && b->w_host == NULL)
- return 1;
- else if (a->w_host == NULL && b->w_host != NULL)
- return -1;
- if (a->w_host != NULL && b->w_host != NULL &&
- (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
- return i;
-
- /* job priority */
- return workcmpf0(a, b);
-}
-/*
-** WORKCMPF3 -- simple submission-time-only compare function.
-**
-** Parameters:
-** a -- the first argument.
-** b -- the second argument.
-**
-** Returns:
-** -1 if a < b
-** 0 if a == b
-** +1 if a > b
-**
-*/
-
-static int
-workcmpf3(a, b)
- register WORK *a;
- register WORK *b;
-{
- if (a->w_ctime > b->w_ctime)
- return 1;
- else if (a->w_ctime < b->w_ctime)
- return -1;
- else
- 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
-**
-*/
-
-static int
-workcmpf4(a, b)
- register WORK *a;
- register WORK *b;
-{
- return strcmp(a->w_name, b->w_name);
-}
-/*
-** WORKCMPF5 -- compare based on assigned random number
-**
-** Parameters:
-** a -- the first argument (ignored).
-** b -- the second argument (ignored).
-**
-** Returns:
-** randomly 1/-1
-*/
-
-/* ARGSUSED0 */
-static int
-workcmpf5(a, b)
- register WORK *a;
- register WORK *b;
-{
- if (strlen(a->w_name) < randi || strlen(b->w_name) < randi)
- return -1;
- return a->w_name[randi] - b->w_name[randi];
-}
-/*
-** WORKCMPF6 -- simple modification-time-only compare function.
-**
-** Parameters:
-** a -- the first argument.
-** b -- the second argument.
-**
-** Returns:
-** -1 if a < b
-** 0 if a == b
-** +1 if a > b
-**
-*/
-
-static int
-workcmpf6(a, b)
- register WORK *a;
- register WORK *b;
-{
- if (a->w_mtime > b->w_mtime)
- return 1;
- else if (a->w_mtime < b->w_mtime)
- return -1;
- else
- return 0;
-}
-#if _FFR_RHS
-/*
-** WORKCMPF7 -- compare function for ordering work based on shuffled host name.
-**
-** Sorts on lock status, host name, and priority in that order.
-**
-** Parameters:
-** a -- the first argument.
-** b -- the second argument.
-**
-** Returns:
-** <0 if a < b
-** 0 if a == b
-** >0 if a > b
-**
-*/
-
-static int
-workcmpf7(a, b)
- register WORK *a;
- register WORK *b;
-{
- int i;
-
- /* lock status */
- if (a->w_lock != b->w_lock)
- return a->w_lock - b->w_lock;
-
- /* host name */
- if (a->w_host != NULL && b->w_host == NULL)
- return 1;
- else if (a->w_host == NULL && b->w_host != NULL)
- return -1;
- if (a->w_host != NULL && b->w_host != NULL &&
- (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0)
- return i;
-
- /* job priority */
- return workcmpf0(a, b);
-}
-#endif /* _FFR_RHS */
-/*
-** STRREV -- reverse string
-**
-** Returns a pointer to a new string that is the reverse of
-** 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;
-}
-
-#if _FFR_RHS
-
-# define NASCII 128
-# define NCHAR 256
-
-static unsigned char ShuffledAlphabet[NCHAR];
-
-void
-init_shuffle_alphabet()
-{
- static bool init = false;
- int i;
-
- if (init)
- return;
-
- /* fill the ShuffledAlphabet */
- for (i = 0; i < NASCII; i++)
- ShuffledAlphabet[i] = i;
-
- /* mix it */
- for (i = 1; i < NASCII; i++)
- {
- register int j = get_random() % NASCII;
- register int tmp;
-
- tmp = ShuffledAlphabet[j];
- ShuffledAlphabet[j] = ShuffledAlphabet[i];
- ShuffledAlphabet[i] = tmp;
- }
-
- /* make it case insensitive */
- for (i = 'A'; i <= 'Z'; i++)
- ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A'];
-
- /* fill the upper part */
- for (i = 0; i < NASCII; i++)
- ShuffledAlphabet[i + NASCII] = ShuffledAlphabet[i];
- init = true;
-}
-
-static int
-sm_strshufflecmp(a, b)
- char *a;
- char *b;
-{
- const unsigned char *us1 = (const unsigned char *) a;
- const unsigned char *us2 = (const unsigned char *) b;
-
- while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++])
- {
- if (*us1++ == '\0')
- return 0;
- }
- return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]);
-}
-#endif /* _FFR_RHS */
-
-/*
-** DOWORK -- do a work request.
-**
-** Parameters:
-** qgrp -- the index of the queue group for the job.
-** qdir -- the index of the queue directory for the job.
-** id -- the ID of the job to run.
-** forkflag -- if set, run this in background.
-** requeueflag -- if set, reinstantiate the queue quickly.
-** This is used when expanding aliases in the queue.
-** If forkflag is also set, it doesn't wait for the
-** child.
-** e - the envelope in which to run it.
-**
-** Returns:
-** process id of process that is running the queue job.
-**
-** Side Effects:
-** The work request is satisfied if possible.
-*/
-
-pid_t
-dowork(qgrp, qdir, id, forkflag, requeueflag, e)
- int qgrp;
- int qdir;
- char *id;
- bool forkflag;
- bool requeueflag;
- register ENVELOPE *e;
-{
- register pid_t pid;
- SM_RPOOL_T *rpool;
-
- if (tTd(40, 1))
- sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id);
-
- /*
- ** Fork for work.
- */
-
- if (forkflag)
- {
- /*
- ** Since the delivery may happen in a child and the
- ** parent does not wait, the parent may close the
- ** maps thereby removing any shared memory used by
- ** the map. Therefore, close the maps now so the
- ** child will dynamically open them if necessary.
- */
-
- closemaps(false);
-
- pid = fork();
- if (pid < 0)
- {
- syserr("dowork: cannot fork");
- return 0;
- }
- else if (pid > 0)
- {
- /* parent -- clean out connection cache */
- mci_flush(false, NULL);
- }
- else
- {
- /*
- ** Initialize exception stack and default exception
- ** handler for child process.
- */
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- CurrentPid = getpid();
- sm_exc_newthread(fatal_error);
-
- /*
- ** See note above about SMTP processes and SIGCHLD.
- */
-
- if (OpMode == MD_SMTP ||
- OpMode == MD_DAEMON ||
- MaxQueueChildren > 0)
- {
- proc_list_clear();
- sm_releasesignal(SIGCHLD);
- (void) sm_signal(SIGCHLD, SIG_DFL);
- }
-
- /* child -- error messages to the transcript */
- QuickAbort = OnlyOneError = false;
- }
- }
- else
- {
- pid = 0;
- }
-
- if (pid == 0)
- {
- /*
- ** CHILD
- ** Lock the control file to avoid duplicate deliveries.
- ** Then run the file as though we had just read it.
- ** We save an idea of the temporary name so we
- ** can recover on interrupt.
- */
-
- if (forkflag)
- {
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- }
-
- /* set basic modes, etc. */
- sm_clear_events();
- clearstats();
- rpool = sm_rpool_new_x(NULL);
- clearenvelope(e, false, rpool);
- e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
- set_delivery_mode(SM_DELIVER, e);
- e->e_errormode = EM_MAIL;
- e->e_id = id;
- e->e_qgrp = qgrp;
- e->e_qdir = qdir;
- GrabTo = UseErrorsTo = false;
- ExitStat = EX_OK;
- if (forkflag)
- {
- disconnect(1, e);
- set_op_mode(MD_QUEUERUN);
- }
- sm_setproctitle(true, e, "%s from queue", qid_printname(e));
- if (LogLevel > 76)
- sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d",
- (int) CurrentPid);
-
- /* don't use the headers from sendmail.cf... */
- e->e_header = NULL;
-
- /* read the queue control file -- return if locked */
- if (!readqf(e, false))
- {
- if (tTd(40, 4) && e->e_id != NULL)
- sm_dprintf("readqf(%s) failed\n",
- qid_printname(e));
- e->e_id = NULL;
- if (forkflag)
- finis(false, true, EX_OK);
- else
- {
- /* adding this frees 8 bytes */
- clearenvelope(e, false, rpool);
-
- /* adding this frees 12 bytes */
- sm_rpool_free(rpool);
- e->e_rpool = NULL;
- return 0;
- }
- }
-
- e->e_flags |= EF_INQUEUE;
- eatheader(e, requeueflag, true);
-
- if (requeueflag)
- queueup(e, false, false);
-
- /* do the delivery */
- sendall(e, SM_DELIVER);
-
- /* finish up and exit */
- if (forkflag)
- finis(true, true, ExitStat);
- else
- {
- dropenvelope(e, true, false);
- sm_rpool_free(rpool);
- e->e_rpool = NULL;
- }
- }
- e->e_id = NULL;
- return pid;
-}
-
-/*
-** DOWORKLIST -- process a list of envelopes as work requests
-**
-** Similar to dowork(), except that after forking, it processes an
-** envelope and its siblings, treating each envelope as a work request.
-**
-** Parameters:
-** el -- envelope to be processed including its siblings.
-** forkflag -- if set, run this in background.
-** requeueflag -- if set, reinstantiate the queue quickly.
-** This is used when expanding aliases in the queue.
-** If forkflag is also set, it doesn't wait for the
-** child.
-**
-** Returns:
-** process id of process that is running the queue job.
-**
-** Side Effects:
-** The work request is satisfied if possible.
-*/
-
-pid_t
-doworklist(el, forkflag, requeueflag)
- ENVELOPE *el;
- bool forkflag;
- bool requeueflag;
-{
- register pid_t pid;
- ENVELOPE *ei;
-
- if (tTd(40, 1))
- sm_dprintf("doworklist()\n");
-
- /*
- ** Fork for work.
- */
-
- if (forkflag)
- {
- /*
- ** Since the delivery may happen in a child and the
- ** parent does not wait, the parent may close the
- ** maps thereby removing any shared memory used by
- ** the map. Therefore, close the maps now so the
- ** child will dynamically open them if necessary.
- */
-
- closemaps(false);
-
- pid = fork();
- if (pid < 0)
- {
- syserr("doworklist: cannot fork");
- return 0;
- }
- else if (pid > 0)
- {
- /* parent -- clean out connection cache */
- mci_flush(false, NULL);
- }
- else
- {
- /*
- ** Initialize exception stack and default exception
- ** handler for child process.
- */
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- CurrentPid = getpid();
- sm_exc_newthread(fatal_error);
-
- /*
- ** See note above about SMTP processes and SIGCHLD.
- */
-
- if (OpMode == MD_SMTP ||
- OpMode == MD_DAEMON ||
- MaxQueueChildren > 0)
- {
- proc_list_clear();
- sm_releasesignal(SIGCHLD);
- (void) sm_signal(SIGCHLD, SIG_DFL);
- }
-
- /* child -- error messages to the transcript */
- QuickAbort = OnlyOneError = false;
- }
- }
- else
- {
- pid = 0;
- }
-
- if (pid != 0)
- return pid;
-
- /*
- ** IN CHILD
- ** Lock the control file to avoid duplicate deliveries.
- ** Then run the file as though we had just read it.
- ** We save an idea of the temporary name so we
- ** can recover on interrupt.
- */
-
- if (forkflag)
- {
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- }
-
- /* set basic modes, etc. */
- sm_clear_events();
- clearstats();
- GrabTo = UseErrorsTo = false;
- ExitStat = EX_OK;
- if (forkflag)
- {
- disconnect(1, el);
- set_op_mode(MD_QUEUERUN);
- }
- if (LogLevel > 76)
- sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d",
- (int) CurrentPid);
-
- for (ei = el; ei != NULL; ei = ei->e_sibling)
- {
- ENVELOPE e;
- SM_RPOOL_T *rpool;
-
- if (WILL_BE_QUEUED(ei->e_sendmode))
- continue;
- else if (QueueMode != QM_QUARANTINE &&
- ei->e_quarmsg != NULL)
- continue;
-
- rpool = sm_rpool_new_x(NULL);
- clearenvelope(&e, true, rpool);
- e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
- set_delivery_mode(SM_DELIVER, &e);
- e.e_errormode = EM_MAIL;
- e.e_id = ei->e_id;
- e.e_qgrp = ei->e_qgrp;
- e.e_qdir = ei->e_qdir;
- openxscript(&e);
- sm_setproctitle(true, &e, "%s from queue", qid_printname(&e));
-
- /* don't use the headers from sendmail.cf... */
- e.e_header = NULL;
- CurEnv = &e;
-
- /* read the queue control file -- return if locked */
- if (readqf(&e, false))
- {
- e.e_flags |= EF_INQUEUE;
- eatheader(&e, requeueflag, true);
-
- if (requeueflag)
- queueup(&e, false, false);
-
- /* do the delivery */
- sendall(&e, SM_DELIVER);
- dropenvelope(&e, true, false);
- }
- else
- {
- if (tTd(40, 4) && e.e_id != NULL)
- sm_dprintf("readqf(%s) failed\n",
- qid_printname(&e));
- }
- sm_rpool_free(rpool);
- ei->e_id = NULL;
- }
-
- /* restore CurEnv */
- CurEnv = el;
-
- /* finish up and exit */
- if (forkflag)
- finis(true, true, ExitStat);
- return 0;
-}
-/*
-** READQF -- read queue file and set up environment.
-**
-** Parameters:
-** e -- the envelope of the job to run.
-** openonly -- only open the qf (returned as e_lockfp)
-**
-** Returns:
-** true if it successfully read the queue file.
-** false otherwise.
-**
-** Side Effects:
-** The queue file is returned locked.
-*/
-
-static bool
-readqf(e, openonly)
- register ENVELOPE *e;
- bool openonly;
-{
- register SM_FILE_T *qfp;
- ADDRESS *ctladdr;
- struct stat st, stf;
- char *bp;
- int qfver = 0;
- long hdrsize = 0;
- register char *p;
- char *frcpt = NULL;
- char *orcpt = NULL;
- bool nomore = false;
- bool bogus = false;
- MODE_T qsafe;
- char *err;
- char qf[MAXPATHLEN];
- char buf[MAXLINE];
- int bufsize;
-
- /*
- ** Read and process the file.
- */
-
- SM_REQUIRE(e != NULL);
- bp = NULL;
- (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof(qf));
- qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR_B, NULL);
- if (qfp == NULL)
- {
- int save_errno = errno;
-
- if (tTd(40, 8))
- sm_dprintf("readqf(%s): sm_io_open failure (%s)\n",
- qf, sm_errstring(errno));
- errno = save_errno;
- if (errno != ENOENT
- )
- syserr("readqf: no control file %s", qf);
- RELEASE_QUEUE;
- return false;
- }
-
- if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL,
- LOCK_EX|LOCK_NB))
- {
- /* being processed by another queuer */
- if (Verbose)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "%s: locked\n", e->e_id);
- if (tTd(40, 8))
- sm_dprintf("%s: locked\n", e->e_id);
- if (LogLevel > 19)
- sm_syslog(LOG_DEBUG, e->e_id, "locked");
- (void) sm_io_close(qfp, SM_TIME_DEFAULT);
- RELEASE_QUEUE;
- return false;
- }
-
- RELEASE_QUEUE;
-
- /*
- ** Prevent locking race condition.
- **
- ** Process A: readqf(): qfp = fopen(qffile)
- ** Process B: queueup(): rename(tf, qf)
- ** Process B: unlocks(tf)
- ** Process A: lockfile(qf);
- **
- ** Process A (us) has the old qf file (before the rename deleted
- ** the directory entry) and will be delivering based on old data.
- ** This can lead to multiple deliveries of the same recipients.
- **
- ** Catch this by checking if the underlying qf file has changed
- ** *after* acquiring our lock and if so, act as though the file
- ** was still locked (i.e., just return like the lockfile() case
- ** above.
- */
-
- if (stat(qf, &stf) < 0 ||
- fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0)
- {
- /* must have been being processed by someone else */
- if (tTd(40, 8))
- sm_dprintf("readqf(%s): [f]stat failure (%s)\n",
- qf, sm_errstring(errno));
- (void) sm_io_close(qfp, SM_TIME_DEFAULT);
- return false;
- }
-
- if (st.st_nlink != stf.st_nlink ||
- st.st_dev != stf.st_dev ||
- ST_INODE(st) != ST_INODE(stf) ||
-#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
- st.st_gen != stf.st_gen ||
-#endif /* HAS_ST_GEN && 0 */
- st.st_uid != stf.st_uid ||
- st.st_gid != stf.st_gid ||
- st.st_size != stf.st_size)
- {
- /* changed after opened */
- if (Verbose)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "%s: changed\n", e->e_id);
- if (tTd(40, 8))
- sm_dprintf("%s: changed\n", e->e_id);
- if (LogLevel > 19)
- sm_syslog(LOG_DEBUG, e->e_id, "changed");
- (void) sm_io_close(qfp, SM_TIME_DEFAULT);
- return false;
- }
-
- /*
- ** Check the queue file for plausibility to avoid attacks.
- */
-
- qsafe = S_IWOTH|S_IWGRP;
- if (bitset(S_IWGRP, QueueFileMode))
- qsafe &= ~S_IWGRP;
-
- bogus = st.st_uid != geteuid() &&
- st.st_uid != TrustedUid &&
- geteuid() != RealUid;
-
- /*
- ** If this qf file results from a set-group-ID binary, then
- ** we check whether the directory is group-writable,
- ** the queue file mode contains the group-writable bit, and
- ** the groups are the same.
- ** Notice: this requires that the set-group-ID binary is used to
- ** run the queue!
- */
-
- if (bogus && st.st_gid == getegid() && UseMSP)
- {
- char delim;
- struct stat dst;
-
- bp = SM_LAST_DIR_DELIM(qf);
- if (bp == NULL)
- delim = '\0';
- else
- {
- delim = *bp;
- *bp = '\0';
- }
- if (stat(delim == '\0' ? "." : qf, &dst) < 0)
- syserr("readqf: cannot stat directory %s",
- delim == '\0' ? "." : qf);
- else
- {
- bogus = !(bitset(S_IWGRP, QueueFileMode) &&
- bitset(S_IWGRP, dst.st_mode) &&
- dst.st_gid == st.st_gid);
- }
- if (delim != '\0')
- *bp = delim;
- bp = NULL;
- }
- if (!bogus)
- bogus = bitset(qsafe, st.st_mode);
- if (bogus)
- {
- if (LogLevel > 0)
- {
- sm_syslog(LOG_ALERT, e->e_id,
- "bogus queue file, uid=%d, gid=%d, mode=%o",
- st.st_uid, st.st_gid, st.st_mode);
- }
- if (tTd(40, 8))
- sm_dprintf("readqf(%s): bogus file\n", qf);
- e->e_flags |= EF_INQUEUE;
- if (!openonly)
- loseqfile(e, "bogus file uid/gid in mqueue");
- (void) sm_io_close(qfp, SM_TIME_DEFAULT);
- return false;
- }
-
- if (st.st_size == 0)
- {
- /* must be a bogus file -- if also old, just remove it */
- if (!openonly && st.st_ctime + 10 * 60 < curtime())
- {
- (void) xunlink(queuename(e, DATAFL_LETTER));
- (void) xunlink(queuename(e, ANYQFL_LETTER));
- }
- (void) sm_io_close(qfp, SM_TIME_DEFAULT);
- return false;
- }
-
- if (st.st_nlink == 0)
- {
- /*
- ** Race condition -- we got a file just as it was being
- ** unlinked. Just assume it is zero length.
- */
-
- (void) sm_io_close(qfp, SM_TIME_DEFAULT);
- return false;
- }
-
-#if _FFR_TRUSTED_QF
- /*
- ** If we don't own the file mark it as unsafe.
- ** However, allow TrustedUser to own it as well
- ** in case TrustedUser manipulates the queue.
- */
-
- if (st.st_uid != geteuid() && st.st_uid != TrustedUid)
- e->e_flags |= EF_UNSAFE;
-#else /* _FFR_TRUSTED_QF */
- /* If we don't own the file mark it as unsafe */
- if (st.st_uid != geteuid())
- e->e_flags |= EF_UNSAFE;
-#endif /* _FFR_TRUSTED_QF */
-
- /* good file -- save this lock */
- e->e_lockfp = qfp;
-
- /* Just wanted the open file */
- if (openonly)
- return true;
-
- /* do basic system initialization */
- initsys(e);
- macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
-
- LineNumber = 0;
- e->e_flags |= EF_GLOBALERRS;
- set_op_mode(MD_QUEUERUN);
- ctladdr = NULL;
- e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
- e->e_dfqgrp = e->e_qgrp;
- e->e_dfqdir = e->e_qdir;
-#if _FFR_QUEUE_MACRO
- macdefine(&e->e_macro, A_TEMP, macid("{queue}"),
- qid_printqueue(e->e_qgrp, e->e_qdir));
-#endif /* _FFR_QUEUE_MACRO */
- e->e_dfino = -1;
- e->e_msgsize = -1;
- while (bufsize = sizeof(buf),
- (bp = fgetfolded(buf, &bufsize, qfp)) != NULL)
- {
- unsigned long qflags;
- ADDRESS *q;
- int r;
- time_t now;
- auto char *ep;
-
- if (tTd(40, 4))
- sm_dprintf("+++++ %s\n", bp);
- if (nomore)
- {
- /* hack attack */
- hackattack:
- syserr("SECURITY ALERT: extra or bogus data in queue file: %s",
- bp);
- err = "bogus queue line";
- goto fail;
- }
- switch (bp[0])
- {
- case 'A': /* AUTH= parameter */
- if (!xtextok(&bp[1]))
- goto hackattack;
- e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
- break;
-
- case 'B': /* body type */
- r = check_bodytype(&bp[1]);
- if (!BODYTYPE_VALID(r))
- goto hackattack;
- e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
- break;
-
- case 'C': /* specify controlling user */
- ctladdr = setctluser(&bp[1], qfver, e);
- break;
-
- case 'D': /* data file name */
- /* obsolete -- ignore */
- break;
-
- case 'd': /* data file directory name */
- {
- int qgrp, qdir;
-
-#if _FFR_MSP_PARANOIA
- /* forbid queue groups in MSP? */
- if (UseMSP)
- goto hackattack;
-#endif /* _FFR_MSP_PARANOIA */
- for (qgrp = 0;
- qgrp < NumQueue && Queue[qgrp] != NULL;
- ++qgrp)
- {
- for (qdir = 0;
- qdir < Queue[qgrp]->qg_numqueues;
- ++qdir)
- {
- if (strcmp(&bp[1],
- Queue[qgrp]->qg_qpaths[qdir].qp_name)
- == 0)
- {
- e->e_dfqgrp = qgrp;
- e->e_dfqdir = qdir;
- goto done;
- }
- }
- }
- err = "bogus queue file directory";
- goto fail;
- done:
- break;
- }
-
- case 'E': /* specify error recipient */
- /* no longer used */
- break;
-
- case 'F': /* flag bits */
- if (strncmp(bp, "From ", 5) == 0)
- {
- /* we are being spoofed! */
- syserr("SECURITY ALERT: bogus qf line %s", bp);
- err = "bogus queue line";
- goto fail;
- }
- for (p = &bp[1]; *p != '\0'; p++)
- {
- switch (*p)
- {
- case '8': /* has 8 bit data */
- e->e_flags |= EF_HAS8BIT;
- break;
-
- case 'b': /* delete Bcc: header */
- e->e_flags |= EF_DELETE_BCC;
- break;
-
- case 'd': /* envelope has DSN RET= */
- e->e_flags |= EF_RET_PARAM;
- break;
-
- case 'n': /* don't return body */
- e->e_flags |= EF_NO_BODY_RETN;
- break;
-
- case 'r': /* response */
- e->e_flags |= EF_RESPONSE;
- break;
-
- case 's': /* split */
- e->e_flags |= EF_SPLIT;
- break;
-
- case 'w': /* warning sent */
- e->e_flags |= EF_WARNING;
- break;
- }
- }
- break;
-
- case 'q': /* quarantine reason */
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), e->e_quarmsg);
- break;
-
- case 'H': /* header */
-
- /*
- ** count size before chompheader() destroys the line.
- ** this isn't accurate due to macro expansion, but
- ** better than before. "-3" to skip H?? at least.
- */
-
- hdrsize += strlen(bp) - 3;
- (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
- break;
-
- case 'I': /* data file's inode number */
- /* regenerated below */
- break;
-
- case 'K': /* time of last delivery attempt */
- e->e_dtime = atol(&buf[1]);
- break;
-
- case 'L': /* Solaris Content-Length: */
- case 'M': /* message */
- /* ignore this; we want a new message next time */
- break;
-
- case 'N': /* number of delivery attempts */
- e->e_ntries = atoi(&buf[1]);
-
- /* if this has been tried recently, let it be */
- now = curtime();
- if (e->e_ntries > 0 && e->e_dtime <= now &&
- now < e->e_dtime + MinQueueAge)
- {
- char *howlong;
-
- howlong = pintvl(now - e->e_dtime, true);
- if (Verbose)
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "%s: too young (%s)\n",
- e->e_id, howlong);
- if (tTd(40, 8))
- sm_dprintf("%s: too young (%s)\n",
- e->e_id, howlong);
- if (LogLevel > 19)
- sm_syslog(LOG_DEBUG, e->e_id,
- "too young (%s)",
- howlong);
- e->e_id = NULL;
- unlockqueue(e);
- if (bp != buf)
- sm_free(bp);
- return false;
- }
- macdefine(&e->e_macro, A_TEMP,
- macid("{ntries}"), &buf[1]);
-
-#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 */
- e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
- break;
-
- case 'Q': /* original recipient */
- orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
- break;
-
- case 'r': /* final recipient */
- frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
- break;
-
- case 'R': /* specify recipient */
- p = bp;
- qflags = 0;
- if (qfver >= 1)
- {
- /* get flag bits */
- while (*++p != '\0' && *p != ':')
- {
- switch (*p)
- {
- case 'N':
- qflags |= QHASNOTIFY;
- break;
-
- case 'S':
- qflags |= QPINGONSUCCESS;
- break;
-
- case 'F':
- qflags |= QPINGONFAILURE;
- break;
-
- case 'D':
- qflags |= QPINGONDELAY;
- break;
-
- case 'P':
- qflags |= QPRIMARY;
- break;
-
- case 'A':
- if (ctladdr != NULL)
- ctladdr->q_flags |= QALIAS;
- break;
-
- default: /* ignore or complain? */
- break;
- }
- }
- }
- else
- qflags |= QPRIMARY;
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
- "e r");
- if (*p != '\0')
- q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0',
- NULL, e, true);
- else
- q = NULL;
- if (q != NULL)
- {
- /* make sure we keep the current qgrp */
- if (ISVALIDQGRP(e->e_qgrp))
- q->q_qgrp = e->e_qgrp;
- q->q_alias = ctladdr;
- if (qfver >= 1)
- q->q_flags &= ~Q_PINGFLAGS;
- q->q_flags |= qflags;
- q->q_finalrcpt = frcpt;
- q->q_orcpt = orcpt;
- (void) recipient(q, &e->e_sendqueue, 0, e);
- }
- frcpt = NULL;
- orcpt = NULL;
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
- NULL);
- break;
-
- case 'S': /* sender */
- setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]),
- e, NULL, '\0', true);
- break;
-
- case 'T': /* init time */
- e->e_ctime = atol(&bp[1]);
- break;
-
- case 'V': /* queue file version number */
- qfver = atoi(&bp[1]);
- if (qfver <= QF_VERSION)
- break;
- syserr("Version number in queue file (%d) greater than max (%d)",
- qfver, QF_VERSION);
- err = "unsupported queue file version";
- goto fail;
- /* NOTREACHED */
- break;
-
- case 'Z': /* original envelope id from ESMTP */
- e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_envid}"), e->e_envid);
- break;
-
- case '!': /* deliver by */
-
- /* format: flag (1 char) space long-integer */
- e->e_dlvr_flag = buf[1];
- e->e_deliver_by = strtol(&buf[3], NULL, 10);
-
- case '$': /* define macro */
- {
- char *p;
-
- /* XXX elimate p? */
- r = macid_parse(&bp[1], &ep);
- if (r == 0)
- break;
- p = sm_rpool_strdup_x(e->e_rpool, ep);
- macdefine(&e->e_macro, A_PERM, r, p);
- }
- break;
-
- case '.': /* terminate file */
- nomore = true;
- break;
-
-#if _FFR_QUEUEDELAY
- case 'G':
- case 'Y':
-
- /*
- ** Maintain backward compatibility for
- ** users who defined _FFR_QUEUEDELAY in
- ** previous releases. Remove this
- ** code in 8.14 or 8.15.
- */
-
- if (qfver == 5 || qfver == 7)
- break;
-
- /* If not qfver 5 or 7, then 'G' or 'Y' is invalid */
- /* FALLTHROUGH */
-#endif /* _FFR_QUEUEDELAY */
-
- default:
- syserr("readqf: %s: line %d: bad line \"%s\"",
- qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
- err = "unrecognized line";
- goto fail;
- }
-
- if (bp != buf)
- SM_FREE(bp);
- }
-
- /*
- ** If we haven't read any lines, this queue file is empty.
- ** Arrange to remove it without referencing any null pointers.
- */
-
- if (LineNumber == 0)
- {
- errno = 0;
- e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE;
- return true;
- }
-
- /* Check to make sure we have a complete queue file read */
- if (!nomore)
- {
- syserr("readqf: %s: incomplete queue file read", qf);
- (void) sm_io_close(qfp, SM_TIME_DEFAULT);
- return false;
- }
-
-#if _FFR_QF_PARANOIA
- /* Check to make sure key fields were read */
- if (e->e_from.q_mailer == NULL)
- {
- syserr("readqf: %s: sender not specified in queue file", qf);
- (void) sm_io_close(qfp, SM_TIME_DEFAULT);
- return false;
- }
- /* other checks? */
-#endif /* _FFR_QF_PARANOIA */
-
- /* possibly set ${dsn_ret} macro */
- if (bitset(EF_RET_PARAM, e->e_flags))
- {
- if (bitset(EF_NO_BODY_RETN, e->e_flags))
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_ret}"), "hdrs");
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_ret}"), "full");
- }
-
- /*
- ** Arrange to read the data file.
- */
-
- p = queuename(e, DATAFL_LETTER);
- e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY_B,
- NULL);
- if (e->e_dfp == NULL)
- {
- syserr("readqf: cannot open %s", p);
- }
- else
- {
- e->e_flags |= EF_HAS_DF;
- if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st)
- >= 0)
- {
- e->e_msgsize = st.st_size + hdrsize;
- e->e_dfdev = st.st_dev;
- e->e_dfino = ST_INODE(st);
- (void) sm_snprintf(buf, sizeof(buf), "%ld",
- e->e_msgsize);
- macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"),
- buf);
- }
- }
-
- return true;
-
- fail:
- /*
- ** There was some error reading the qf file (reason is in err var.)
- ** Cleanup:
- ** close file; clear e_lockfp since it is the same as qfp,
- ** hence it is invalid (as file) after qfp is closed;
- ** the qf file is on disk, so set the flag to avoid calling
- ** queueup() with bogus data.
- */
-
- if (bp != buf)
- SM_FREE(bp);
- if (qfp != NULL)
- (void) sm_io_close(qfp, SM_TIME_DEFAULT);
- e->e_lockfp = NULL;
- e->e_flags |= EF_INQUEUE;
- loseqfile(e, err);
- return false;
-}
-/*
-** PRTSTR -- print a string, "unprintable" characters are shown as \oct
-**
-** Parameters:
-** s -- string to print
-** ml -- maximum length of output
-**
-** Returns:
-** number of entries
-**
-** Side Effects:
-** Prints a string on stdout.
-*/
-
-static void prtstr __P((char *, int));
-
-static void
-prtstr(s, ml)
- char *s;
- int ml;
-{
- int c;
-
- if (s == NULL)
- return;
- while (ml-- > 0 && ((c = *s++) != '\0'))
- {
- if (c == '\\')
- {
- if (ml-- > 0)
- {
- (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
- (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
- }
- }
- else if (isascii(c) && isprint(c))
- (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
- else
- {
- if ((ml -= 3) > 0)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "\\%03o", c & 0xFF);
- }
- }
-}
-/*
-** PRINTNQE -- print out number of entries in the mail queue
-**
-** Parameters:
-** out -- output file pointer.
-** prefix -- string to output in front of each line.
-**
-** Returns:
-** none.
-*/
-
-void
-printnqe(out, prefix)
- SM_FILE_T *out;
- char *prefix;
-{
-#if SM_CONF_SHM
- int i, k = 0, nrequests = 0;
- bool unknown = false;
-
- if (ShmId == SM_SHM_NO_ID)
- {
- if (prefix == NULL)
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
- "Data unavailable: shared memory not updated\n");
- else
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
- "%sNOTCONFIGURED:-1\r\n", prefix);
- return;
- }
- for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
- {
- int j;
-
- k++;
- for (j = 0; j < Queue[i]->qg_numqueues; j++)
- {
- int n;
-
- if (StopRequest)
- stop_sendmail();
-
- n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx);
- if (prefix != NULL)
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
- "%s%s:%d\r\n",
- prefix, qid_printqueue(i, j), n);
- else if (n < 0)
- {
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
- "%s: unknown number of entries\n",
- qid_printqueue(i, j));
- unknown = true;
- }
- else if (n == 0)
- {
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
- "%s is empty\n",
- qid_printqueue(i, j));
- }
- else if (n > 0)
- {
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
- "%s: entries=%d\n",
- qid_printqueue(i, j), n);
- nrequests += n;
- k++;
- }
- }
- }
- if (prefix == NULL && k > 1)
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
- "\t\tTotal requests: %d%s\n",
- nrequests, unknown ? " (about)" : "");
-#else /* SM_CONF_SHM */
- if (prefix == NULL)
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
- "Data unavailable without shared memory support\n");
- else
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
- "%sNOTAVAILABLE:-1\r\n", prefix);
-#endif /* SM_CONF_SHM */
-}
-/*
-** PRINTQUEUE -- print out a representation of the mail queue
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Prints a listing of the mail queue on the standard output.
-*/
-
-void
-printqueue()
-{
- int i, k = 0, nrequests = 0;
-
- for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
- {
- int j;
-
- k++;
- for (j = 0; j < Queue[i]->qg_numqueues; j++)
- {
- if (StopRequest)
- stop_sendmail();
- nrequests += print_single_queue(i, j);
- k++;
- }
- }
- if (k > 1)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "\t\tTotal requests: %d\n",
- nrequests);
-}
-/*
-** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
-**
-** Parameters:
-** qgrp -- the index of the queue group.
-** qdir -- the queue directory.
-**
-** Returns:
-** number of requests in mail queue.
-**
-** Side Effects:
-** Prints a listing of the mail queue on the standard output.
-*/
-
-int
-print_single_queue(qgrp, qdir)
- int qgrp;
- int qdir;
-{
- register WORK *w;
- SM_FILE_T *f;
- int nrequests;
- char qd[MAXPATHLEN];
- char qddf[MAXPATHLEN];
- char buf[MAXLINE];
-
- if (qdir == NOQDIR)
- {
- (void) sm_strlcpy(qd, ".", sizeof(qd));
- (void) sm_strlcpy(qddf, ".", sizeof(qddf));
- }
- else
- {
- (void) sm_strlcpyn(qd, sizeof(qd), 2,
- Queue[qgrp]->qg_qpaths[qdir].qp_name,
- (bitset(QP_SUBQF,
- Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
- ? "/qf" : ""));
- (void) sm_strlcpyn(qddf, sizeof(qddf), 2,
- Queue[qgrp]->qg_qpaths[qdir].qp_name,
- (bitset(QP_SUBDF,
- Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
- ? "/df" : ""));
- }
-
- /*
- ** Check for permission to print the queue
- */
-
- if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
- {
- struct stat st;
-#ifdef NGROUPS_MAX
- int n;
- extern GIDSET_T InitialGidSet[NGROUPS_MAX];
-#endif /* NGROUPS_MAX */
-
- if (stat(qd, &st) < 0)
- {
- syserr("Cannot stat %s",
- qid_printqueue(qgrp, qdir));
- return 0;
- }
-#ifdef NGROUPS_MAX
- n = NGROUPS_MAX;
- while (--n >= 0)
- {
- if (InitialGidSet[n] == st.st_gid)
- break;
- }
- if (n < 0 && RealGid != st.st_gid)
-#else /* NGROUPS_MAX */
- if (RealGid != st.st_gid)
-#endif /* NGROUPS_MAX */
- {
- usrerr("510 You are not permitted to see the queue");
- setstat(EX_NOPERM);
- return 0;
- }
- }
-
- /*
- ** Read and order the queue.
- */
-
- nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
- (void) sortq(Queue[qgrp]->qg_maxlist);
-
- /*
- ** Print the work list that we have read.
- */
-
- /* first see if there is anything */
- if (nrequests <= 0)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n",
- qid_printqueue(qgrp, qdir));
- return 0;
- }
-
- sm_getla(); /* get load average */
-
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s",
- qid_printqueue(qgrp, qdir),
- nrequests, nrequests == 1 ? "" : "s");
- if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- ", only %d printed", MaxQueueRun);
- if (Verbose)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- ")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n");
- else
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- ")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n");
- for (w = WorkQ; w != NULL; w = w->w_next)
- {
- struct stat st;
- auto time_t submittime = 0;
- long dfsize;
- int flags = 0;
- int qfver;
- char quarmsg[MAXLINE];
- char statmsg[MAXLINE];
- char bodytype[MAXNAME + 1];
- char qf[MAXPATHLEN];
-
- if (StopRequest)
- stop_sendmail();
-
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s",
- w->w_name + 2);
- (void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", w->w_name);
- f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
- NULL);
- if (f == NULL)
- {
- if (errno == EPERM)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- " (permission denied)\n");
- else if (errno == ENOENT)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- " (job completed)\n");
- else
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- " (%s)\n",
- sm_errstring(errno));
- errno = 0;
- continue;
- }
- w->w_name[0] = DATAFL_LETTER;
- (void) sm_strlcpyn(qf, sizeof(qf), 3, qddf, "/", w->w_name);
- if (stat(qf, &st) >= 0)
- dfsize = st.st_size;
- else
- {
- ENVELOPE e;
-
- /*
- ** Maybe the df file can't be statted because
- ** it is in a different directory than the qf file.
- ** In order to find out, we must read the qf file.
- */
-
- newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL));
- e.e_id = w->w_name + 2;
- e.e_qgrp = qgrp;
- e.e_qdir = qdir;
- dfsize = -1;
- if (readqf(&e, false))
- {
- char *df = queuename(&e, DATAFL_LETTER);
- if (stat(df, &st) >= 0)
- dfsize = st.st_size;
- }
- if (e.e_lockfp != NULL)
- {
- (void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT);
- e.e_lockfp = NULL;
- }
- clearenvelope(&e, false, e.e_rpool);
- sm_rpool_free(e.e_rpool);
- }
- if (w->w_lock)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*");
- else if (QueueMode == QM_LOST)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?");
- else if (w->w_tooyoung)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-");
- else if (shouldqueue(w->w_pri, w->w_ctime))
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X");
- else
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " ");
-
- errno = 0;
-
- quarmsg[0] = '\0';
- statmsg[0] = bodytype[0] = '\0';
- qfver = 0;
- while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
- {
- register int i;
- register char *p;
-
- if (StopRequest)
- stop_sendmail();
-
- fixcrlf(buf, true);
- switch (buf[0])
- {
- case 'V': /* queue file version */
- qfver = atoi(&buf[1]);
- break;
-
- case 'M': /* error message */
- if ((i = strlen(&buf[1])) >= sizeof(statmsg))
- i = sizeof(statmsg) - 1;
- memmove(statmsg, &buf[1], i);
- statmsg[i] = '\0';
- break;
-
- case 'q': /* quarantine reason */
- if ((i = strlen(&buf[1])) >= sizeof(quarmsg))
- i = sizeof(quarmsg) - 1;
- memmove(quarmsg, &buf[1], i);
- quarmsg[i] = '\0';
- break;
-
- case 'B': /* body type */
- if ((i = strlen(&buf[1])) >= sizeof(bodytype))
- i = sizeof(bodytype) - 1;
- memmove(bodytype, &buf[1], i);
- bodytype[i] = '\0';
- break;
-
- case 'S': /* sender name */
- if (Verbose)
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "%8ld %10ld%c%.12s ",
- dfsize,
- w->w_pri,
- bitset(EF_WARNING, flags)
- ? '+' : ' ',
- ctime(&submittime) + 4);
- prtstr(&buf[1], 78);
- }
- else
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "%8ld %.16s ",
- dfsize,
- ctime(&submittime));
- prtstr(&buf[1], 39);
- }
-
- if (quarmsg[0] != '\0')
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "\n QUARANTINE: %.*s",
- Verbose ? 100 : 60,
- quarmsg);
- quarmsg[0] = '\0';
- }
-
- if (statmsg[0] != '\0' || bodytype[0] != '\0')
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "\n %10.10s",
- bodytype);
- if (statmsg[0] != '\0')
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- " (%.*s)",
- Verbose ? 100 : 60,
- statmsg);
- statmsg[0] = '\0';
- }
- break;
-
- case 'C': /* controlling user */
- if (Verbose)
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "\n\t\t\t\t\t\t(---%.64s---)",
- &buf[1]);
- break;
-
- case 'R': /* recipient name */
- p = &buf[1];
- if (qfver >= 1)
- {
- p = strchr(p, ':');
- if (p == NULL)
- break;
- p++;
- }
- if (Verbose)
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "\n\t\t\t\t\t\t");
- prtstr(p, 71);
- }
- else
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "\n\t\t\t\t\t ");
- prtstr(p, 38);
- }
- if (Verbose && statmsg[0] != '\0')
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "\n\t\t (%.100s)",
- statmsg);
- statmsg[0] = '\0';
- }
- break;
-
- case 'T': /* creation time */
- submittime = atol(&buf[1]);
- break;
-
- case 'F': /* flag bits */
- for (p = &buf[1]; *p != '\0'; p++)
- {
- switch (*p)
- {
- case 'w':
- flags |= EF_WARNING;
- break;
- }
- }
- }
- }
- if (submittime == (time_t) 0)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- " (no control file)");
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
- (void) sm_io_close(f, SM_TIME_DEFAULT);
- }
- return nrequests;
-}
-
-/*
-** QUEUE_LETTER -- get the proper queue letter for the current QueueMode.
-**
-** Parameters:
-** e -- envelope to build it in/from.
-** type -- the file type, used as the first character
-** of the file name.
-**
-** Returns:
-** the letter to use
-*/
-
-static char
-queue_letter(e, type)
- ENVELOPE *e;
- int type;
-{
- /* Change type according to QueueMode */
- if (type == ANYQFL_LETTER)
- {
- if (e->e_quarmsg != NULL)
- type = QUARQF_LETTER;
- else
- {
- switch (QueueMode)
- {
- case QM_NORMAL:
- type = NORMQF_LETTER;
- break;
-
- case QM_QUARANTINE:
- type = QUARQF_LETTER;
- break;
-
- case QM_LOST:
- type = LOSEQF_LETTER;
- break;
-
- default:
- /* should never happen */
- abort();
- /* NOTREACHED */
- }
- }
- }
- return type;
-}
-
-/*
-** QUEUENAME -- build a file name in the queue directory for this envelope.
-**
-** 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 queue name (in a static buffer).
-**
-** Side Effects:
-** 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().
-*/
-
-char *
-queuename(e, type)
- register ENVELOPE *e;
- int type;
-{
- int qd, qg;
- char *sub = "/";
- char pref[3];
- static char buf[MAXPATHLEN];
-
- /* Assign an ID if needed */
- if (e->e_id == NULL)
- assign_queueid(e);
- type = queue_letter(e, type);
-
- /* begin of filename */
- pref[0] = (char) type;
- pref[1] = 'f';
- pref[2] = '\0';
-
- /* Assign a queue group/directory if needed */
- if (type == XSCRPT_LETTER)
- {
- /*
- ** We don't want to call setnewqueue() if we are fetching
- ** the pathname of the transcript file, because setnewqueue
- ** chooses a queue, and sometimes we need to write to the
- ** transcript file before we have gathered enough information
- ** to choose a queue.
- */
-
- if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
- {
- if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR)
- {
- e->e_xfqgrp = e->e_qgrp;
- e->e_xfqdir = e->e_qdir;
- }
- else
- {
- e->e_xfqgrp = 0;
- if (Queue[e->e_xfqgrp]->qg_numqueues <= 1)
- e->e_xfqdir = 0;
- else
- {
- e->e_xfqdir = get_rand_mod(
- Queue[e->e_xfqgrp]->qg_numqueues);
- }
- }
- }
- qd = e->e_xfqdir;
- qg = e->e_xfqgrp;
- }
- else
- {
- if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
- (void) setnewqueue(e);
- if (type == DATAFL_LETTER)
- {
- qd = e->e_dfqdir;
- qg = e->e_dfqgrp;
- }
- else
- {
- qd = e->e_qdir;
- qg = e->e_qgrp;
- }
- }
-
- /* xf files always have a valid qd and qg picked above */
- if ((qd == NOQDIR || qg == NOQGRP) && type != XSCRPT_LETTER)
- (void) sm_strlcpyn(buf, sizeof(buf), 2, pref, e->e_id);
- else
- {
- switch (type)
- {
- case DATAFL_LETTER:
- if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
- sub = "/df/";
- break;
-
- case QUARQF_LETTER:
- case TEMPQF_LETTER:
- case NEWQFL_LETTER:
- case LOSEQF_LETTER:
- case NORMQF_LETTER:
- if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
- sub = "/qf/";
- break;
-
- case XSCRPT_LETTER:
- if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
- sub = "/xf/";
- break;
-
- default:
- sm_abort("queuename: bad queue file type %d", type);
- }
-
- (void) sm_strlcpyn(buf, sizeof(buf), 4,
- Queue[qg]->qg_qpaths[qd].qp_name,
- sub, pref, e->e_id);
- }
-
- if (tTd(7, 2))
- sm_dprintf("queuename: %s\n", buf);
- return buf;
-}
-
-/*
-** INIT_QID_ALG -- Initialize the (static) parameters that are used to
-** generate a queue ID.
-**
-** This function is called by the daemon to reset
-** LastQueueTime and LastQueuePid which are used by assign_queueid().
-** Otherwise the algorithm may cause problems because
-** LastQueueTime and LastQueuePid are set indirectly by main()
-** before the daemon process is started, hence LastQueuePid is not
-** the pid of the daemon and therefore a child of the daemon can
-** actually have the same pid as LastQueuePid which means the section
-** in assign_queueid():
-** * see if we need to get a new base time/pid *
-** is NOT triggered which will cause the same queue id to be generated.
-**
-** Parameters:
-** none
-**
-** Returns:
-** none.
-*/
-
-void
-init_qid_alg()
-{
- LastQueueTime = 0;
- LastQueuePid = -1;
-}
-
-/*
-** 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 do not already exist in the queue.
-** [No longer initializes e_qdir to NOQDIR.]
-**
-** Parameters:
-** e -- envelope to set it in.
-**
-** Returns:
-** none.
-*/
-
-static const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-# define QIC_LEN 60
-# define QIC_LEN_R 62
-
-/*
-** Note: the length is "officially" 60 because minutes and seconds are
-** usually only 0-59. However (Linux):
-** tm_sec The number of seconds after the minute, normally in
-** the range 0 to 59, but can be up to 61 to allow for
-** leap seconds.
-** Hence the real length of the string is 62 to take this into account.
-** Alternatively % QIC_LEN can (should) be used for access everywhere.
-*/
-
-# define queuenextid() CurrentPid
-
-
-void
-assign_queueid(e)
- register ENVELOPE *e;
-{
- pid_t pid = queuenextid();
- static int cX = 0;
- static long random_offset;
- struct tm *tm;
- char idbuf[MAXQFNAME - 2];
- int seq;
-
- if (e->e_id != NULL)
- return;
-
- /* see if we need to get a new base time/pid */
- if (cX >= QIC_LEN * QIC_LEN || 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)
- {
- (void) sleep(1);
- }
- LastQueuePid = queuenextid();
- cX = 0;
- }
-
- /*
- ** Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1.
- ** This lets us generate up to QIC_LEN*QIC_LEN unique queue ids
- ** per second, per process. With envelope splitting,
- ** a single message can consume many queue ids.
- */
-
- seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN));
- ++cX;
- if (tTd(7, 50))
- sm_dprintf("assign_queueid: random_offset = %ld (%d)\n",
- random_offset, seq);
-
- tm = gmtime(&LastQueueTime);
- idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN];
- idbuf[1] = QueueIdChars[tm->tm_mon];
- idbuf[2] = QueueIdChars[tm->tm_mday];
- idbuf[3] = QueueIdChars[tm->tm_hour];
- idbuf[4] = QueueIdChars[tm->tm_min % QIC_LEN_R];
- idbuf[5] = QueueIdChars[tm->tm_sec % QIC_LEN_R];
- idbuf[6] = QueueIdChars[seq / QIC_LEN];
- idbuf[7] = QueueIdChars[seq % QIC_LEN];
- (void) sm_snprintf(&idbuf[8], sizeof(idbuf) - 8, "%06d",
- (int) LastQueuePid);
- e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf);
- macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
-#if 0
- /* XXX: inherited from MainEnvelope */
- e->e_qgrp = NOQGRP; /* too early to do anything else */
- e->e_qdir = NOQDIR;
- e->e_xfqgrp = NOQGRP;
-#endif /* 0 */
-
- /* New ID means it's not on disk yet */
- e->e_qfletter = '\0';
-
- if (tTd(7, 1))
- sm_dprintf("assign_queueid: assigned id %s, e=%p\n",
- e->e_id, e);
- if (LogLevel > 93)
- sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
-}
-/*
-** SYNC_QUEUE_TIME -- Assure exclusive PID in any given second
-**
-** Make sure one PID can't be used by two processes in any one second.
-**
-** 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 == CurrentPid &&
- curtime() == LastQueueTime)
- (void) sleep(1);
-#endif /* FAST_PID_RECYCLE */
-}
-/*
-** UNLOCKQUEUE -- unlock the queue entry for a specified envelope
-**
-** Parameters:
-** e -- the envelope to unlock.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** unlocks the queue for `e'.
-*/
-
-void
-unlockqueue(e)
- ENVELOPE *e;
-{
- if (tTd(51, 4))
- sm_dprintf("unlockqueue(%s)\n",
- e->e_id == NULL ? "NOQUEUE" : e->e_id);
-
-
- /* if there is a lock file in the envelope, close it */
- if (e->e_lockfp != NULL)
- (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
- e->e_lockfp = NULL;
-
- /* don't create a queue id if we don't already have one */
- if (e->e_id == NULL)
- return;
-
- /* remove the transcript */
- if (LogLevel > 87)
- sm_syslog(LOG_DEBUG, e->e_id, "unlock");
- if (!tTd(51, 104))
- (void) xunlink(queuename(e, XSCRPT_LETTER));
-}
-/*
-** SETCTLUSER -- create a controlling address
-**
-** Create a fake "address" given only a local login name; this is
-** used as a "controlling user" for future recipient addresses.
-**
-** Parameters:
-** user -- the user name of the controlling user.
-** qfver -- the version stamp of this queue file.
-** e -- envelope
-**
-** Returns:
-** An address descriptor for the controlling user,
-** using storage allocated from e->e_rpool.
-**
-*/
-
-static ADDRESS *
-setctluser(user, qfver, e)
- char *user;
- int qfver;
- ENVELOPE *e;
-{
- register ADDRESS *a;
- struct passwd *pw;
- char *p;
-
- /*
- ** See if this clears our concept of controlling user.
- */
-
- if (user == NULL || *user == '\0')
- return NULL;
-
- /*
- ** Set up addr fields for controlling user.
- */
-
- a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a));
- memset((char *) a, '\0', sizeof(*a));
-
- if (*user == ':')
- {
- p = &user[1];
- a->q_user = sm_rpool_strdup_x(e->e_rpool, p);
- }
- else
- {
- p = strtok(user, ":");
- a->q_user = sm_rpool_strdup_x(e->e_rpool, user);
- if (qfver >= 2)
- {
- if ((p = strtok(NULL, ":")) != NULL)
- a->q_uid = atoi(p);
- if ((p = strtok(NULL, ":")) != NULL)
- a->q_gid = atoi(p);
- if ((p = strtok(NULL, ":")) != NULL)
- {
- char *o;
-
- a->q_flags |= QGOODUID;
-
- /* if there is another ':': restore it */
- if ((o = strtok(NULL, ":")) != NULL && o > p)
- o[-1] = ':';
- }
- }
- else if ((pw = sm_getpwnam(user)) != NULL)
- {
- if (*pw->pw_dir == '\0')
- a->q_home = NULL;
- else if (strcmp(pw->pw_dir, "/") == 0)
- a->q_home = "";
- else
- a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir);
- a->q_uid = pw->pw_uid;
- a->q_gid = pw->pw_gid;
- a->q_flags |= QGOODUID;
- }
- }
-
- a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */
- a->q_mailer = LocalMailer;
- if (p == NULL)
- a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
- else
- a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p);
- return a;
-}
-/*
-** LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know
-**
-** Parameters:
-** e -- the envelope (e->e_id will be used).
-** why -- reported to whomever can hear.
-**
-** Returns:
-** none.
-*/
-
-void
-loseqfile(e, why)
- register ENVELOPE *e;
- char *why;
-{
- bool loseit = true;
- char *p;
- char buf[MAXPATHLEN];
-
- if (e == NULL || e->e_id == NULL)
- return;
- p = queuename(e, ANYQFL_LETTER);
- if (sm_strlcpy(buf, p, sizeof(buf)) >= sizeof(buf))
- return;
- if (!bitset(EF_INQUEUE, e->e_flags))
- queueup(e, false, true);
- else if (QueueMode == QM_LOST)
- loseit = false;
-
- /* if already lost, no need to re-lose */
- if (loseit)
- {
- p = queuename(e, LOSEQF_LETTER);
- if (rename(buf, p) < 0)
- syserr("cannot rename(%s, %s), uid=%d",
- buf, p, (int) geteuid());
- else if (LogLevel > 0)
- sm_syslog(LOG_ALERT, e->e_id,
- "Losing %s: %s", buf, why);
- }
- if (e->e_dfp != NULL)
- {
- (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
- e->e_dfp = NULL;
- }
- e->e_flags &= ~EF_HAS_DF;
-}
-/*
-** NAME2QID -- translate a queue group name to a queue group id
-**
-** Parameters:
-** queuename -- name of queue group.
-**
-** Returns:
-** queue group id if found.
-** NOQGRP otherwise.
-*/
-
-int
-name2qid(queuename)
- char *queuename;
-{
- register STAB *s;
-
- s = stab(queuename, ST_QUEUE, ST_FIND);
- if (s == NULL)
- return NOQGRP;
- return s->s_quegrp->qg_index;
-}
-/*
-** QID_PRINTNAME -- create externally printable version of queue id
-**
-** Parameters:
-** 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_qdir == NOQDIR)
- return id;
-
- (void) sm_snprintf(idbuf, sizeof(idbuf), "%.32s/%s",
- Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name,
- id);
- return idbuf;
-}
-/*
-** QID_PRINTQUEUE -- create full version of queue directory for data files
-**
-** Parameters:
-** qgrp -- index in queue group.
-** qdir -- the short version of the queue directory
-**
-** Returns:
-** the full pathname to the queue (might point to a static var)
-*/
-
-char *
-qid_printqueue(qgrp, qdir)
- int qgrp;
- int qdir;
-{
- char *subdir;
- static char dir[MAXPATHLEN];
-
- if (qdir == NOQDIR)
- return Queue[qgrp]->qg_qdir;
-
- if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0)
- subdir = NULL;
- else
- subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name;
-
- (void) sm_strlcpyn(dir, sizeof(dir), 4,
- Queue[qgrp]->qg_qdir,
- subdir == NULL ? "" : "/",
- subdir == NULL ? "" : subdir,
- (bitset(QP_SUBDF,
- Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
- ? "/df" : ""));
- return dir;
-}
-
-/*
-** PICKQDIR -- Pick a queue directory from a queue group
-**
-** Parameters:
-** qg -- queue group
-** fsize -- file size in bytes
-** e -- envelope, or NULL
-**
-** Result:
-** NOQDIR if no queue directory in qg has enough free space to
-** hold a file of size 'fsize', otherwise the index of
-** a randomly selected queue directory which resides on a
-** file system with enough disk space.
-** XXX This could be extended to select a queuedir with
-** a few (the fewest?) number of entries. That data
-** is available if shared memory is used.
-**
-** Side Effects:
-** If the request fails and e != NULL then sm_syslog is called.
-*/
-
-int
-pickqdir(qg, fsize, e)
- QUEUEGRP *qg;
- long fsize;
- ENVELOPE *e;
-{
- int qdir;
- int i;
- long avail = 0;
-
- /* Pick a random directory, as a starting point. */
- if (qg->qg_numqueues <= 1)
- qdir = 0;
- else
- qdir = get_rand_mod(qg->qg_numqueues);
-
- if (MinBlocksFree <= 0 && fsize <= 0)
- return qdir;
-
- /*
- ** Now iterate over the queue directories,
- ** looking for a directory with enough space for this message.
- */
-
- i = qdir;
- do
- {
- QPATHS *qp = &qg->qg_qpaths[i];
- long needed = 0;
- long fsavail = 0;
-
- if (fsize > 0)
- needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx)
- + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx)
- > 0) ? 1 : 0);
- if (MinBlocksFree > 0)
- needed += MinBlocksFree;
- fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx);
-#if SM_CONF_SHM
- if (fsavail <= 0)
- {
- long blksize;
-
- /*
- ** might be not correctly updated,
- ** let's try to get the info directly.
- */
-
- fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx),
- &blksize);
- if (fsavail < 0)
- fsavail = 0;
- }
-#endif /* SM_CONF_SHM */
- if (needed <= fsavail)
- return i;
- if (avail < fsavail)
- avail = fsavail;
-
- if (qg->qg_numqueues > 0)
- i = (i + 1) % qg->qg_numqueues;
- } while (i != qdir);
-
- if (e != NULL && LogLevel > 0)
- sm_syslog(LOG_ALERT, e->e_id,
- "low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld",
- CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
- fsize, MinBlocksFree,
- qg->qg_qdir, avail);
- return NOQDIR;
-}
-/*
-** SETNEWQUEUE -- Sets a new queue group and directory
-**
-** Assign a queue group and directory to an envelope and store the
-** directory in e->e_qdir.
-**
-** Parameters:
-** e -- envelope to assign a queue for.
-**
-** Returns:
-** true if successful
-** false otherwise
-**
-** Side Effects:
-** On success, e->e_qgrp and e->e_qdir are non-negative.
-** On failure (not enough disk space),
-** e->qgrp = NOQGRP, e->e_qdir = NOQDIR
-** and usrerr() is invoked (which could raise an exception).
-*/
-
-bool
-setnewqueue(e)
- ENVELOPE *e;
-{
- if (tTd(41, 20))
- sm_dprintf("setnewqueue: called\n");
-
- /* not set somewhere else */
- if (e->e_qgrp == NOQGRP)
- {
- ADDRESS *q;
-
- /*
- ** Use the queue group of the "first" recipient, as set by
- ** the "queuegroup" rule set. If that is not defined, then
- ** use the queue group of the mailer of the first recipient.
- ** If that is not defined either, then use the default
- ** queue group.
- ** Notice: "first" depends on the sorting of sendqueue
- ** in recipient().
- ** To avoid problems with "bad" recipients look
- ** for a valid address first.
- */
-
- q = e->e_sendqueue;
- while (q != NULL &&
- (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state)))
- {
- q = q->q_next;
- }
- if (q == NULL)
- e->e_qgrp = 0;
- else if (q->q_qgrp >= 0)
- e->e_qgrp = q->q_qgrp;
- else if (q->q_mailer != NULL &&
- ISVALIDQGRP(q->q_mailer->m_qgrp))
- e->e_qgrp = q->q_mailer->m_qgrp;
- else
- e->e_qgrp = 0;
- e->e_dfqgrp = e->e_qgrp;
- }
-
- if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir))
- {
- if (tTd(41, 20))
- sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n",
- qid_printqueue(e->e_qgrp, e->e_qdir));
- return true;
- }
-
- filesys_update();
- e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e);
- if (e->e_qdir == NOQDIR)
- {
- e->e_qgrp = NOQGRP;
- if (!bitset(EF_FATALERRS, e->e_flags))
- usrerr("452 4.4.5 Insufficient disk space; try again later");
- e->e_flags |= EF_FATALERRS;
- return false;
- }
-
- if (tTd(41, 3))
- sm_dprintf("setnewqueue: Assigned queue directory %s\n",
- qid_printqueue(e->e_qgrp, e->e_qdir));
-
- if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
- {
- e->e_xfqgrp = e->e_qgrp;
- e->e_xfqdir = e->e_qdir;
- }
- e->e_dfqdir = e->e_qdir;
- return true;
-}
-/*
-** CHKQDIR -- check a queue directory
-**
-** Parameters:
-** name -- name of queue directory
-** sff -- flags for safefile()
-**
-** Returns:
-** is it a queue directory?
-*/
-
-static bool chkqdir __P((char *, long));
-
-static bool
-chkqdir(name, sff)
- char *name;
- long sff;
-{
- struct stat statb;
- int i;
-
- /* skip over . and .. directories */
- if (name[0] == '.' &&
- (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
- return false;
-#if HASLSTAT
- if (lstat(name, &statb) < 0)
-#else /* HASLSTAT */
- if (stat(name, &statb) < 0)
-#endif /* HASLSTAT */
- {
- if (tTd(41, 2))
- sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
- name, sm_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))
- sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
- name, sm_errstring(errno));
- return false;
- }
- }
-#endif /* HASLSTAT */
-
- if (!S_ISDIR(statb.st_mode))
- {
- if (tTd(41, 2))
- sm_dprintf("chkqdir: \"%s\": Not a directory\n",
- name);
- return false;
- }
-
- /* Print a warning if unsafe (but still use it) */
- /* XXX do this only if we want the warning? */
- i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0);
- if (i != 0)
- {
- if (tTd(41, 2))
- sm_dprintf("chkqdir: \"%s\": Not safe: %s\n",
- name, sm_errstring(i));
-#if _FFR_CHK_QUEUE
- if (LogLevel > 8)
- sm_syslog(LOG_WARNING, NOQID,
- "queue directory \"%s\": Not safe: %s",
- name, sm_errstring(i));
-#endif /* _FFR_CHK_QUEUE */
- }
- 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:
-** basedir -- base of all queue directories.
-** blen -- strlen(basedir).
-** qg -- queue group.
-** qn -- number of queue directories already cached.
-** phash -- pointer to hash value over queue dirs.
-#if SM_CONF_SHM
-** only used if shared memory is active.
-#endif * SM_CONF_SHM *
-**
-** Returns:
-** new number of queue directories.
-*/
-
-#define INITIAL_SLOTS 20
-#define ADD_SLOTS 10
-
-static int
-multiqueue_cache(basedir, blen, qg, qn, phash)
- char *basedir;
- int blen;
- QUEUEGRP *qg;
- int qn;
- unsigned int *phash;
-{
- char *cp;
- int i, len;
- int slotsleft = 0;
- long sff = SFF_ANYFILE;
- char qpath[MAXPATHLEN];
- char subdir[MAXPATHLEN];
- char prefix[MAXPATHLEN]; /* dir relative to basedir */
-
- if (tTd(41, 20))
- sm_dprintf("multiqueue_cache: called\n");
-
- /* Initialize to current directory */
- prefix[0] = '.';
- prefix[1] = '\0';
- if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL)
- {
- for (i = 0; i < qg->qg_numqueues; i++)
- {
- if (qg->qg_qpaths[i].qp_name != NULL)
- (void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */
- }
- (void) sm_free((char *) qg->qg_qpaths); /* XXX */
- qg->qg_qpaths = NULL;
- qg->qg_numqueues = 0;
- }
-
- /* If running as root, allow safedirpath() checks to use privs */
- if (RunAsUid == 0)
- sff |= SFF_ROOTOK;
-#if _FFR_CHK_QUEUE
- sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES;
- if (!UseMSP)
- sff |= SFF_NOGWFILES;
-#endif /* _FFR_CHK_QUEUE */
-
- if (!SM_IS_DIR_START(qg->qg_qdir))
- {
- /*
- ** XXX we could add basedir, but then we have to realloc()
- ** the string... Maybe another time.
- */
-
- syserr("QueuePath %s not absolute", qg->qg_qdir);
- ExitStat = EX_CONFIG;
- return qn;
- }
-
- /* qpath: directory of current workgroup */
- len = sm_strlcpy(qpath, qg->qg_qdir, sizeof(qpath));
- if (len >= sizeof(qpath))
- {
- syserr("QueuePath %.256s too long (%d max)",
- qg->qg_qdir, (int) sizeof(qpath));
- ExitStat = EX_CONFIG;
- return qn;
- }
-
- /* begin of qpath must be same as basedir */
- if (strncmp(basedir, qpath, blen) != 0 &&
- (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1))
- {
- syserr("QueuePath %s not subpath of QueueDirectory %s",
- qpath, basedir);
- ExitStat = EX_CONFIG;
- return qn;
- }
-
- /* Do we have a nested subdirectory? */
- if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL)
- {
-
- /* Copy subdirectory into prefix for later use */
- if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof(prefix)) >=
- sizeof(prefix))
- {
- syserr("QueuePath %.256s too long (%d max)",
- qg->qg_qdir, (int) sizeof(qpath));
- ExitStat = EX_CONFIG;
- return qn;
- }
- cp = SM_LAST_DIR_DELIM(prefix);
- SM_ASSERT(cp != NULL);
- *cp = '\0'; /* cut off trailing / */
- }
-
- /* This is guaranteed by the basedir check above */
- SM_ASSERT(len >= blen - 1);
- cp = &qpath[len - 1];
- if (*cp == '*')
- {
- register DIR *dp;
- register struct dirent *d;
- int off;
- char *delim;
- char relpath[MAXPATHLEN];
-
- *cp = '\0'; /* Overwrite wildcard */
- if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL)
- {
- syserr("QueueDirectory: can not wildcard relative path");
- if (tTd(41, 2))
- sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n",
- qpath);
- ExitStat = EX_CONFIG;
- return qn;
- }
- if (cp == qpath)
- {
- /*
- ** Special case of top level wildcard, like /foo*
- ** Change to //foo*
- */
-
- (void) sm_strlcpy(qpath + 1, qpath, sizeof(qpath) - 1);
- ++cp;
- }
- delim = cp;
- *(cp++) = '\0'; /* Replace / with \0 */
- len = strlen(cp); /* Last component of queue directory */
-
- /*
- ** Path relative to basedir, with trailing /
- ** It will be modified below to specify the subdirectories
- ** so they can be opened without chdir().
- */
-
- off = sm_strlcpyn(relpath, sizeof(relpath), 2, prefix, "/");
- SM_ASSERT(off < sizeof(relpath));
-
- if (tTd(41, 2))
- sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n",
- relpath, cp);
-
- /* It is always basedir: we don't need to store it per group */
- /* XXX: optimize this! -> one more global? */
- qg->qg_qdir = newstr(basedir);
- qg->qg_qdir[blen - 1] = '\0'; /* cut off trailing / */
-
- /*
- ** XXX Should probably wrap this whole loop in a timeout
- ** in case some wag decides to NFS mount the queues.
- */
-
- /* Test path to get warning messages. */
- if (qn == 0)
- {
- /* XXX qg_runasuid and qg_runasgid for specials? */
- i = safedirpath(basedir, RunAsUid, RunAsGid, NULL,
- sff, 0, 0);
- if (i != 0 && tTd(41, 2))
- sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
- basedir, sm_errstring(i));
- }
-
- if ((dp = opendir(prefix)) == NULL)
- {
- syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix);
- if (tTd(41, 2))
- sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n",
- qg->qg_qdir, prefix,
- sm_errstring(errno));
- ExitStat = EX_CONFIG;
- return qn;
- }
- while ((d = readdir(dp)) != NULL)
- {
- /* Skip . and .. directories */
- if (strcmp(d->d_name, ".") == 0 ||
- strcmp(d->d_name, "..") == 0)
- continue;
-
- i = strlen(d->d_name);
- if (i < len || strncmp(d->d_name, cp, len) != 0)
- {
- if (tTd(41, 5))
- sm_dprintf("multiqueue_cache: \"%s\", skipped\n",
- d->d_name);
- continue;
- }
-
- /* Create relative pathname: prefix + local directory */
- i = sizeof(relpath) - off;
- if (sm_strlcpy(relpath + off, d->d_name, i) >= i)
- continue; /* way too long */
-
- if (!chkqdir(relpath, sff))
- continue;
-
- if (qg->qg_qpaths == NULL)
- {
- slotsleft = INITIAL_SLOTS;
- qg->qg_qpaths = (QPATHS *)xalloc((sizeof(*qg->qg_qpaths)) *
- slotsleft);
- qg->qg_numqueues = 0;
- }
- else if (slotsleft < 1)
- {
- qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths,
- (sizeof(*qg->qg_qpaths)) *
- (qg->qg_numqueues +
- ADD_SLOTS));
- if (qg->qg_qpaths == NULL)
- {
- (void) closedir(dp);
- return qn;
- }
- slotsleft += ADD_SLOTS;
- }
-
- /* check subdirs */
- qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB;
-
-#define CHKRSUBDIR(name, flag) \
- (void) sm_strlcpyn(subdir, sizeof(subdir), 3, relpath, "/", name); \
- if (chkqdir(subdir, sff)) \
- qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag; \
- else
-
-
- CHKRSUBDIR("qf", QP_SUBQF);
- CHKRSUBDIR("df", QP_SUBDF);
- CHKRSUBDIR("xf", QP_SUBXF);
-
- /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
- /* maybe even - 17 (subdirs) */
-
- if (prefix[0] != '.')
- qg->qg_qpaths[qg->qg_numqueues].qp_name =
- newstr(relpath);
- else
- qg->qg_qpaths[qg->qg_numqueues].qp_name =
- newstr(d->d_name);
-
- if (tTd(41, 2))
- sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
- qg->qg_numqueues, relpath,
- qg->qg_qpaths[qg->qg_numqueues].qp_subdirs);
-#if SM_CONF_SHM
- qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn;
- *phash = hash_q(relpath, *phash);
-#endif /* SM_CONF_SHM */
- qg->qg_numqueues++;
- ++qn;
- slotsleft--;
- }
- (void) closedir(dp);
-
- /* undo damage */
- *delim = '/';
- }
- if (qg->qg_numqueues == 0)
- {
- qg->qg_qpaths = (QPATHS *) xalloc(sizeof(*qg->qg_qpaths));
-
- /* test path to get warning messages */
- i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0);
- if (i == ENOENT)
- {
- syserr("can not opendir(%s)", qpath);
- if (tTd(41, 2))
- sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
- qpath, sm_errstring(i));
- ExitStat = EX_CONFIG;
- return qn;
- }
-
- qg->qg_qpaths[0].qp_subdirs = QP_NOSUB;
- qg->qg_numqueues = 1;
-
- /* check subdirs */
-#define CHKSUBDIR(name, flag) \
- (void) sm_strlcpyn(subdir, sizeof(subdir), 3, qg->qg_qdir, "/", name); \
- if (chkqdir(subdir, sff)) \
- qg->qg_qpaths[0].qp_subdirs |= flag; \
- else
-
- CHKSUBDIR("qf", QP_SUBQF);
- CHKSUBDIR("df", QP_SUBDF);
- CHKSUBDIR("xf", QP_SUBXF);
-
- if (qg->qg_qdir[blen - 1] != '\0' &&
- qg->qg_qdir[blen] != '\0')
- {
- /*
- ** Copy the last component into qpaths and
- ** cut off qdir
- */
-
- qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen);
- qg->qg_qdir[blen - 1] = '\0';
- }
- else
- qg->qg_qpaths[0].qp_name = newstr(".");
-
-#if SM_CONF_SHM
- qg->qg_qpaths[0].qp_idx = qn;
- *phash = hash_q(qg->qg_qpaths[0].qp_name, *phash);
-#endif /* SM_CONF_SHM */
- ++qn;
- }
- return qn;
-}
-
-/*
-** FILESYS_FIND -- find entry in FileSys table, or add new one
-**
-** Given the pathname of a directory, determine the file system
-** in which that directory resides, and return a pointer to the
-** entry in the FileSys table that describes the file system.
-** A new entry is added if necessary (and requested).
-** If the directory does not exist, -1 is returned.
-**
-** Parameters:
-** name -- name of directory (must be persistent!)
-** path -- pathname of directory (name plus maybe "/df")
-** add -- add to structure if not found.
-**
-** Returns:
-** >=0: found: index in file system table
-** <0: some error, i.e.,
-** FSF_TOO_MANY: too many filesystems (-> syserr())
-** FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
-** FSF_NOT_FOUND: not in list
-*/
-
-static short filesys_find __P((const char *, const char *, bool));
-
-#define FSF_NOT_FOUND (-1)
-#define FSF_STAT_FAIL (-2)
-#define FSF_TOO_MANY (-3)
-
-static short
-filesys_find(name, path, add)
- const char *name;
- const char *path;
- bool add;
-{
- struct stat st;
- short i;
-
- if (stat(path, &st) < 0)
- {
- syserr("cannot stat queue directory %s", path);
- return FSF_STAT_FAIL;
- }
- for (i = 0; i < NumFileSys; ++i)
- {
- if (FILE_SYS_DEV(i) == st.st_dev)
- {
- /*
- ** Make sure the file system (FS) name is set:
- ** even though the source code indicates that
- ** FILE_SYS_DEV() is only set below, it could be
- ** set via shared memory, hence we need to perform
- ** this check/assignment here.
- */
-
- if (NULL == FILE_SYS_NAME(i))
- FILE_SYS_NAME(i) = name;
- return i;
- }
- }
- if (i >= MAXFILESYS)
- {
- syserr("too many queue file systems (%d max)", MAXFILESYS);
- return FSF_TOO_MANY;
- }
- if (!add)
- return FSF_NOT_FOUND;
-
- ++NumFileSys;
- FILE_SYS_NAME(i) = name;
- FILE_SYS_DEV(i) = st.st_dev;
- FILE_SYS_AVAIL(i) = 0;
- FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */
- return i;
-}
-
-/*
-** FILESYS_SETUP -- set up mapping from queue directories to file systems
-**
-** This data structure is used to efficiently check the amount of
-** free space available in a set of queue directories.
-**
-** Parameters:
-** add -- initialize structure if necessary.
-**
-** Returns:
-** 0: success
-** <0: some error, i.e.,
-** FSF_NOT_FOUND: not in list
-** FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
-** FSF_TOO_MANY: too many filesystems (-> syserr())
-*/
-
-static int filesys_setup __P((bool));
-
-static int
-filesys_setup(add)
- bool add;
-{
- int i, j;
- short fs;
- int ret;
-
- ret = 0;
- for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
- {
- for (j = 0; j < Queue[i]->qg_numqueues; ++j)
- {
- QPATHS *qp = &Queue[i]->qg_qpaths[j];
- char qddf[MAXPATHLEN];
-
- (void) sm_strlcpyn(qddf, sizeof(qddf), 2, qp->qp_name,
- (bitset(QP_SUBDF, qp->qp_subdirs)
- ? "/df" : ""));
- fs = filesys_find(qp->qp_name, qddf, add);
- if (fs >= 0)
- qp->qp_fsysidx = fs;
- else
- qp->qp_fsysidx = 0;
- if (fs < ret)
- ret = fs;
- }
- }
- return ret;
-}
-
-/*
-** FILESYS_UPDATE -- update amount of free space on all file systems
-**
-** The FileSys table is used to cache the amount of free space
-** available on all queue directory file systems.
-** This function updates the cached information if it has expired.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Updates FileSys table.
-*/
-
-void
-filesys_update()
-{
- int i;
- long avail, blksize;
- time_t now;
- static time_t nextupdate = 0;
-
-#if SM_CONF_SHM
- /*
- ** Only the daemon updates the shared memory, i.e.,
- ** if shared memory is available but the pid is not the
- ** one of the daemon, then don't do anything.
- */
-
- if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid)
- return;
-#endif /* SM_CONF_SHM */
- now = curtime();
- if (now < nextupdate)
- return;
- nextupdate = now + FILESYS_UPDATE_INTERVAL;
- for (i = 0; i < NumFileSys; ++i)
- {
- FILESYS *fs = &FILE_SYS(i);
-
- avail = freediskspace(FILE_SYS_NAME(i), &blksize);
- if (avail < 0 || blksize <= 0)
- {
- if (LogLevel > 5)
- sm_syslog(LOG_ERR, NOQID,
- "filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld",
- sm_errstring(errno),
- FILE_SYS_NAME(i), avail, blksize);
- fs->fs_avail = 0;
- fs->fs_blksize = 1024; /* avoid divide by zero */
- nextupdate = now + 2; /* let's do this soon again */
- }
- else
- {
- fs->fs_avail = avail;
- fs->fs_blksize = blksize;
- }
- }
-}
-
-#if _FFR_ANY_FREE_FS
-/*
-** FILESYS_FREE -- check whether there is at least one fs with enough space.
-**
-** Parameters:
-** fsize -- file size in bytes
-**
-** Returns:
-** true iff there is one fs with more than fsize bytes free.
-*/
-
-bool
-filesys_free(fsize)
- long fsize;
-{
- int i;
-
- if (fsize <= 0)
- return true;
- for (i = 0; i < NumFileSys; ++i)
- {
- long needed = 0;
-
- if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0)
- continue;
- needed += fsize / FILE_SYS_BLKSIZE(i)
- + ((fsize % FILE_SYS_BLKSIZE(i)
- > 0) ? 1 : 0)
- + MinBlocksFree;
- if (needed <= FILE_SYS_AVAIL(i))
- return true;
- }
- return false;
-}
-#endif /* _FFR_ANY_FREE_FS */
-
-/*
-** DISK_STATUS -- show amount of free space in queue directories
-**
-** Parameters:
-** out -- output file pointer.
-** prefix -- string to output in front of each line.
-**
-** Returns:
-** none.
-*/
-
-void
-disk_status(out, prefix)
- SM_FILE_T *out;
- char *prefix;
-{
- int i;
- long avail, blksize;
- long free;
-
- for (i = 0; i < NumFileSys; ++i)
- {
- avail = freediskspace(FILE_SYS_NAME(i), &blksize);
- if (avail >= 0 && blksize > 0)
- {
- free = (long)((double) avail *
- ((double) blksize / 1024));
- }
- else
- free = -1;
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT,
- "%s%d/%s/%ld\r\n",
- prefix, i,
- FILE_SYS_NAME(i),
- free);
- }
-}
-
-#if SM_CONF_SHM
-
-/*
-** INIT_SEM -- initialize semaphore system
-**
-** Parameters:
-** owner -- is this the owner of semaphores?
-**
-** Returns:
-** none.
-*/
-
-#if _FFR_USE_SEM_LOCKING
-#if SM_CONF_SEM
-static int SemId = -1; /* Semaphore Id */
-int SemKey = SM_SEM_KEY;
-#endif /* SM_CONF_SEM */
-#endif /* _FFR_USE_SEM_LOCKING */
-
-static void init_sem __P((bool));
-
-static void
-init_sem(owner)
- bool owner;
-{
-#if _FFR_USE_SEM_LOCKING
-#if SM_CONF_SEM
- SemId = sm_sem_start(SemKey, 1, 0, owner);
- if (SemId < 0)
- {
- sm_syslog(LOG_ERR, NOQID,
- "func=init_sem, sem_key=%ld, sm_sem_start=%d",
- (long) SemKey, SemId);
- return;
- }
-#endif /* SM_CONF_SEM */
-#endif /* _FFR_USE_SEM_LOCKING */
- return;
-}
-
-/*
-** STOP_SEM -- stop semaphore system
-**
-** Parameters:
-** owner -- is this the owner of semaphores?
-**
-** Returns:
-** none.
-*/
-
-static void stop_sem __P((bool));
-
-static void
-stop_sem(owner)
- bool owner;
-{
-#if _FFR_USE_SEM_LOCKING
-#if SM_CONF_SEM
- if (owner && SemId >= 0)
- sm_sem_stop(SemId);
-#endif /* SM_CONF_SEM */
-#endif /* _FFR_USE_SEM_LOCKING */
- return;
-}
-
-/*
-** UPD_QS -- update information about queue when adding/deleting an entry
-**
-** Parameters:
-** e -- envelope.
-** count -- add/remove entry (+1/0/-1: add/no change/remove)
-** space -- update the space available as well.
-** (>0/0/<0: add/no change/remove)
-** where -- caller (for logging)
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Modifies available space in filesystem.
-** Changes number of entries in queue directory.
-*/
-
-void
-upd_qs(e, count, space, where)
- ENVELOPE *e;
- int count;
- int space;
- char *where;
-{
- short fidx;
- int idx;
-# if _FFR_USE_SEM_LOCKING
- int r;
-# endif /* _FFR_USE_SEM_LOCKING */
- long s;
-
- if (ShmId == SM_SHM_NO_ID || e == NULL)
- return;
- if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
- return;
- idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx;
- if (tTd(73,2))
- sm_dprintf("func=upd_qs, count=%d, space=%d, where=%s, idx=%d, entries=%d\n",
- count, space, where, idx, QSHM_ENTRIES(idx));
-
- /* XXX in theory this needs to be protected with a mutex */
- if (QSHM_ENTRIES(idx) >= 0 && count != 0)
- {
-# if _FFR_USE_SEM_LOCKING
- r = sm_sem_acq(SemId, 0, 1);
-# endif /* _FFR_USE_SEM_LOCKING */
- QSHM_ENTRIES(idx) += count;
-# if _FFR_USE_SEM_LOCKING
- if (r >= 0)
- r = sm_sem_rel(SemId, 0, 1);
-# endif /* _FFR_USE_SEM_LOCKING */
- }
-
- fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx;
- if (fidx < 0)
- return;
-
- /* update available space also? (might be loseqfile) */
- if (space == 0)
- return;
-
- /* convert size to blocks; this causes rounding errors */
- s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx);
- if (s == 0)
- return;
-
- /* XXX in theory this needs to be protected with a mutex */
- if (space > 0)
- FILE_SYS_AVAIL(fidx) += s;
- else
- FILE_SYS_AVAIL(fidx) -= s;
-
-}
-
-static bool write_key_file __P((char *, long));
-static long read_key_file __P((char *, long));
-
-/*
-** WRITE_KEY_FILE -- record some key into a file.
-**
-** Parameters:
-** keypath -- file name.
-** key -- key to write.
-**
-** Returns:
-** true iff file could be written.
-**
-** Side Effects:
-** writes file.
-*/
-
-static bool
-write_key_file(keypath, key)
- char *keypath;
- long key;
-{
- bool ok;
- long sff;
- SM_FILE_T *keyf;
-
- ok = false;
- if (keypath == NULL || *keypath == '\0')
- return ok;
- sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
- if (TrustedUid != 0 && RealUid == TrustedUid)
- sff |= SFF_OPENASROOT;
- keyf = safefopen(keypath, O_WRONLY|O_TRUNC, FileMode, sff);
- if (keyf == NULL)
- {
- sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
- keypath, sm_errstring(errno));
- }
- else
- {
- if (geteuid() == 0 && RunAsUid != 0)
- {
-# if HASFCHOWN
- int fd;
-
- fd = keyf->f_file;
- if (fd >= 0 && fchown(fd, RunAsUid, -1) < 0)
- {
- int err = errno;
-
- sm_syslog(LOG_ALERT, NOQID,
- "ownership change on %s to %d failed: %s",
- keypath, RunAsUid, sm_errstring(err));
- }
-# endif /* HASFCHOWN */
- }
- ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) !=
- SM_IO_EOF;
- ok = (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF) && ok;
- }
- return ok;
-}
-
-/*
-** READ_KEY_FILE -- read a key from a file.
-**
-** Parameters:
-** keypath -- file name.
-** key -- default key.
-**
-** Returns:
-** key.
-*/
-
-static long
-read_key_file(keypath, key)
- char *keypath;
- long key;
-{
- int r;
- long sff, n;
- SM_FILE_T *keyf;
-
- if (keypath == NULL || *keypath == '\0')
- return key;
- sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY;
- if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid))
- sff |= SFF_OPENASROOT;
- keyf = safefopen(keypath, O_RDONLY, FileMode, sff);
- if (keyf == NULL)
- {
- sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s",
- keypath, sm_errstring(errno));
- }
- else
- {
- r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n);
- if (r == 1)
- key = n;
- (void) sm_io_close(keyf, SM_TIME_DEFAULT);
- }
- return key;
-}
-
-/*
-** INIT_SHM -- initialize shared memory structure
-**
-** Initialize or attach to shared memory segment.
-** Currently it is not a fatal error if this doesn't work.
-** However, it causes us to have a "fallback" storage location
-** for everything that is supposed to be in the shared memory,
-** which makes the code slightly ugly.
-**
-** Parameters:
-** qn -- number of queue directories.
-** owner -- owner of shared memory.
-** hash -- identifies data that is stored in shared memory.
-**
-** Returns:
-** none.
-*/
-
-static void init_shm __P((int, bool, unsigned int));
-
-static void
-init_shm(qn, owner, hash)
- int qn;
- bool owner;
- unsigned int hash;
-{
- int i;
- int count;
- int save_errno;
- bool keyselect;
-
- PtrFileSys = &FileSys[0];
- PNumFileSys = &Numfilesys;
-/* if this "key" is specified: select one yourself */
-#define SEL_SHM_KEY ((key_t) -1)
-#define FIRST_SHM_KEY 25
-
- /* This allows us to disable shared memory at runtime. */
- if (ShmKey == 0)
- return;
-
- count = 0;
- shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T);
- keyselect = ShmKey == SEL_SHM_KEY;
- if (keyselect)
- {
- if (owner)
- ShmKey = FIRST_SHM_KEY;
- else
- {
- errno = 0;
- ShmKey = read_key_file(ShmKeyFile, ShmKey);
- keyselect = false;
- if (ShmKey == SEL_SHM_KEY)
- {
- save_errno = (errno != 0) ? errno : EINVAL;
- goto error;
- }
- }
- }
- for (;;)
- {
- /* allow read/write access for group? */
- Pshm = sm_shmstart(ShmKey, shms,
- SHM_R|SHM_W|(SHM_R>>3)|(SHM_W>>3),
- &ShmId, owner);
- save_errno = errno;
- if (Pshm != NULL || !sm_file_exists(save_errno))
- break;
- if (++count >= 3)
- {
- if (keyselect)
- {
- ++ShmKey;
-
- /* back where we started? */
- if (ShmKey == SEL_SHM_KEY)
- break;
- continue;
- }
- break;
- }
-
- /* only sleep if we are at the first key */
- if (!keyselect || ShmKey == SEL_SHM_KEY)
- sleep(count);
- }
- if (Pshm != NULL)
- {
- int *p;
-
- if (keyselect)
- (void) write_key_file(ShmKeyFile, (long) ShmKey);
- if (owner && RunAsUid != 0)
- {
- i = sm_shmsetowner(ShmId, RunAsUid, RunAsGid, 0660);
- if (i != 0)
- sm_syslog(LOG_ERR, NOQID,
- "key=%ld, sm_shmsetowner=%d, RunAsUid=%d, RunAsGid=%d",
- (long) ShmKey, i, RunAsUid, RunAsGid);
- }
- p = (int *) Pshm;
- if (owner)
- {
- *p = (int) shms;
- *((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid;
- p = (int *) SHM_OFF_TAG(Pshm);
- *p = hash;
- }
- else
- {
- if (*p != (int) shms)
- {
- save_errno = EINVAL;
- cleanup_shm(false);
- goto error;
- }
- p = (int *) SHM_OFF_TAG(Pshm);
- if (*p != (int) hash)
- {
- save_errno = EINVAL;
- cleanup_shm(false);
- goto error;
- }
-
- /*
- ** XXX how to check the pid?
- ** Read it from the pid-file? That does
- ** not need to exist.
- ** We could disable shm if we can't confirm
- ** that it is the right one.
- */
- }
-
- PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm);
- PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm);
- QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm);
- PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm);
- *PRSATmpCnt = 0;
- if (owner)
- {
- /* initialize values in shared memory */
- NumFileSys = 0;
- for (i = 0; i < qn; i++)
- QShm[i].qs_entries = -1;
- }
- init_sem(owner);
- return;
- }
- error:
- if (LogLevel > (owner ? 8 : 11))
- {
- sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID,
- "can't %s shared memory, key=%ld: %s",
- owner ? "initialize" : "attach to",
- (long) ShmKey, sm_errstring(save_errno));
- }
-}
-#endif /* SM_CONF_SHM */
-
-
-/*
-** SETUP_QUEUES -- set up all queue groups
-**
-** Parameters:
-** owner -- owner of shared memory?
-**
-** Returns:
-** none.
-**
-#if SM_CONF_SHM
-** Side Effects:
-** attaches shared memory.
-#endif * SM_CONF_SHM *
-*/
-
-void
-setup_queues(owner)
- bool owner;
-{
- int i, qn, len;
- unsigned int hashval;
- time_t now;
- char basedir[MAXPATHLEN];
- struct stat st;
-
- /*
- ** Determine basedir for all queue directories.
- ** All queue directories must be (first level) subdirectories
- ** of the basedir. The basedir is the QueueDir
- ** without wildcards, but with trailing /
- */
-
- hashval = 0;
- errno = 0;
- len = sm_strlcpy(basedir, QueueDir, sizeof(basedir));
-
- /* Provide space for trailing '/' */
- if (len >= sizeof(basedir) - 1)
- {
- syserr("QueueDirectory: path too long: %d, max %d",
- len, (int) sizeof(basedir) - 1);
- ExitStat = EX_CONFIG;
- return;
- }
- SM_ASSERT(len > 0);
- if (basedir[len - 1] == '*')
- {
- char *cp;
-
- cp = SM_LAST_DIR_DELIM(basedir);
- if (cp == NULL)
- {
- syserr("QueueDirectory: can not wildcard relative path \"%s\"",
- QueueDir);
- if (tTd(41, 2))
- sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n",
- QueueDir);
- ExitStat = EX_CONFIG;
- return;
- }
-
- /* cut off wildcard pattern */
- *++cp = '\0';
- len = cp - basedir;
- }
- else if (!SM_IS_DIR_DELIM(basedir[len - 1]))
- {
- /* append trailing slash since it is a directory */
- basedir[len] = '/';
- basedir[++len] = '\0';
- }
-
- /* len counts up to the last directory delimiter */
- SM_ASSERT(basedir[len - 1] == '/');
-
- if (chdir(basedir) < 0)
- {
- int save_errno = errno;
-
- syserr("can not chdir(%s)", basedir);
- if (save_errno == EACCES)
- (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
- "Program mode requires special privileges, e.g., root or TrustedUser.\n");
- if (tTd(41, 2))
- sm_dprintf("setup_queues: \"%s\": %s\n",
- basedir, sm_errstring(errno));
- ExitStat = EX_CONFIG;
- return;
- }
-#if SM_CONF_SHM
- hashval = hash_q(basedir, hashval);
-#endif /* SM_CONF_SHM */
-
- /* initialize for queue runs */
- DoQueueRun = false;
- now = curtime();
- for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
- Queue[i]->qg_nextrun = now;
-
-
- if (UseMSP && OpMode != MD_TEST)
- {
- long sff = SFF_CREAT;
-
- if (stat(".", &st) < 0)
- {
- syserr("can not stat(%s)", basedir);
- if (tTd(41, 2))
- sm_dprintf("setup_queues: \"%s\": %s\n",
- basedir, sm_errstring(errno));
- ExitStat = EX_CONFIG;
- return;
- }
- if (RunAsUid == 0)
- sff |= SFF_ROOTOK;
-
- /*
- ** Check queue directory permissions.
- ** Can we write to a group writable queue directory?
- */
-
- if (bitset(S_IWGRP, QueueFileMode) &&
- bitset(S_IWGRP, st.st_mode) &&
- safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff,
- QueueFileMode, NULL) != 0)
- {
- syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)",
- basedir, (int) RunAsGid, (int) st.st_gid);
- }
- if (bitset(S_IWOTH|S_IXOTH, st.st_mode))
- {
-#if _FFR_MSP_PARANOIA
- syserr("dangerous permissions=%o on queue directory %s",
- (int) st.st_mode, basedir);
-#else /* _FFR_MSP_PARANOIA */
- if (LogLevel > 0)
- sm_syslog(LOG_ERR, NOQID,
- "dangerous permissions=%o on queue directory %s",
- (int) st.st_mode, basedir);
-#endif /* _FFR_MSP_PARANOIA */
- }
-#if _FFR_MSP_PARANOIA
- if (NumQueue > 1)
- syserr("can not use multiple queues for MSP");
-#endif /* _FFR_MSP_PARANOIA */
- }
-
- /* initial number of queue directories */
- qn = 0;
- for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
- qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval);
-
-#if SM_CONF_SHM
- init_shm(qn, owner, hashval);
- i = filesys_setup(owner || ShmId == SM_SHM_NO_ID);
- if (i == FSF_NOT_FOUND)
- {
- /*
- ** We didn't get the right filesystem data
- ** This may happen if we don't have the right shared memory.
- ** So let's do this without shared memory.
- */
-
- SM_ASSERT(!owner);
- cleanup_shm(false); /* release shared memory */
- i = filesys_setup(false);
- if (i < 0)
- syserr("filesys_setup failed twice, result=%d", i);
- else if (LogLevel > 8)
- sm_syslog(LOG_WARNING, NOQID,
- "shared memory does not contain expected data, ignored");
- }
-#else /* SM_CONF_SHM */
- i = filesys_setup(true);
-#endif /* SM_CONF_SHM */
- if (i < 0)
- ExitStat = EX_CONFIG;
-}
-
-#if SM_CONF_SHM
-/*
-** CLEANUP_SHM -- do some cleanup work for shared memory etc
-**
-** Parameters:
-** owner -- owner of shared memory?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** detaches shared memory.
-*/
-
-void
-cleanup_shm(owner)
- bool owner;
-{
- if (ShmId != SM_SHM_NO_ID)
- {
- if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s",
- sm_errstring(errno));
- Pshm = NULL;
- ShmId = SM_SHM_NO_ID;
- }
- stop_sem(owner);
-}
-#endif /* SM_CONF_SHM */
-
-/*
-** CLEANUP_QUEUES -- do some cleanup work for queues
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-*/
-
-void
-cleanup_queues()
-{
- sync_queue_time();
-}
-/*
-** SET_DEF_QUEUEVAL -- set default values for a queue group.
-**
-** Parameters:
-** qg -- queue group
-** all -- set all values (true for default group)?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** sets default values for the queue group.
-*/
-
-void
-set_def_queueval(qg, all)
- QUEUEGRP *qg;
- bool all;
-{
- if (bitnset(QD_DEFINED, qg->qg_flags))
- return;
- if (all)
- qg->qg_qdir = QueueDir;
-#if _FFR_QUEUE_GROUP_SORTORDER
- qg->qg_sortorder = QueueSortOrder;
-#endif /* _FFR_QUEUE_GROUP_SORTORDER */
- qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1;
- qg->qg_nice = NiceQueueRun;
-}
-/*
-** MAKEQUEUE -- define a new queue.
-**
-** Parameters:
-** line -- description of queue. This is in labeled fields.
-** The fields are:
-** F -- the flags associated with the queue
-** I -- the interval between running the queue
-** J -- the maximum # of jobs in work list
-** [M -- the maximum # of jobs in a queue run]
-** N -- the niceness at which to run
-** P -- the path to the queue
-** S -- the queue sorting order
-** R -- number of parallel queue runners
-** r -- max recipients per envelope
-** The first word is the canonical name of the queue.
-** qdef -- this is a 'Q' definition from .cf
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** enters the queue into the queue table.
-*/
-
-void
-makequeue(line, qdef)
- char *line;
- bool qdef;
-{
- register char *p;
- register QUEUEGRP *qg;
- register STAB *s;
- int i;
- char fcode;
-
- /* allocate a queue and set up defaults */
- qg = (QUEUEGRP *) xalloc(sizeof(*qg));
- memset((char *) qg, '\0', sizeof(*qg));
-
- if (line[0] == '\0')
- {
- syserr("name required for queue");
- return;
- }
-
- /* collect the queue name */
- for (p = line;
- *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
- p++)
- continue;
- if (*p != '\0')
- *p++ = '\0';
- qg->qg_name = newstr(line);
-
- /* set default values, can be overridden below */
- set_def_queueval(qg, false);
-
- /* now scan through and assign info from the fields */
- while (*p != '\0')
- {
- auto char *delimptr;
-
- while (*p != '\0' &&
- (*p == ',' || (isascii(*p) && isspace(*p))))
- p++;
-
- /* p now points to field code */
- fcode = *p;
- while (*p != '\0' && *p != '=' && *p != ',')
- p++;
- if (*p++ != '=')
- {
- syserr("queue %s: `=' expected", qg->qg_name);
- return;
- }
- while (isascii(*p) && isspace(*p))
- p++;
-
- /* p now points to the field body */
- p = munchstring(p, &delimptr, ',');
-
- /* install the field into the queue struct */
- switch (fcode)
- {
- case 'P': /* pathname */
- if (*p == '\0')
- syserr("queue %s: empty path name",
- qg->qg_name);
- else
- qg->qg_qdir = newstr(p);
- break;
-
- case 'F': /* flags */
- for (; *p != '\0'; p++)
- if (!(isascii(*p) && isspace(*p)))
- setbitn(*p, qg->qg_flags);
- break;
-
- /*
- ** Do we need two intervals here:
- ** One for persistent queue runners,
- ** one for "normal" queue runs?
- */
-
- case 'I': /* interval between running the queue */
- qg->qg_queueintvl = convtime(p, 'm');
- break;
-
- case 'N': /* run niceness */
- qg->qg_nice = atoi(p);
- break;
-
- case 'R': /* maximum # of runners for the group */
- i = atoi(p);
-
- /* can't have more runners than allowed total */
- if (MaxQueueChildren > 0 && i > MaxQueueChildren)
- {
- qg->qg_maxqrun = MaxQueueChildren;
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n",
- qg->qg_name, i,
- MaxQueueChildren);
- }
- else
- qg->qg_maxqrun = i;
- break;
-
- case 'J': /* maximum # of jobs in work list */
- qg->qg_maxlist = atoi(p);
- break;
-
- case 'r': /* max recipients per envelope */
- qg->qg_maxrcpt = atoi(p);
- break;
-
-#if _FFR_QUEUE_GROUP_SORTORDER
- case 'S': /* queue sorting order */
- switch (*p)
- {
- case 'h': /* Host first */
- case 'H':
- qg->qg_sortorder = QSO_BYHOST;
- break;
-
- case 'p': /* Priority order */
- case 'P':
- qg->qg_sortorder = QSO_BYPRIORITY;
- break;
-
- case 't': /* Submission time */
- case 'T':
- qg->qg_sortorder = QSO_BYTIME;
- break;
-
- case 'f': /* File name */
- case 'F':
- qg->qg_sortorder = QSO_BYFILENAME;
- break;
-
- case 'm': /* Modification time */
- case 'M':
- qg->qg_sortorder = QSO_BYMODTIME;
- break;
-
- case 'r': /* Random */
- case 'R':
- qg->qg_sortorder = QSO_RANDOM;
- break;
-
-# if _FFR_RHS
- case 's': /* Shuffled host name */
- case 'S':
- qg->qg_sortorder = QSO_BYSHUFFLE;
- break;
-# endif /* _FFR_RHS */
-
- case 'n': /* none */
- case 'N':
- qg->qg_sortorder = QSO_NONE;
- break;
-
- default:
- syserr("Invalid queue sort order \"%s\"", p);
- }
- break;
-#endif /* _FFR_QUEUE_GROUP_SORTORDER */
-
- default:
- syserr("Q%s: unknown queue equate %c=",
- qg->qg_name, fcode);
- break;
- }
-
- p = delimptr;
- }
-
-#if !HASNICE
- if (qg->qg_nice != NiceQueueRun)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Q%s: Warning: N= set on system that doesn't support nice()\n",
- qg->qg_name);
- }
-#endif /* !HASNICE */
-
- /* do some rationality checking */
- if (NumQueue >= MAXQUEUEGROUPS)
- {
- syserr("too many queue groups defined (%d max)",
- MAXQUEUEGROUPS);
- return;
- }
-
- if (qg->qg_qdir == NULL)
- {
- if (QueueDir == NULL || *QueueDir == '\0')
- {
- syserr("QueueDir must be defined before queue groups");
- return;
- }
- qg->qg_qdir = newstr(QueueDir);
- }
-
- if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags))
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n",
- qg->qg_name, qg->qg_maxqrun, QD_FORK);
- }
-
- /* enter the queue into the symbol table */
- if (tTd(37, 8))
- sm_syslog(LOG_INFO, NOQID,
- "Adding %s to stab, path: %s", qg->qg_name,
- qg->qg_qdir);
- s = stab(qg->qg_name, ST_QUEUE, ST_ENTER);
- if (s->s_quegrp != NULL)
- {
- i = s->s_quegrp->qg_index;
-
- /* XXX what about the pointers inside this struct? */
- sm_free(s->s_quegrp); /* XXX */
- }
- else
- i = NumQueue++;
- Queue[i] = s->s_quegrp = qg;
- qg->qg_index = i;
-
- /* set default value for max queue runners */
- if (qg->qg_maxqrun < 0)
- {
- if (MaxRunnersPerQueue > 0)
- qg->qg_maxqrun = MaxRunnersPerQueue;
- else
- qg->qg_maxqrun = 1;
- }
- if (qdef)
- setbitn(QD_DEFINED, qg->qg_flags);
-}
-#if 0
-/*
-** HASHFQN -- calculate a hash value for a fully qualified host name
-**
-** Arguments:
-** 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;
-
- 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 */
-
-/*
-** A structure for sorting Queue according to maxqrun without
-** screwing up Queue itself.
-*/
-
-struct sortqgrp
-{
- int sg_idx; /* original index */
- int sg_maxqrun; /* max queue runners */
-};
-typedef struct sortqgrp SORTQGRP_T;
-static int cmpidx __P((const void *, const void *));
-
-static int
-cmpidx(a, b)
- const void *a;
- const void *b;
-{
- /* The sort is highest to lowest, so the comparison is reversed */
- if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun)
- return 1;
- else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun)
- return -1;
- else
- return 0;
-}
-
-/*
-** MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren
-**
-** Take the now defined queue groups and assign them to work groups.
-** This is done to balance out the number of concurrently active
-** queue runners such that MaxQueueChildren is not exceeded. This may
-** result in more than one queue group per work group. In such a case
-** the number of running queue groups in that work group will have no
-** more than the work group maximum number of runners (a "fair" portion
-** of MaxQueueRunners). All queue groups within a work group will get a
-** chance at running.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** nothing.
-**
-** Side Effects:
-** Sets up WorkGrp structure.
-*/
-
-void
-makeworkgroups()
-{
- int i, j, total_runners, dir, h;
- SORTQGRP_T si[MAXQUEUEGROUPS + 1];
-
- total_runners = 0;
- if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0)
- {
- /*
- ** There is only the "mqueue" queue group (a default)
- ** containing all of the queues. We want to provide to
- ** this queue group the maximum allowable queue runners.
- ** To match older behavior (8.10/8.11) we'll try for
- ** 1 runner per queue capping it at MaxQueueChildren.
- ** So if there are N queues, then there will be N runners
- ** for the "mqueue" queue group (where N is kept less than
- ** MaxQueueChildren).
- */
-
- NumWorkGroups = 1;
- WorkGrp[0].wg_numqgrp = 1;
- WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *));
- WorkGrp[0].wg_qgs[0] = Queue[0];
- if (MaxQueueChildren > 0 &&
- Queue[0]->qg_numqueues > MaxQueueChildren)
- WorkGrp[0].wg_runners = MaxQueueChildren;
- else
- WorkGrp[0].wg_runners = Queue[0]->qg_numqueues;
-
- Queue[0]->qg_wgrp = 0;
-
- /* can't have more runners than allowed total */
- if (MaxQueueChildren > 0 &&
- Queue[0]->qg_maxqrun > MaxQueueChildren)
- Queue[0]->qg_maxqrun = MaxQueueChildren;
- WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun;
- WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl;
- return;
- }
-
- for (i = 0; i < NumQueue; i++)
- {
- si[i].sg_maxqrun = Queue[i]->qg_maxqrun;
- si[i].sg_idx = i;
- }
- qsort(si, NumQueue, sizeof(si[0]), cmpidx);
-
- NumWorkGroups = 0;
- for (i = 0; i < NumQueue; i++)
- {
- total_runners += si[i].sg_maxqrun;
- if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren)
- NumWorkGroups++;
- else
- break;
- }
-
- if (NumWorkGroups < 1)
- NumWorkGroups = 1; /* gotta have one at least */
- else if (NumWorkGroups > MAXWORKGROUPS)
- NumWorkGroups = MAXWORKGROUPS; /* the limit */
-
- /*
- ** We now know the number of work groups to pack the queue groups
- ** into. The queue groups in 'Queue' are sorted from highest
- ** to lowest for the number of runners per queue group.
- ** We put the queue groups with the largest number of runners
- ** into work groups first. Then the smaller ones are fitted in
- ** where it looks best.
- */
-
- j = 0;
- dir = 1;
- for (i = 0; i < NumQueue; i++)
- {
- /* a to-and-fro packing scheme, continue from last position */
- if (j >= NumWorkGroups)
- {
- dir = -1;
- j = NumWorkGroups - 1;
- }
- else if (j < 0)
- {
- j = 0;
- dir = 1;
- }
-
- if (WorkGrp[j].wg_qgs == NULL)
- WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) *
- (WorkGrp[j].wg_numqgrp + 1));
- else
- WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs,
- sizeof(QUEUEGRP *) *
- (WorkGrp[j].wg_numqgrp + 1));
- if (WorkGrp[j].wg_qgs == NULL)
- {
- syserr("!cannot allocate memory for work queues, need %d bytes",
- (int) (sizeof(QUEUEGRP *) *
- (WorkGrp[j].wg_numqgrp + 1)));
- }
-
- h = si[i].sg_idx;
- WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[h];
- WorkGrp[j].wg_numqgrp++;
- WorkGrp[j].wg_runners += Queue[h]->qg_maxqrun;
- Queue[h]->qg_wgrp = j;
-
- if (WorkGrp[j].wg_maxact == 0)
- {
- /* can't have more runners than allowed total */
- if (MaxQueueChildren > 0 &&
- Queue[h]->qg_maxqrun > MaxQueueChildren)
- Queue[h]->qg_maxqrun = MaxQueueChildren;
- WorkGrp[j].wg_maxact = Queue[h]->qg_maxqrun;
- }
-
- /*
- ** XXX: must wg_lowqintvl be the GCD?
- ** qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for
- ** qg2 occur?
- */
-
- /* keep track of the lowest interval for a persistent runner */
- if (Queue[h]->qg_queueintvl > 0 &&
- WorkGrp[j].wg_lowqintvl < Queue[h]->qg_queueintvl)
- WorkGrp[j].wg_lowqintvl = Queue[h]->qg_queueintvl;
- j += dir;
- }
- if (tTd(41, 9))
- {
- for (i = 0; i < NumWorkGroups; i++)
- {
- sm_dprintf("Workgroup[%d]=", i);
- for (j = 0; j < WorkGrp[i].wg_numqgrp; j++)
- {
- sm_dprintf("%s, ",
- WorkGrp[i].wg_qgs[j]->qg_name);
- }
- sm_dprintf("\n");
- }
- }
-}
-
-/*
-** DUP_DF -- duplicate envelope data file
-**
-** Copy the data file from the 'old' envelope to the 'new' envelope
-** in the most efficient way possible.
-**
-** Create a hard link from the 'old' data file to the 'new' data file.
-** If the old and new queue directories are on different file systems,
-** then the new data file link is created in the old queue directory,
-** and the new queue file will contain a 'd' record pointing to the
-** directory containing the new data file.
-**
-** Parameters:
-** old -- old envelope.
-** new -- new envelope.
-**
-** Results:
-** Returns true on success, false on failure.
-**
-** Side Effects:
-** On success, the new data file is created.
-** On fatal failure, EF_FATALERRS is set in old->e_flags.
-*/
-
-static bool dup_df __P((ENVELOPE *, ENVELOPE *));
-
-static bool
-dup_df(old, new)
- ENVELOPE *old;
- ENVELOPE *new;
-{
- int ofs, nfs, r;
- char opath[MAXPATHLEN];
- char npath[MAXPATHLEN];
-
- if (!bitset(EF_HAS_DF, old->e_flags))
- {
- /*
- ** this can happen if: SuperSafe != True
- ** and a bounce mail is sent that is split.
- */
-
- queueup(old, false, true);
- }
- SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir));
- SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir));
-
- (void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof(opath));
- (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath));
-
- if (old->e_dfp != NULL)
- {
- r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL);
- if (r < 0 && errno != EINVAL)
- {
- syserr("@can't commit %s", opath);
- old->e_flags |= EF_FATALERRS;
- return false;
- }
- }
-
- /*
- ** Attempt to create a hard link, if we think both old and new
- ** are on the same file system, otherwise copy the file.
- **
- ** Don't waste time attempting a hard link unless old and new
- ** are on the same file system.
- */
-
- SM_REQUIRE(ISVALIDQGRP(old->e_dfqgrp) && ISVALIDQDIR(old->e_dfqdir));
- SM_REQUIRE(ISVALIDQGRP(new->e_dfqgrp) && ISVALIDQDIR(new->e_dfqdir));
-
- ofs = Queue[old->e_dfqgrp]->qg_qpaths[old->e_dfqdir].qp_fsysidx;
- nfs = Queue[new->e_dfqgrp]->qg_qpaths[new->e_dfqdir].qp_fsysidx;
- if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs))
- {
- if (link(opath, npath) == 0)
- {
- new->e_flags |= EF_HAS_DF;
- SYNC_DIR(npath, true);
- return true;
- }
- goto error;
- }
-
- /*
- ** Can't link across queue directories, so try to create a hard
- ** link in the same queue directory as the old df file.
- ** The qf file will refer to the new df file using a 'd' record.
- */
-
- new->e_dfqgrp = old->e_dfqgrp;
- new->e_dfqdir = old->e_dfqdir;
- (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath));
- if (link(opath, npath) == 0)
- {
- new->e_flags |= EF_HAS_DF;
- SYNC_DIR(npath, true);
- return true;
- }
-
- error:
- if (LogLevel > 0)
- sm_syslog(LOG_ERR, old->e_id,
- "dup_df: can't link %s to %s, error=%s, envelope splitting failed",
- opath, npath, sm_errstring(errno));
- return false;
-}
-
-/*
-** SPLIT_ENV -- Allocate a new envelope based on a given envelope.
-**
-** Parameters:
-** e -- envelope.
-** sendqueue -- sendqueue for new envelope.
-** qgrp -- index of queue group.
-** qdir -- queue directory.
-**
-** Results:
-** new envelope.
-**
-*/
-
-static ENVELOPE *split_env __P((ENVELOPE *, ADDRESS *, int, int));
-
-static ENVELOPE *
-split_env(e, sendqueue, qgrp, qdir)
- ENVELOPE *e;
- ADDRESS *sendqueue;
- int qgrp;
- int qdir;
-{
- ENVELOPE *ee;
-
- ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof(*ee));
- STRUCTCOPY(*e, *ee);
- ee->e_message = NULL; /* XXX use original message? */
- ee->e_id = NULL;
- assign_queueid(ee);
- ee->e_sendqueue = sendqueue;
- ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS
- |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF);
- ee->e_flags |= EF_NORECEIPT; /* XXX really? */
- ee->e_from.q_state = QS_SENDER;
- ee->e_dfp = NULL;
- ee->e_lockfp = NULL;
- if (e->e_xfp != NULL)
- ee->e_xfp = sm_io_dup(e->e_xfp);
-
- /* failed to dup e->e_xfp, start a new transcript */
- if (ee->e_xfp == NULL)
- openxscript(ee);
-
- ee->e_qgrp = ee->e_dfqgrp = qgrp;
- ee->e_qdir = ee->e_dfqdir = qdir;
- ee->e_errormode = EM_MAIL;
- ee->e_statmsg = NULL;
- if (e->e_quarmsg != NULL)
- ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
- e->e_quarmsg);
-
- /*
- ** XXX Not sure if this copying is necessary.
- ** sendall() does this copying, but I (dm) don't know if that is
- ** because of the storage management discipline we were using
- ** before rpools were introduced, or if it is because these lists
- ** can be modified later.
- */
-
- ee->e_header = copyheader(e->e_header, ee->e_rpool);
- ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool);
-
- return ee;
-}
-
-/* return values from split functions, check also below! */
-#define SM_SPLIT_FAIL (0)
-#define SM_SPLIT_NONE (1)
-#define SM_SPLIT_NEW(n) (1 + (n))
-
-/*
-** SPLIT_ACROSS_QUEUE_GROUPS
-**
-** This function splits an envelope across multiple queue groups
-** based on the queue group of each recipient.
-**
-** Parameters:
-** e -- envelope.
-**
-** Results:
-** SM_SPLIT_FAIL on failure
-** SM_SPLIT_NONE if no splitting occurred,
-** or 1 + the number of additional envelopes created.
-**
-** Side Effects:
-** On success, e->e_sibling points to a list of zero or more
-** additional envelopes, and the associated data files exist
-** on disk. But the queue files are not created.
-**
-** On failure, e->e_sibling is not changed.
-** The order of recipients in e->e_sendqueue is permuted.
-** Abandoned data files for additional envelopes that failed
-** to be created may exist on disk.
-*/
-
-static int q_qgrp_compare __P((const void *, const void *));
-static int e_filesys_compare __P((const void *, const void *));
-
-static int
-q_qgrp_compare(p1, p2)
- const void *p1;
- const void *p2;
-{
- ADDRESS **pq1 = (ADDRESS **) p1;
- ADDRESS **pq2 = (ADDRESS **) p2;
-
- return (*pq1)->q_qgrp - (*pq2)->q_qgrp;
-}
-
-static int
-e_filesys_compare(p1, p2)
- const void *p1;
- const void *p2;
-{
- ENVELOPE **pe1 = (ENVELOPE **) p1;
- ENVELOPE **pe2 = (ENVELOPE **) p2;
- int fs1, fs2;
-
- fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx;
- fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx;
- if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2))
- return -1;
- if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2))
- return 1;
- return 0;
-}
-
-static int split_across_queue_groups __P((ENVELOPE *));
-static int
-split_across_queue_groups(e)
- ENVELOPE *e;
-{
- int naddrs, nsplits, i;
- bool changed;
- char **pvp;
- ADDRESS *q, **addrs;
- ENVELOPE *ee, *es;
- ENVELOPE *splits[MAXQUEUEGROUPS];
- char pvpbuf[PSBUFSIZE];
-
- SM_REQUIRE(ISVALIDQGRP(e->e_qgrp));
-
- /* Count addresses and assign queue groups. */
- naddrs = 0;
- changed = false;
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_DEAD(q->q_state))
- continue;
- ++naddrs;
-
- /* bad addresses and those already sent stay put */
- if (QS_IS_BADADDR(q->q_state) ||
- QS_IS_SENT(q->q_state))
- q->q_qgrp = e->e_qgrp;
- else if (!ISVALIDQGRP(q->q_qgrp))
- {
- /* call ruleset which should return a queue group */
- i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp,
- pvpbuf, sizeof(pvpbuf));
- if (i == EX_OK &&
- pvp != NULL && pvp[0] != NULL &&
- (pvp[0][0] & 0377) == CANONNET &&
- pvp[1] != NULL && pvp[1][0] != '\0')
- {
- i = name2qid(pvp[1]);
- if (ISVALIDQGRP(i))
- {
- q->q_qgrp = i;
- changed = true;
- if (tTd(20, 4))
- sm_syslog(LOG_INFO, NOQID,
- "queue group name %s -> %d",
- pvp[1], i);
- continue;
- }
- else if (LogLevel > 10)
- sm_syslog(LOG_INFO, NOQID,
- "can't find queue group name %s, selection ignored",
- pvp[1]);
- }
- if (q->q_mailer != NULL &&
- ISVALIDQGRP(q->q_mailer->m_qgrp))
- {
- changed = true;
- q->q_qgrp = q->q_mailer->m_qgrp;
- }
- else if (ISVALIDQGRP(e->e_qgrp))
- q->q_qgrp = e->e_qgrp;
- else
- q->q_qgrp = 0;
- }
- }
-
- /* only one address? nothing to split. */
- if (naddrs <= 1 && !changed)
- return SM_SPLIT_NONE;
-
- /* sort the addresses by queue group */
- addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *));
- for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_DEAD(q->q_state))
- continue;
- addrs[i++] = q;
- }
- qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare);
-
- /* split into multiple envelopes, by queue group */
- nsplits = 0;
- es = NULL;
- e->e_sendqueue = NULL;
- for (i = 0; i < naddrs; ++i)
- {
- if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp)
- addrs[i]->q_next = NULL;
- else
- addrs[i]->q_next = addrs[i + 1];
-
- /* same queue group as original envelope? */
- if (addrs[i]->q_qgrp == e->e_qgrp)
- {
- if (e->e_sendqueue == NULL)
- e->e_sendqueue = addrs[i];
- continue;
- }
-
- /* different queue group than original envelope */
- if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp)
- {
- ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR);
- es = ee;
- splits[nsplits++] = ee;
- }
- }
-
- /* no splits? return right now. */
- if (nsplits <= 0)
- return SM_SPLIT_NONE;
-
- /* assign a queue directory to each additional envelope */
- for (i = 0; i < nsplits; ++i)
- {
- es = splits[i];
-#if 0
- es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es);
-#endif /* 0 */
- if (!setnewqueue(es))
- goto failure;
- }
-
- /* sort the additional envelopes by queue file system */
- qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare);
-
- /* create data files for each additional envelope */
- if (!dup_df(e, splits[0]))
- {
- i = 0;
- goto failure;
- }
- for (i = 1; i < nsplits; ++i)
- {
- /* copy or link to the previous data file */
- if (!dup_df(splits[i - 1], splits[i]))
- goto failure;
- }
-
- /* success: prepend the new envelopes to the e->e_sibling list */
- for (i = 0; i < nsplits; ++i)
- {
- es = splits[i];
- es->e_sibling = e->e_sibling;
- e->e_sibling = es;
- }
- return SM_SPLIT_NEW(nsplits);
-
- /* failure: clean up */
- failure:
- if (i > 0)
- {
- int j;
-
- for (j = 0; j < i; j++)
- (void) unlink(queuename(splits[j], DATAFL_LETTER));
- }
- e->e_sendqueue = addrs[0];
- for (i = 0; i < naddrs - 1; ++i)
- addrs[i]->q_next = addrs[i + 1];
- addrs[naddrs - 1]->q_next = NULL;
- return SM_SPLIT_FAIL;
-}
-
-/*
-** SPLIT_WITHIN_QUEUE
-**
-** Split an envelope with multiple recipients into several
-** envelopes within the same queue directory, if the number of
-** recipients exceeds the limit for the queue group.
-**
-** Parameters:
-** e -- envelope.
-**
-** Results:
-** SM_SPLIT_FAIL on failure
-** SM_SPLIT_NONE if no splitting occurred,
-** or 1 + the number of additional envelopes created.
-*/
-
-#define SPLIT_LOG_LEVEL 8
-
-static int split_within_queue __P((ENVELOPE *));
-
-static int
-split_within_queue(e)
- ENVELOPE *e;
-{
- int maxrcpt, nrcpt, ndead, nsplit, i;
- int j, l;
- char *lsplits;
- ADDRESS *q, **addrs;
- ENVELOPE *ee, *firstsibling;
-
- if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags))
- return SM_SPLIT_NONE;
-
- /* don't bother if there is no recipient limit */
- maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt;
- if (maxrcpt <= 0)
- return SM_SPLIT_NONE;
-
- /* count recipients */
- nrcpt = 0;
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_DEAD(q->q_state))
- continue;
- ++nrcpt;
- }
- if (nrcpt <= maxrcpt)
- return SM_SPLIT_NONE;
-
- /*
- ** Preserve the recipient list
- ** so that we can restore it in case of error.
- ** (But we discard dead addresses.)
- */
-
- addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *));
- for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_DEAD(q->q_state))
- continue;
- addrs[i++] = q;
- }
-
- /*
- ** Partition the recipient list so that bad and sent addresses
- ** come first. These will go with the original envelope, and
- ** do not count towards the maxrcpt limit.
- ** addrs[] does not contain QS_IS_DEAD() addresses.
- */
-
- ndead = 0;
- for (i = 0; i < nrcpt; ++i)
- {
- if (QS_IS_BADADDR(addrs[i]->q_state) ||
- QS_IS_SENT(addrs[i]->q_state) ||
- QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */
- {
- if (i > ndead)
- {
- ADDRESS *tmp = addrs[i];
-
- addrs[i] = addrs[ndead];
- addrs[ndead] = tmp;
- }
- ++ndead;
- }
- }
-
- /* Check if no splitting required. */
- if (nrcpt - ndead <= maxrcpt)
- return SM_SPLIT_NONE;
-
- /* fix links */
- for (i = 0; i < nrcpt - 1; ++i)
- addrs[i]->q_next = addrs[i + 1];
- addrs[nrcpt - 1]->q_next = NULL;
- e->e_sendqueue = addrs[0];
-
- /* prepare buffer for logging */
- if (LogLevel > SPLIT_LOG_LEVEL)
- {
- l = MAXLINE;
- lsplits = sm_malloc(l);
- if (lsplits != NULL)
- *lsplits = '\0';
- j = 0;
- }
- else
- {
- /* get rid of stupid compiler warnings */
- lsplits = NULL;
- j = l = 0;
- }
-
- /* split the envelope */
- firstsibling = e->e_sibling;
- i = maxrcpt + ndead;
- nsplit = 0;
- for (;;)
- {
- addrs[i - 1]->q_next = NULL;
- ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir);
- if (!dup_df(e, ee))
- {
-
- ee = firstsibling;
- while (ee != NULL)
- {
- (void) unlink(queuename(ee, DATAFL_LETTER));
- ee = ee->e_sibling;
- }
-
- /* Error. Restore e's sibling & recipient lists. */
- e->e_sibling = firstsibling;
- for (i = 0; i < nrcpt - 1; ++i)
- addrs[i]->q_next = addrs[i + 1];
- if (lsplits != NULL)
- sm_free(lsplits);
- return SM_SPLIT_FAIL;
- }
-
- /* prepend the new envelope to e->e_sibling */
- ee->e_sibling = e->e_sibling;
- e->e_sibling = ee;
- ++nsplit;
- if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
- {
- if (j >= l - strlen(ee->e_id) - 3)
- {
- char *p;
-
- l += MAXLINE;
- p = sm_realloc(lsplits, l);
- if (p == NULL)
- {
- /* let's try to get this done */
- sm_free(lsplits);
- lsplits = NULL;
- }
- else
- lsplits = p;
- }
- if (lsplits != NULL)
- {
- if (j == 0)
- j += sm_strlcat(lsplits + j,
- ee->e_id,
- l - j);
- else
- j += sm_strlcat2(lsplits + j,
- "; ",
- ee->e_id,
- l - j);
- SM_ASSERT(j < l);
- }
- }
- if (nrcpt - i <= maxrcpt)
- break;
- i += maxrcpt;
- }
- if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
- {
- if (nsplit > 0)
- {
- sm_syslog(LOG_NOTICE, e->e_id,
- "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s",
- maxrcpt, nrcpt - ndead, nsplit,
- nsplit > 1 ? "s" : "", lsplits);
- }
- sm_free(lsplits);
- }
- return SM_SPLIT_NEW(nsplit);
-}
-/*
-** SPLIT_BY_RECIPIENT
-**
-** Split an envelope with multiple recipients into multiple
-** envelopes as required by the sendmail configuration.
-**
-** Parameters:
-** e -- envelope.
-**
-** Results:
-** Returns true on success, false on failure.
-**
-** Side Effects:
-** see split_across_queue_groups(), split_within_queue(e)
-*/
-
-bool
-split_by_recipient(e)
- ENVELOPE *e;
-{
- int split, n, i, j, l;
- char *lsplits;
- ENVELOPE *ee, *next, *firstsibling;
-
- if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) ||
- bitset(EF_SPLIT, e->e_flags))
- return true;
- n = split_across_queue_groups(e);
- if (n == SM_SPLIT_FAIL)
- return false;
- firstsibling = ee = e->e_sibling;
- if (n > 1 && LogLevel > SPLIT_LOG_LEVEL)
- {
- l = MAXLINE;
- lsplits = sm_malloc(l);
- if (lsplits != NULL)
- *lsplits = '\0';
- j = 0;
- }
- else
- {
- /* get rid of stupid compiler warnings */
- lsplits = NULL;
- j = l = 0;
- }
- for (i = 1; i < n; ++i)
- {
- next = ee->e_sibling;
- if (split_within_queue(ee) == SM_SPLIT_FAIL)
- {
- e->e_sibling = firstsibling;
- return false;
- }
- ee->e_flags |= EF_SPLIT;
- if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
- {
- if (j >= l - strlen(ee->e_id) - 3)
- {
- char *p;
-
- l += MAXLINE;
- p = sm_realloc(lsplits, l);
- if (p == NULL)
- {
- /* let's try to get this done */
- sm_free(lsplits);
- lsplits = NULL;
- }
- else
- lsplits = p;
- }
- if (lsplits != NULL)
- {
- if (j == 0)
- j += sm_strlcat(lsplits + j,
- ee->e_id, l - j);
- else
- j += sm_strlcat2(lsplits + j, "; ",
- ee->e_id, l - j);
- SM_ASSERT(j < l);
- }
- }
- ee = next;
- }
- if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1)
- {
- sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s",
- n - 1, n > 2 ? "s" : "", lsplits);
- sm_free(lsplits);
- }
- split = split_within_queue(e) != SM_SPLIT_FAIL;
- if (split)
- e->e_flags |= EF_SPLIT;
- return split;
-}
-
-/*
-** QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope
-**
-** Add/remove quarantine reason and requeue appropriately.
-**
-** Parameters:
-** qgrp -- queue group for the item
-** qdir -- queue directory in the given queue group
-** e -- envelope information for the item
-** reason -- quarantine reason, NULL means unquarantine.
-**
-** Results:
-** true if item changed, false otherwise
-**
-** Side Effects:
-** Changes quarantine tag in queue file and renames it.
-*/
-
-static bool
-quarantine_queue_item(qgrp, qdir, e, reason)
- int qgrp;
- int qdir;
- ENVELOPE *e;
- char *reason;
-{
- bool dirty = false;
- bool failing = false;
- bool foundq = false;
- bool finished = false;
- int fd;
- int flags;
- int oldtype;
- int newtype;
- int save_errno;
- MODE_T oldumask = 0;
- SM_FILE_T *oldqfp, *tempqfp;
- char *bp;
- int bufsize;
- char oldqf[MAXPATHLEN];
- char tempqf[MAXPATHLEN];
- char newqf[MAXPATHLEN];
- char buf[MAXLINE];
-
- oldtype = queue_letter(e, ANYQFL_LETTER);
- (void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof(oldqf));
- (void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof(tempqf));
-
- /*
- ** Instead of duplicating all the open
- ** and lock code here, tell readqf() to
- ** do that work and return the open
- ** file pointer in e_lockfp. Note that
- ** we must release the locks properly when
- ** we are done.
- */
-
- if (!readqf(e, true))
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Skipping %s\n", qid_printname(e));
- return false;
- }
- oldqfp = e->e_lockfp;
-
- /* open the new queue file */
- flags = O_CREAT|O_WRONLY|O_EXCL;
- if (bitset(S_IWGRP, QueueFileMode))
- oldumask = umask(002);
- fd = open(tempqf, flags, QueueFileMode);
- if (bitset(S_IWGRP, QueueFileMode))
- (void) umask(oldumask);
- RELEASE_QUEUE;
-
- if (fd < 0)
- {
- save_errno = errno;
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Skipping %s: Could not open %s: %s\n",
- qid_printname(e), tempqf,
- sm_errstring(save_errno));
- (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
- return false;
- }
- if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB))
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Skipping %s: Could not lock %s\n",
- qid_printname(e), tempqf);
- (void) close(fd);
- (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
- return false;
- }
-
- tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd,
- SM_IO_WRONLY_B, NULL);
- if (tempqfp == NULL)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Skipping %s: Could not lock %s\n",
- qid_printname(e), tempqf);
- (void) close(fd);
- (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
- return false;
- }
-
- /* Copy the data over, changing the quarantine reason */
- while (bufsize = sizeof(buf),
- (bp = fgetfolded(buf, &bufsize, oldqfp)) != NULL)
- {
- if (tTd(40, 4))
- sm_dprintf("+++++ %s\n", bp);
- switch (bp[0])
- {
- case 'q': /* quarantine reason */
- foundq = true;
- if (reason == NULL)
- {
- if (Verbose)
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "%s: Removed quarantine of \"%s\"\n",
- e->e_id, &bp[1]);
- }
- sm_syslog(LOG_INFO, e->e_id, "unquarantine");
- dirty = true;
- }
- else if (strcmp(reason, &bp[1]) == 0)
- {
- if (Verbose)
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "%s: Already quarantined with \"%s\"\n",
- e->e_id, reason);
- }
- (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
- "q%s\n", reason);
- }
- else
- {
- if (Verbose)
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "%s: Quarantine changed from \"%s\" to \"%s\"\n",
- e->e_id, &bp[1],
- reason);
- }
- (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
- "q%s\n", reason);
- sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
- reason);
- dirty = true;
- }
- break;
-
- case 'S':
- /*
- ** If we are quarantining an unquarantined item,
- ** need to put in a new 'q' line before it's
- ** too late.
- */
-
- if (!foundq && reason != NULL)
- {
- if (Verbose)
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "%s: Quarantined with \"%s\"\n",
- e->e_id, reason);
- }
- (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
- "q%s\n", reason);
- sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
- reason);
- foundq = true;
- dirty = true;
- }
-
- /* Copy the line to the new file */
- (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
- "%s\n", bp);
- break;
-
- case '.':
- finished = true;
- /* FALLTHROUGH */
-
- default:
- /* Copy the line to the new file */
- (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
- "%s\n", bp);
- break;
- }
- if (bp != buf)
- sm_free(bp);
- }
-
- /* Make sure we read the whole old file */
- errno = sm_io_error(tempqfp);
- if (errno != 0 && errno != SM_IO_EOF)
- {
- save_errno = errno;
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Skipping %s: Error reading %s: %s\n",
- qid_printname(e), oldqf,
- sm_errstring(save_errno));
- failing = true;
- }
-
- if (!failing && !finished)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Skipping %s: Incomplete file: %s\n",
- qid_printname(e), oldqf);
- failing = true;
- }
-
- /* Check if we actually changed anything or we can just bail now */
- if (!dirty)
- {
- /* pretend we failed, even though we technically didn't */
- failing = true;
- }
-
- /* Make sure we wrote things out safely */
- if (!failing &&
- (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 ||
- ((SuperSafe == SAFE_REALLY ||
- SuperSafe == SAFE_REALLY_POSTMILTER ||
- SuperSafe == SAFE_INTERACTIVE) &&
- fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) ||
- ((errno = sm_io_error(tempqfp)) != 0)))
- {
- save_errno = errno;
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Skipping %s: Error writing %s: %s\n",
- qid_printname(e), tempqf,
- sm_errstring(save_errno));
- failing = true;
- }
-
-
- /* Figure out the new filename */
- newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER);
- if (oldtype == newtype)
- {
- /* going to rename tempqf to oldqf */
- (void) sm_strlcpy(newqf, oldqf, sizeof(newqf));
- }
- else
- {
- /* going to rename tempqf to new name based on newtype */
- (void) sm_strlcpy(newqf, queuename(e, newtype), sizeof(newqf));
- }
-
- save_errno = 0;
-
- /* rename tempqf to newqf */
- if (!failing &&
- rename(tempqf, newqf) < 0)
- save_errno = (errno == 0) ? EINVAL : errno;
-
- /* Check rename() success */
- if (!failing && save_errno != 0)
- {
- sm_syslog(LOG_DEBUG, e->e_id,
- "quarantine_queue_item: rename(%s, %s): %s",
- tempqf, newqf, sm_errstring(save_errno));
-
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Error renaming %s to %s: %s\n",
- tempqf, newqf,
- sm_errstring(save_errno));
- if (oldtype == newtype)
- {
- /*
- ** Bail here since we don't know the state of
- ** the filesystem and may need to keep tempqf
- ** for the user to rescue us.
- */
-
- RELEASE_QUEUE;
- errno = save_errno;
- syserr("!452 Error renaming control file %s", tempqf);
- /* NOTREACHED */
- }
- else
- {
- /* remove new file (if rename() half completed) */
- if (xunlink(newqf) < 0)
- {
- save_errno = errno;
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Error removing %s: %s\n",
- newqf,
- sm_errstring(save_errno));
- }
-
- /* tempqf removed below */
- failing = true;
- }
-
- }
-
- /* If changing file types, need to remove old type */
- if (!failing && oldtype != newtype)
- {
- if (xunlink(oldqf) < 0)
- {
- save_errno = errno;
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Error removing %s: %s\n",
- oldqf, sm_errstring(save_errno));
- }
- }
-
- /* see if anything above failed */
- if (failing)
- {
- /* Something failed: remove new file, old file still there */
- (void) xunlink(tempqf);
- }
-
- /*
- ** fsync() after file operations to make sure metadata is
- ** written to disk on filesystems in which renames are
- ** not guaranteed. It's ok if they fail, mail won't be lost.
- */
-
- if (SuperSafe != SAFE_NO)
- {
- /* for soft-updates */
- (void) fsync(sm_io_getinfo(tempqfp,
- SM_IO_WHAT_FD, NULL));
-
- if (!failing)
- {
- /* for soft-updates */
- (void) fsync(sm_io_getinfo(oldqfp,
- SM_IO_WHAT_FD, NULL));
- }
-
- /* for other odd filesystems */
- SYNC_DIR(tempqf, false);
- }
-
- /* Close up shop */
- RELEASE_QUEUE;
- if (tempqfp != NULL)
- (void) sm_io_close(tempqfp, SM_TIME_DEFAULT);
- if (oldqfp != NULL)
- (void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
-
- /* All went well */
- return !failing;
-}
-
-/*
-** QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue
-**
-** Read all matching queue items, add/remove quarantine
-** reason, and requeue appropriately.
-**
-** Parameters:
-** reason -- quarantine reason, "." means unquarantine.
-** qgrplimit -- limit to single queue group unless NOQGRP
-**
-** Results:
-** none.
-**
-** Side Effects:
-** Lots of changes to the queue.
-*/
-
-void
-quarantine_queue(reason, qgrplimit)
- char *reason;
- int qgrplimit;
-{
- int changed = 0;
- int qgrp;
-
- /* Convert internal representation of unquarantine */
- if (reason != NULL && reason[0] == '.' && reason[1] == '\0')
- reason = NULL;
-
- if (reason != NULL)
- {
- /* clean it */
- reason = newstr(denlstring(reason, true, true));
- }
-
- for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
- {
- int qdir;
-
- if (qgrplimit != NOQGRP && qgrplimit != qgrp)
- continue;
-
- for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++)
- {
- int i;
- int nrequests;
-
- if (StopRequest)
- stop_sendmail();
-
- nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
-
- /* first see if there is anything */
- if (nrequests <= 0)
- {
- if (Verbose)
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT, "%s: no matches\n",
- qid_printqueue(qgrp, qdir));
- }
- continue;
- }
-
- if (Verbose)
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT, "Processing %s:\n",
- qid_printqueue(qgrp, qdir));
- }
-
- for (i = 0; i < WorkListCount; i++)
- {
- ENVELOPE e;
-
- if (StopRequest)
- stop_sendmail();
-
- /* setup envelope */
- clearenvelope(&e, true, sm_rpool_new_x(NULL));
- e.e_id = WorkList[i].w_name + 2;
- e.e_qgrp = qgrp;
- e.e_qdir = qdir;
-
- if (tTd(70, 101))
- {
- sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Would do %s\n", e.e_id);
- changed++;
- }
- else if (quarantine_queue_item(qgrp, qdir,
- &e, reason))
- changed++;
-
- /* clean up */
- sm_rpool_free(e.e_rpool);
- e.e_rpool = NULL;
- }
- if (WorkList != NULL)
- sm_free(WorkList); /* XXX */
- WorkList = NULL;
- WorkListSize = 0;
- WorkListCount = 0;
- }
- }
- if (Verbose)
- {
- if (changed == 0)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "No changes\n");
- else
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "%d change%s\n",
- changed,
- changed == 1 ? "" : "s");
- }
-}
diff --git a/contrib/sendmail/src/ratectrl.c b/contrib/sendmail/src/ratectrl.c
deleted file mode 100644
index 22f9803..0000000
--- a/contrib/sendmail/src/ratectrl.c
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * Copyright (c) 2003 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 Jose Marcio Martins da Cruz - Ecole des Mines de Paris
- * Jose-Marcio.Martins@ensmp.fr
- */
-
-/* a part of this code is based on inetd.c for which this copyright applies: */
-/*
- * Copyright (c) 1983, 1991, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sendmail.h>
-SM_RCSID("@(#)$Id: ratectrl.c,v 8.11 2006/08/15 23:24:57 ca Exp $")
-
-/*
-** stuff included - given some warnings (inet_ntoa)
-** - surely not everything is needed
-*/
-
-#if NETINET || NETINET6
-# include <arpa/inet.h>
-#endif /* NETINET || NETINET6 */
-
-#include <sm/time.h>
-
-#ifndef HASH_ALG
-# define HASH_ALG 2
-#endif /* HASH_ALG */
-
-#ifndef RATECTL_DEBUG
-# define RATECTL_DEBUG 0
-#endif /* RATECTL_DEBUG */
-
-/* forward declarations */
-static int client_rate __P((time_t, SOCKADDR *, bool));
-static int total_rate __P((time_t, bool));
-#if 0
-static int sockaddrcmp __P((SOCKADDR *, SOCKADDR *));
-#endif /* 0 */
-
-/*
-** CONNECTION_RATE_CHECK - updates connection history data
-** and computes connection rate for the given host
-**
-** Parameters:
-** hostaddr -- ip address of smtp client
-** e -- envelope
-**
-** Returns:
-** true (always)
-**
-** Side Effects:
-** updates connection history
-**
-** Warnings:
-** For each connection, this call shall be
-** done only once with the value true for the
-** update parameter.
-** Typically, this call is done with the value
-** true by the father, and once again with
-** the value false by the children.
-**
-*/
-
-bool
-connection_rate_check(hostaddr, e)
- SOCKADDR *hostaddr;
- ENVELOPE *e;
-{
- time_t now;
- int totalrate, clientrate;
- static int clientconn = 0;
-
- now = time(NULL);
-#if RATECTL_DEBUG
- sm_syslog(LOG_INFO, NOQID, "connection_rate_check entering...");
-#endif /* RATECTL_DEBUG */
-
- /* update server connection rate */
- totalrate = total_rate(now, e == NULL);
-#if RATECTL_DEBUG
- sm_syslog(LOG_INFO, NOQID, "global connection rate: %d", globalRate);
-#endif /* RATECTL_DEBUG */
-
- /* update client connection rate */
- clientrate = client_rate(now, hostaddr, e == NULL);
-
- if (e == NULL)
- clientconn = count_open_connections(hostaddr);
-
- if (e != NULL)
- {
- char s[16];
-
- sm_snprintf(s, sizeof(s), "%d", clientrate);
- macdefine(&e->e_macro, A_TEMP, macid("{client_rate}"), s);
- sm_snprintf(s, sizeof(s), "%d", totalrate);
- macdefine(&e->e_macro, A_TEMP, macid("{total_rate}"), s);
- sm_snprintf(s, sizeof(s), "%d", clientconn);
- macdefine(&e->e_macro, A_TEMP, macid("{client_connections}"),
- s);
- }
- return true;
-}
-
-/*
-** Data declarations needed to evaluate connection rate
-*/
-
-static int CollTime = 60;
-
-/* this should be a power of 2, otherwise CPMHMASK doesn't work well */
-#ifndef CPMHSIZE
-# define CPMHSIZE 1024
-#endif /* CPMHSIZE */
-
-#define CPMHMASK (CPMHSIZE-1)
-
-#ifndef MAX_CT_STEPS
-# define MAX_CT_STEPS 10
-#endif /* MAX_CT_STEPS */
-
-/*
-** time granularity: 10s (that's one "tick")
-** will be initialised to ConnectionRateWindowSize/CHTSIZE
-** before being used the first time
-*/
-
-static int ChtGran = -1;
-
-#define CHTSIZE 6
-
-/* Number of connections for a certain "tick" */
-typedef struct CTime
-{
- unsigned long ct_Ticks;
- int ct_Count;
-}
-CTime_T;
-
-typedef struct CHash
-{
-#if NETINET6 && NETINET
- union
- {
- struct in_addr c4_Addr;
- struct in6_addr c6_Addr;
- } cu_Addr;
-# define ch_Addr4 cu_Addr.c4_Addr
-# define ch_Addr6 cu_Addr.c6_Addr
-#else /* NETINET6 && NETINET */
-# if NETINET6
- struct in6_addr ch_Addr;
-# define ch_Addr6 ch_Addr
-# else /* NETINET6 */
- struct in_addr ch_Addr;
-# define ch_Addr4 ch_Addr
-# endif /* NETINET6 */
-#endif /* NETINET6 && NETINET */
-
- int ch_Family;
- time_t ch_LTime;
- unsigned long ch_colls;
-
- /* 6 buckets for ticks: 60s */
- CTime_T ch_Times[CHTSIZE];
-}
-CHash_T;
-
-static CHash_T CHashAry[CPMHSIZE];
-static bool CHashAryOK = false;
-
-/*
-** CLIENT_RATE - Evaluate connection rate per smtp client
-**
-** Parameters:
-** now - current time in secs
-** saddr - client address
-** update - update data / check only
-**
-** Returns:
-** connection rate (connections / ConnectionRateWindowSize)
-**
-** Side effects:
-** update static global data
-**
-*/
-
-static int
-client_rate(now, saddr, update)
- time_t now;
- SOCKADDR *saddr;
- bool update;
-{
- unsigned int hv;
- int i;
- int cnt;
- bool coll;
- CHash_T *chBest = NULL;
- unsigned int ticks;
-
- cnt = 0;
- hv = 0xABC3D20F;
- if (ChtGran < 0)
- ChtGran = ConnectionRateWindowSize / CHTSIZE;
- if (ChtGran <= 0)
- ChtGran = 10;
-
- ticks = now / ChtGran;
-
- if (!CHashAryOK)
- {
- memset(CHashAry, 0, sizeof(CHashAry));
- CHashAryOK = true;
- }
-
- {
- char *p;
- int addrlen;
-#if HASH_ALG != 1
- int c, d;
-#endif /* HASH_ALG != 1 */
-
- switch (saddr->sa.sa_family)
- {
-#if NETINET
- case AF_INET:
- p = (char *)&saddr->sin.sin_addr;
- addrlen = sizeof(struct in_addr);
- break;
-#endif /* NETINET */
-#if NETINET6
- case AF_INET6:
- p = (char *)&saddr->sin6.sin6_addr;
- addrlen = sizeof(struct in6_addr);
- break;
-#endif /* NETINET6 */
- default:
- /* should not happen */
- return -1;
- }
-
- /* compute hash value */
- for (i = 0; i < addrlen; ++i, ++p)
-#if HASH_ALG == 1
- hv = (hv << 5) ^ (hv >> 23) ^ *p;
- hv = (hv ^ (hv >> 16));
-#elif HASH_ALG == 2
- {
- d = *p;
- c = d;
- c ^= c<<6;
- hv += (c<<11) ^ (c>>1);
- hv ^= (d<<14) + (d<<7) + (d<<4) + d;
- }
-#elif HASH_ALG == 3
- {
- hv = (hv << 4) + *p;
- d = hv & 0xf0000000;
- if (d != 0)
- {
- hv ^= (d >> 24);
- hv ^= d;
- }
- }
-#else /* HASH_ALG == 1 */
- hv = ((hv << 1) ^ (*p & 0377)) % cctx->cc_size;
-#endif /* HASH_ALG == 1 */
- }
-
- coll = true;
- for (i = 0; i < MAX_CT_STEPS; ++i)
- {
- CHash_T *ch = &CHashAry[(hv + i) & CPMHMASK];
-
-#if NETINET
- if (saddr->sa.sa_family == AF_INET &&
- ch->ch_Family == AF_INET &&
- (saddr->sin.sin_addr.s_addr == ch->ch_Addr4.s_addr ||
- ch->ch_Addr4.s_addr == 0))
- {
- chBest = ch;
- coll = false;
- break;
- }
-#endif /* NETINET */
-#if NETINET6
- if (saddr->sa.sa_family == AF_INET6 &&
- ch->ch_Family == AF_INET6 &&
- (IN6_ARE_ADDR_EQUAL(&saddr->sin6.sin6_addr,
- &ch->ch_Addr6) != 0 ||
- IN6_IS_ADDR_UNSPECIFIED(&ch->ch_Addr6)))
- {
- chBest = ch;
- coll = false;
- break;
- }
-#endif /* NETINET6 */
- if (chBest == NULL || ch->ch_LTime == 0 ||
- ch->ch_LTime < chBest->ch_LTime)
- chBest = ch;
- }
-
- /* Let's update data... */
- if (update)
- {
- if (coll && (now - chBest->ch_LTime < CollTime))
- {
- /*
- ** increment the number of collisions last
- ** CollTime for this client
- */
-
- chBest->ch_colls++;
-
- /*
- ** Maybe shall log if collision rate is too high...
- ** and take measures to resize tables
- ** if this is the case
- */
- }
-
- /*
- ** If it's not a match, then replace the data.
- ** Note: this purges the history of a colliding entry,
- ** which may cause "overruns", i.e., if two entries are
- ** "cancelling" each other out, then they may exceed
- ** the limits that are set. This might be mitigated a bit
- ** by the above "best of 5" function however.
- **
- ** Alternative approach: just use the old data, which may
- ** cause false positives however.
- ** To activate this, change deactivate following memset call.
- */
-
- if (coll)
- {
-#if NETINET
- if (saddr->sa.sa_family == AF_INET)
- {
- chBest->ch_Family = AF_INET;
- chBest->ch_Addr4 = saddr->sin.sin_addr;
- }
-#endif /* NETINET */
-#if NETINET6
- if (saddr->sa.sa_family == AF_INET6)
- {
- chBest->ch_Family = AF_INET6;
- chBest->ch_Addr6 = saddr->sin6.sin6_addr;
- }
-#endif /* NETINET6 */
-#if 1
- memset(chBest->ch_Times, '\0',
- sizeof(chBest->ch_Times));
-#endif /* 1 */
- }
-
- chBest->ch_LTime = now;
- {
- CTime_T *ct = &chBest->ch_Times[ticks % CHTSIZE];
-
- if (ct->ct_Ticks != ticks)
- {
- ct->ct_Ticks = ticks;
- ct->ct_Count = 0;
- }
- ++ct->ct_Count;
- }
- }
-
- /* Now let's count connections on the window */
- for (i = 0; i < CHTSIZE; ++i)
- {
- CTime_T *ct = &chBest->ch_Times[i];
-
- if (ct->ct_Ticks <= ticks && ct->ct_Ticks >= ticks - CHTSIZE)
- cnt += ct->ct_Count;
- }
-
-#if RATECTL_DEBUG
- sm_syslog(LOG_WARNING, NOQID,
- "cln: cnt=(%d), CHTSIZE=(%d), ChtGran=(%d)",
- cnt, CHTSIZE, ChtGran);
-#endif /* RATECTL_DEBUG */
- return cnt;
-}
-
-/*
-** TOTAL_RATE - Evaluate global connection rate
-**
-** Parameters:
-** now - current time in secs
-** update - update data / check only
-**
-** Returns:
-** connection rate (connections / ConnectionRateWindowSize)
-*/
-
-static CTime_T srv_Times[CHTSIZE];
-static bool srv_Times_OK = false;
-
-static int
-total_rate(now, update)
- time_t now;
- bool update;
-{
- int i;
- int cnt = 0;
- CTime_T *ct;
- unsigned int ticks;
-
- if (ChtGran < 0)
- ChtGran = ConnectionRateWindowSize / CHTSIZE;
- if (ChtGran == 0)
- ChtGran = 10;
- ticks = now / ChtGran;
- if (!srv_Times_OK)
- {
- memset(srv_Times, 0, sizeof(srv_Times));
- srv_Times_OK = true;
- }
-
- /* Let's update data */
- if (update)
- {
- ct = &srv_Times[ticks % CHTSIZE];
-
- if (ct->ct_Ticks != ticks)
- {
- ct->ct_Ticks = ticks;
- ct->ct_Count = 0;
- }
- ++ct->ct_Count;
- }
-
- /* Let's count connections on the window */
- for (i = 0; i < CHTSIZE; ++i)
- {
- ct = &srv_Times[i];
-
- if (ct->ct_Ticks <= ticks && ct->ct_Ticks >= ticks - CHTSIZE)
- cnt += ct->ct_Count;
- }
-
-#if RATECTL_DEBUG
- sm_syslog(LOG_WARNING, NOQID,
- "srv: cnt=(%d), CHTSIZE=(%d), ChtGran=(%d)",
- cnt, CHTSIZE, ChtGran);
-#endif /* RATECTL_DEBUG */
-
- return cnt;
-}
-
-#if 0
-/*
-** SOCKADDRCMP - compare two SOCKADDR structures
-** this function may be used to compare SOCKADDR
-** structures when using bsearch and qsort functions
-** in the same way we do with strcmp
-**
-** Parameters:
-** a, b - addresses
-**
-** Returns:
-** 1 if a > b
-** -1 if a < b
-** 0 if a = b
-**
-** OBS: This call isn't used at the moment, it will
-** be used when code will be extended to work with IPV6
-*/
-
-static int
-sockaddrcmp(a, b)
- SOCKADDR *a;
- SOCKADDR *b;
-{
- if (a->sa.sa_family > b->sa.sa_family)
- return 1;
- if (a->sa.sa_family < b->sa.sa_family)
- return -1;
-
- switch (a->sa.sa_family)
- {
- case AF_INET:
- if (a->sin.sin_addr.s_addr > b->sin.sin_addr.s_addr)
- return 1;
- if (a->sin.sin_addr.s_addr < b->sin.sin_addr.s_addr)
- return -1;
- return 0;
- break;
-
- case AF_INET6:
- /* TO BE DONE */
- break;
- }
- return 0;
-}
-#endif /* 0 */
diff --git a/contrib/sendmail/src/readcf.c b/contrib/sendmail/src/readcf.c
deleted file mode 100644
index 0d0849b..0000000
--- a/contrib/sendmail/src/readcf.c
+++ /dev/null
@@ -1,4564 +0,0 @@
-/*
- * Copyright (c) 1998-2006 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.
- *
- */
-
-#include <sendmail.h>
-#include <sm/sendmail.h>
-
-SM_RCSID("@(#)$Id: readcf.c,v 8.664 2007/07/10 17:01:22 ca Exp $")
-
-#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, bool));
-static char **makeargv __P((char *));
-static void settimeout __P((char *, char *, bool));
-static void toomany __P((int, int));
-static char *extrquotstr __P((char *, char **, char *, bool *));
-static void parse_class_words __P((int, char *));
-
-/*
-** READCF -- read configuration file.
-**
-** This routine reads the configuration file and builds the internal
-** form.
-**
-** The file is formatted as a sequence of lines, each taken
-** atomically. The first character of each line describes how
-** the line is to be interpreted. The lines are:
-** Dxval Define macro x to have value val.
-** Cxword Put word into class x.
-** Fxfile [fmt] Read file for lines to put into
-** class x. Use scanf string 'fmt'
-** or "%s" if not present. Fmt should
-** only produce one string-valued result.
-** Hname: value Define header with field-name 'name'
-** and value as specified; this will be
-** macro expanded immediately before
-** use.
-** Sn Use rewriting set n.
-** Rlhs rhs Rewrite addresses that match lhs to
-** be rhs.
-** Mn arg=val... Define mailer. n is the internal name.
-** Args specify mailer parameters.
-** Oxvalue Set option x to value.
-** O option value Set option (long name) to value.
-** Pname=value Set precedence name to value.
-** Qn arg=val... Define queue groups. n is the internal name.
-** Args specify queue parameters.
-** Vversioncode[/vendorcode]
-** Version level/vendor name of
-** configuration syntax.
-** Kmapname mapclass arguments....
-** Define keyed lookup of a given class.
-** Arguments are class dependent.
-** Eenvar=value Set the environment value to the given value.
-**
-** Parameters:
-** cfname -- configuration file name.
-** safe -- true if this is the system config file;
-** false otherwise.
-** e -- the main envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Builds several internal tables.
-*/
-
-void
-readcf(cfname, safe, e)
- char *cfname;
- bool safe;
- register ENVELOPE *e;
-{
- SM_FILE_T *cf;
- int ruleset = -1;
- char *q;
- struct rewrite *rwp = NULL;
- char *bp;
- auto char *ep;
- int nfuzzy;
- char *file;
- bool optional;
- bool ok;
- bool ismap;
- int mid;
- register char *p;
- long sff = SFF_OPENASROOT;
- struct stat statb;
- char buf[MAXLINE];
- int bufsize;
- char exbuf[MAXLINE];
- char pvpbuf[MAXLINE + MAXATOM];
- static char *null_list[1] = { NULL };
- extern unsigned char TokTypeNoC[];
-
- FileName = cfname;
- LineNumber = 0;
-
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
- cf = safefopen(cfname, O_RDONLY, 0444, sff);
- if (cf == NULL)
- {
- syserr("cannot open");
- finis(false, true, EX_OSFILE);
- }
-
- if (fstat(sm_io_getinfo(cf, SM_IO_WHAT_FD, NULL), &statb) < 0)
- {
- syserr("cannot fstat");
- finis(false, true, EX_OSFILE);
- }
-
- if (!S_ISREG(statb.st_mode))
- {
- syserr("not a plain file");
- finis(false, true, EX_OSFILE);
- }
-
- if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
- {
- if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
- (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
- "%s: WARNING: dangerous write permissions\n",
- FileName);
- if (LogLevel > 0)
- sm_syslog(LOG_CRIT, NOQID,
- "%s: WARNING: dangerous write permissions",
- FileName);
- }
-
-#if XLA
- xla_zero();
-#endif /* XLA */
-
- while (bufsize = sizeof(buf),
- (bp = fgetfolded(buf, &bufsize, cf)) != NULL)
- {
- char *nbp;
-
- if (bp[0] == '#')
- {
- if (bp != buf)
- sm_free(bp); /* XXX */
- continue;
- }
-
- /* do macro expansion mappings */
- nbp = translate_dollars(bp, bp, &bufsize);
- if (nbp != bp && bp != buf)
- sm_free(bp);
- bp = nbp;
-
- /* interpret this line */
- errno = 0;
- switch (bp[0])
- {
- case '\0':
- case '#': /* comment */
- 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;
-
- if (*p == '\0')
- {
- syserr("invalid rewrite line \"%s\" (tab expected)", bp);
- break;
- }
-
- /* allocate space for the rule header */
- if (rwp == NULL)
- {
- RewriteRules[ruleset] = rwp =
- (struct rewrite *) xalloc(sizeof(*rwp));
- }
- else
- {
- rwp->r_next = (struct rewrite *) xalloc(sizeof(*rwp));
- rwp = rwp->r_next;
- }
- rwp->r_next = NULL;
-
- /* expand and save the LHS */
- *p = '\0';
- expand(&bp[1], exbuf, sizeof(exbuf), e);
- rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
- sizeof(pvpbuf), NULL,
- ConfigLevel >= 9 ? TokTypeNoC : IntTokenTab,
- true);
- nfuzzy = 0;
- if (rwp->r_lhs != NULL)
- {
- register char **ap;
-
- rwp->r_lhs = copyplist(rwp->r_lhs, true, NULL);
-
- /* count the number of fuzzy matches in LHS */
- for (ap = rwp->r_lhs; *ap != NULL; ap++)
- {
- char *botch;
-
- botch = NULL;
- switch (ap[0][0] & 0377)
- {
- case MATCHZANY:
- case MATCHANY:
- case MATCHONE:
- case MATCHCLASS:
- case MATCHNCLASS:
- nfuzzy++;
- break;
-
- case MATCHREPL:
- botch = "$1-$9";
- break;
-
- case CANONUSER:
- botch = "$:";
- break;
-
- case CALLSUBR:
- botch = "$>";
- break;
-
- case CONDIF:
- botch = "$?";
- break;
-
- case CONDFI:
- botch = "$.";
- break;
-
- case HOSTBEGIN:
- botch = "$[";
- break;
-
- case HOSTEND:
- botch = "$]";
- break;
-
- case LOOKUPBEGIN:
- botch = "$(";
- break;
-
- case LOOKUPEND:
- botch = "$)";
- break;
- }
- if (botch != NULL)
- syserr("Inappropriate use of %s on LHS",
- botch);
- }
- rwp->r_line = LineNumber;
- }
- else
- {
- syserr("R line: null LHS");
- rwp->r_lhs = null_list;
- }
- if (nfuzzy > MAXMATCH)
- {
- syserr("R line: too many wildcards");
- rwp->r_lhs = null_list;
- }
-
- /* expand and save the RHS */
- while (*++p == '\t')
- continue;
- q = p;
- while (*p != '\0' && *p != '\t')
- p++;
- *p = '\0';
- expand(q, exbuf, sizeof(exbuf), e);
- rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
- sizeof(pvpbuf), NULL,
- ConfigLevel >= 9 ? TokTypeNoC : IntTokenTab,
- true);
- if (rwp->r_rhs != NULL)
- {
- register char **ap;
- int args, endtoken;
-#if _FFR_EXTRA_MAP_CHECK
- int nexttoken;
-#endif /* _FFR_EXTRA_MAP_CHECK */
- bool inmap;
-
- rwp->r_rhs = copyplist(rwp->r_rhs, true, NULL);
-
- /* check no out-of-bounds replacements */
- nfuzzy += '0';
- inmap = false;
- args = 0;
- endtoken = 0;
- for (ap = rwp->r_rhs; *ap != NULL; ap++)
- {
- char *botch;
-
- botch = NULL;
- switch (ap[0][0] & 0377)
- {
- case MATCHREPL:
- if (ap[0][1] <= '0' ||
- ap[0][1] > nfuzzy)
- {
- syserr("replacement $%c out of bounds",
- ap[0][1]);
- }
- break;
-
- case MATCHZANY:
- botch = "$*";
- break;
-
- case MATCHANY:
- botch = "$+";
- break;
-
- case MATCHONE:
- botch = "$-";
- break;
-
- case MATCHCLASS:
- botch = "$=";
- break;
-
- case MATCHNCLASS:
- botch = "$~";
- break;
-
- case CANONHOST:
- if (!inmap)
- break;
- if (++args >= MAX_MAP_ARGS)
- syserr("too many arguments for map lookup");
- break;
-
- case HOSTBEGIN:
- endtoken = HOSTEND;
- /* FALLTHROUGH */
- case LOOKUPBEGIN:
- /* see above... */
- if ((ap[0][0] & 0377) == LOOKUPBEGIN)
- endtoken = LOOKUPEND;
- if (inmap)
- syserr("cannot nest map lookups");
- inmap = true;
- args = 0;
-#if _FFR_EXTRA_MAP_CHECK
- if (ap[1] == NULL)
- {
- syserr("syntax error in map lookup");
- break;
- }
- nexttoken = ap[1][0] & 0377;
- if (nexttoken == CANONHOST ||
- nexttoken == CANONUSER ||
- nexttoken == endtoken))
- {
- syserr("missing map name for lookup");
- break;
- }
- if (ap[2] == NULL)
- {
- syserr("syntax error in map lookup");
- break;
- }
- if (ap[0][0] == HOSTBEGIN)
- break;
- nexttoken = ap[2][0] & 0377;
- if (nexttoken == CANONHOST ||
- nexttoken == CANONUSER ||
- nexttoken == endtoken)
- {
- syserr("missing key name for lookup");
- break;
- }
-#endif /* _FFR_EXTRA_MAP_CHECK */
- break;
-
- case HOSTEND:
- case LOOKUPEND:
- if ((ap[0][0] & 0377) != endtoken)
- break;
- inmap = false;
- endtoken = 0;
- break;
-
-
-#if 0
-/*
-** This doesn't work yet as there are maps defined *after* the cf
-** is read such as host, user, and alias. So for now, it's removed.
-** When it comes back, the RELEASE_NOTES entry will be:
-** Emit warnings for unknown maps when reading the .cf file. Based on
-** patch from Robert Harker of Harker Systems.
-*/
-
- case LOOKUPBEGIN:
- /*
- ** Got a database lookup,
- ** check if map is defined.
- */
-
- ep = ap[1];
- if ((ep[0] & 0377) != MACRODEXPAND &&
- stab(ep, ST_MAP, ST_FIND) == NULL)
- {
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "Warning: %s: line %d: map %s not found\n",
- FileName,
- LineNumber,
- ep);
- }
- break;
-#endif /* 0 */
- }
- if (botch != NULL)
- syserr("Inappropriate use of %s on RHS",
- botch);
- }
- if (inmap)
- syserr("missing map closing token");
- }
- else
- {
- syserr("R line: null RHS");
- rwp->r_rhs = null_list;
- }
- break;
-
- case 'S': /* select rewriting set */
- expand(&bp[1], exbuf, sizeof(exbuf), e);
- ruleset = strtorwset(exbuf, NULL, ST_ENTER);
- if (ruleset < 0)
- break;
-
- rwp = RewriteRules[ruleset];
- if (rwp != NULL)
- {
- if (OpMode == MD_TEST)
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "WARNING: Ruleset %s has multiple definitions\n",
- &bp[1]);
- if (tTd(37, 1))
- sm_dprintf("WARNING: Ruleset %s has multiple definitions\n",
- &bp[1]);
- while (rwp->r_next != NULL)
- rwp = rwp->r_next;
- }
- break;
-
- case 'D': /* macro definition */
- mid = macid_parse(&bp[1], &ep);
- if (mid == 0)
- break;
- p = munchstring(ep, NULL, '\0');
- macdefine(&e->e_macro, A_TEMP, mid, p);
- break;
-
- case 'H': /* required header line */
- (void) chompheader(&bp[1], CHHDR_DEF, NULL, e);
- break;
-
- case 'C': /* word class */
- case 'T': /* trusted user (set class `t') */
- if (bp[0] == 'C')
- {
- mid = macid_parse(&bp[1], &ep);
- if (mid == 0)
- break;
- expand(ep, exbuf, sizeof(exbuf), e);
- p = exbuf;
- }
- else
- {
- mid = 't';
- p = &bp[1];
- }
- while (*p != '\0')
- {
- register char *wd;
- char delim;
-
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
- wd = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- delim = *p;
- *p = '\0';
- if (wd[0] != '\0')
- setclass(mid, wd);
- *p = delim;
- }
- break;
-
- case 'F': /* word class from file */
- mid = macid_parse(&bp[1], &ep);
- if (mid == 0)
- break;
- for (p = ep; isascii(*p) && isspace(*p); )
- p++;
- if (p[0] == '-' && p[1] == 'o')
- {
- optional = true;
- while (*p != '\0' &&
- !(isascii(*p) && isspace(*p)))
- p++;
- while (isascii(*p) && isspace(*p))
- p++;
- file = p;
- }
- else
- optional = false;
-
- /* check if [key]@map:spec */
- ismap = false;
- if (!SM_IS_DIR_DELIM(*p) &&
- *p != '|' &&
- (q = strchr(p, '@')) != NULL)
- {
- q++;
-
- /* look for @LDAP or @map: in string */
- if (strcmp(q, "LDAP") == 0 ||
- (*q != ':' &&
- strchr(q, ':') != NULL))
- ismap = true;
- }
-
- if (ismap)
- {
- /* use entire spec */
- file = p;
- }
- else
- {
- file = extrquotstr(p, &q, " ", &ok);
- if (!ok)
- {
- syserr("illegal filename '%s'", p);
- break;
- }
- }
-
- if (*file == '|' || ismap)
- p = "%s";
- else
- {
- p = q;
- if (*p == '\0')
- p = "%s";
- else
- {
- *p = '\0';
- while (isascii(*++p) && isspace(*p))
- continue;
- }
- }
- fileclass(mid, file, p, ismap, safe, optional);
- break;
-
-#if XLA
- case 'L': /* extended load average description */
- xla_init(&bp[1]);
- break;
-#endif /* XLA */
-
-#if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)
- case 'L': /* lookup macro */
- case 'G': /* lookup class */
- /* reserved for Sun -- NIS+ database lookup */
- if (VendorCode != VENDOR_SUN)
- goto badline;
- sun_lg_config_line(bp, e);
- break;
-#endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */
-
- case 'M': /* define mailer */
- makemailer(&bp[1]);
- break;
-
- case 'O': /* set option */
- setoption(bp[1], &bp[2], safe, false, e);
- break;
-
- case 'P': /* set precedence */
- if (NumPriorities >= MAXPRIORITIES)
- {
- toomany('P', MAXPRIORITIES);
- break;
- }
- for (p = &bp[1]; *p != '\0' && *p != '='; p++)
- continue;
- if (*p == '\0')
- goto badline;
- *p = '\0';
- Priorities[NumPriorities].pri_name = newstr(&bp[1]);
- Priorities[NumPriorities].pri_val = atoi(++p);
- NumPriorities++;
- break;
-
- case 'Q': /* define queue */
- makequeue(&bp[1], true);
- break;
-
- case 'V': /* configuration syntax version */
- for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
- continue;
- if (!isascii(*p) || !isdigit(*p))
- {
- syserr("invalid argument to V line: \"%.20s\"",
- &bp[1]);
- break;
- }
- ConfigLevel = strtol(p, &ep, 10);
-
- /*
- ** Do heuristic tweaking for back compatibility.
- */
-
- if (ConfigLevel >= 5)
- {
- /* level 5 configs have short name in $w */
- p = macvalue('w', e);
- if (p != NULL && (p = strchr(p, '.')) != NULL)
- {
- *p = '\0';
- macdefine(&e->e_macro, A_TEMP, 'w',
- macvalue('w', e));
- }
- }
- if (ConfigLevel >= 6)
- {
- ColonOkInAddr = false;
- }
-
- /*
- ** Look for vendor code.
- */
-
- if (*ep++ == '/')
- {
- /* extract vendor code */
- for (p = ep; isascii(*p) && isalpha(*p); )
- p++;
- *p = '\0';
-
- if (!setvendor(ep))
- syserr("invalid V line vendor code: \"%s\"",
- ep);
- }
- break;
-
- case 'K':
- expand(&bp[1], exbuf, sizeof(exbuf), e);
- (void) makemapentry(exbuf);
- break;
-
- case 'E':
- p = strchr(bp, '=');
- if (p != NULL)
- *p++ = '\0';
- sm_setuserenv(&bp[1], p);
- break;
-
- case 'X': /* mail filter */
-#if MILTER
- milter_setup(&bp[1]);
-#else /* MILTER */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Filter usage ('X') requires Milter support (-DMILTER)\n");
-#endif /* MILTER */
- break;
-
- default:
- badline:
- syserr("unknown configuration line \"%s\"", bp);
- }
- if (bp != buf)
- sm_free(bp); /* XXX */
- }
- if (sm_io_error(cf))
- {
- syserr("I/O read error");
- finis(false, true, EX_OSFILE);
- }
- (void) sm_io_close(cf, SM_TIME_DEFAULT);
- 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;
- char *maptype[MAXMAPSTACK];
- short mapreturn[MAXMAPACTIONS];
-
- nmaps = switch_map_find("hosts", maptype, mapreturn);
- UseNameServer = false;
- if (nmaps > 0 && nmaps <= MAXMAPSTACK)
- {
- register int mapno;
-
- for (mapno = 0; mapno < nmaps && !UseNameServer;
- mapno++)
- {
- if (strcmp(maptype[mapno], "dns") == 0)
- UseNameServer = true;
- }
- }
- }
-}
-
-/*
-** TRANSLATE_DOLLARS -- convert $x into internal form
-**
-** Actually does all appropriate pre-processing of a config line
-** to turn it into internal form.
-**
-** Parameters:
-** ibp -- the buffer to translate.
-** obp -- where to put the translation; may be the same as obp
-** bsp -- a pointer to the size of obp; will be updated if
-** the buffer needs to be replaced.
-**
-** Returns:
-** The buffer pointer; may differ from obp if the expansion
-** is larger then *bsp, in which case this will point to
-** malloc()ed memory which must be free()d by the caller.
-*/
-
-char *
-translate_dollars(ibp, obp, bsp)
- char *ibp;
- char *obp;
- int *bsp;
-{
- register char *p;
- auto char *ep;
- char *bp;
-
- if (tTd(37, 53))
- {
- sm_dprintf("translate_dollars(");
- xputs(sm_debug_file(), ibp);
- sm_dprintf(")\n");
- }
-
- bp = quote_internal_chars(ibp, obp, bsp);
-
- for (p = bp; *p != '\0'; p++)
- {
- if (*p == '#' && p > bp && ConfigLevel >= 3)
- {
- register char *e;
-
- switch (*--p & 0377)
- {
- case MACROEXPAND:
- /* it's from $# -- let it go through */
- p++;
- break;
-
- case '\\':
- /* it's backslash escaped */
- (void) sm_strlcpy(p, p + 1, strlen(p));
- break;
-
- default:
- /* delete leading white space */
- while (isascii(*p) && isspace(*p) &&
- *p != '\n' && p > bp)
- {
- p--;
- }
- if ((e = strchr(++p, '\n')) != NULL)
- (void) sm_strlcpy(p, e, strlen(p));
- else
- *p-- = '\0';
- break;
- }
- continue;
- }
-
- if (*p != '$' || p[1] == '\0')
- continue;
-
- if (p[1] == '$')
- {
- /* actual dollar sign.... */
- (void) sm_strlcpy(p, p + 1, strlen(p));
- continue;
- }
-
- /* convert to macro expansion character */
- *p++ = MACROEXPAND;
-
- /* special handling for $=, $~, $&, and $? */
- if (*p == '=' || *p == '~' || *p == '&' || *p == '?')
- p++;
-
- /* convert macro name to code */
- *p = macid_parse(p, &ep);
- if (ep != p + 1)
- (void) sm_strlcpy(p + 1, ep, strlen(p + 1));
- }
-
- /* strip trailing white space from the line */
- while (--p > bp && isascii(*p) && isspace(*p))
- *p = '\0';
-
- if (tTd(37, 53))
- {
- sm_dprintf(" translate_dollars => ");
- xputs(sm_debug_file(), bp);
- sm_dprintf("\n");
- }
-
- return bp;
-}
-/*
-** TOOMANY -- signal too many of some option
-**
-** Parameters:
-** id -- the id of the error line
-** maxcnt -- the maximum possible values
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** gives a syserr.
-*/
-
-static void
-toomany(id, maxcnt)
- int id;
- int maxcnt;
-{
- syserr("too many %c lines, %d max", id, maxcnt);
-}
-/*
-** FILECLASS -- read members of a class from a file
-**
-** Parameters:
-** class -- class to define.
-** filename -- name of file to read.
-** fmt -- scanf string to use for match.
-** ismap -- if set, this is a map lookup.
-** safe -- if set, this is a safe read.
-** optional -- if set, it is not an error for the file to
-** not exist.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** puts all lines in filename that match a scanf into
-** the named class.
-*/
-
-/*
-** Break up the match into words and add to class.
-*/
-
-static void
-parse_class_words(class, line)
- int class;
- char *line;
-{
- while (line != NULL && *line != '\0')
- {
- register char *q;
-
- /* strip leading spaces */
- while (isascii(*line) && isspace(*line))
- line++;
- if (*line == '\0')
- break;
-
- /* find the end of the word */
- q = line;
- while (*line != '\0' && !(isascii(*line) && isspace(*line)))
- line++;
- if (*line != '\0')
- *line++ = '\0';
-
- /* enter the word in the symbol table */
- setclass(class, q);
- }
-}
-
-static void
-fileclass(class, filename, fmt, ismap, safe, optional)
- int class;
- char *filename;
- char *fmt;
- bool ismap;
- bool safe;
- bool optional;
-{
- SM_FILE_T *f;
- long sff;
- pid_t pid;
- register char *p;
- char buf[MAXLINE];
-
- if (tTd(37, 2))
- sm_dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);
-
- if (*filename == '\0')
- {
- syserr("fileclass: missing file name");
- return;
- }
- else if (ismap)
- {
- int status = 0;
- char *key;
- char *mn;
- char *cl, *spec;
- STAB *mapclass;
- MAP map;
-
- mn = newstr(macname(class));
-
- key = filename;
-
- /* skip past key */
- if ((p = strchr(filename, '@')) == NULL)
- {
- /* should not happen */
- syserr("fileclass: bogus map specification");
- sm_free(mn);
- return;
- }
-
- /* skip past '@' */
- *p++ = '\0';
- cl = p;
-
-#if LDAPMAP
- if (strcmp(cl, "LDAP") == 0)
- {
- int n;
- char *lc;
- char jbuf[MAXHOSTNAMELEN];
- char lcbuf[MAXLINE];
-
- /* Get $j */
- expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
- if (jbuf[0] == '\0')
- {
- (void) sm_strlcpy(jbuf, "localhost",
- sizeof(jbuf));
- }
-
- /* impose the default schema */
- lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
- if (lc == NULL)
- lc = "";
- else
- {
- expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
- lc = lcbuf;
- }
-
- cl = "ldap";
- n = sm_snprintf(buf, sizeof(buf),
- "-k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName=%s)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))) -v sendmailMTAClassValue,sendmailMTAClassSearch:FILTER:sendmailMTAClass,sendmailMTAClassURL:URL:sendmailMTAClass",
- mn, lc, jbuf);
- if (n >= sizeof(buf))
- {
- syserr("fileclass: F{%s}: Default LDAP string too long",
- mn);
- sm_free(mn);
- return;
- }
- spec = buf;
- }
- else
-#endif /* LDAPMAP */
- {
- if ((spec = strchr(cl, ':')) == NULL)
- {
- syserr("fileclass: F{%s}: missing map class",
- mn);
- sm_free(mn);
- return;
- }
- *spec++ ='\0';
- }
-
- /* set up map structure */
- mapclass = stab(cl, ST_MAPCLASS, ST_FIND);
- if (mapclass == NULL)
- {
- syserr("fileclass: F{%s}: class %s not available",
- mn, cl);
- sm_free(mn);
- return;
- }
- memset(&map, '\0', sizeof(map));
- map.map_class = &mapclass->s_mapclass;
- map.map_mname = mn;
- map.map_mflags |= MF_FILECLASS;
-
- if (tTd(37, 5))
- sm_dprintf("fileclass: F{%s}: map class %s, key %s, spec %s\n",
- mn, cl, key, spec);
-
-
- /* parse map spec */
- if (!map.map_class->map_parse(&map, spec))
- {
- /* map_parse() showed the error already */
- sm_free(mn);
- return;
- }
- map.map_mflags |= MF_VALID;
-
- /* open map */
- if (map.map_class->map_open(&map, O_RDONLY))
- {
- map.map_mflags |= MF_OPEN;
- map.map_pid = getpid();
- }
- else
- {
- if (!optional &&
- !bitset(MF_OPTIONAL, map.map_mflags))
- syserr("fileclass: F{%s}: map open failed",
- mn);
- sm_free(mn);
- return;
- }
-
- /* lookup */
- p = (*map.map_class->map_lookup)(&map, key, NULL, &status);
- if (status != EX_OK && status != EX_NOTFOUND)
- {
- if (!optional)
- syserr("fileclass: F{%s}: map lookup failed",
- mn);
- p = NULL;
- }
-
- /* use the results */
- if (p != NULL)
- parse_class_words(class, p);
-
- /* close map */
- map.map_mflags |= MF_CLOSING;
- map.map_class->map_close(&map);
- map.map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
- sm_free(mn);
- return;
- }
- else if (filename[0] == '|')
- {
- auto int fd;
- int i;
- char *argv[MAXPV + 1];
-
- i = 0;
- for (p = strtok(&filename[1], " \t");
- p != NULL && i < MAXPV;
- p = strtok(NULL, " \t"))
- argv[i++] = p;
- argv[i] = NULL;
- pid = prog_open(argv, &fd, CurEnv);
- if (pid < 0)
- f = NULL;
- else
- f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &fd, SM_IO_RDONLY, NULL);
- }
- else
- {
- pid = -1;
- sff = SFF_REGONLY;
- if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
- sff |= SFF_SAFEDIRPATH;
- if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR,
- DontBlameSendmail))
- sff |= SFF_NOWLINK;
- if (safe)
- sff |= SFF_OPENASROOT;
- else if (RealUid == 0)
- sff |= SFF_ROOTOK;
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
- f = safefopen(filename, O_RDONLY, 0, sff);
- }
- if (f == NULL)
- {
- if (!optional)
- syserr("fileclass: cannot open '%s'", filename);
- return;
- }
-
- while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
- {
-#if SCANF
- char wordbuf[MAXLINE + 1];
-#endif /* SCANF */
-
- if (buf[0] == '#')
- continue;
-#if SCANF
- if (sm_io_sscanf(buf, fmt, wordbuf) != 1)
- continue;
- p = wordbuf;
-#else /* SCANF */
- p = buf;
-#endif /* SCANF */
-
- parse_class_words(class, p);
-
- /*
- ** If anything else is added here,
- ** check if the '@' map case above
- ** needs the code as well.
- */
- }
-
- (void) sm_io_close(f, SM_TIME_DEFAULT);
- if (pid > 0)
- (void) waitfor(pid);
-}
-/*
-** MAKEMAILER -- define a new mailer.
-**
-** Parameters:
-** line -- description of mailer. This is in labeled
-** fields. The fields are:
-** A -- the argv for this mailer
-** C -- the character set for MIME conversions
-** D -- the directory to run in
-** E -- the eol string
-** F -- the flags associated with the mailer
-** L -- the maximum line length
-** M -- the maximum message size
-** N -- the niceness at which to run
-** P -- the path to the mailer
-** Q -- the queue group for the mailer
-** R -- the recipient rewriting set
-** S -- the sender rewriting set
-** T -- the mailer type (for DSNs)
-** U -- the uid to run as
-** W -- the time to wait at the end
-** m -- maximum messages per connection
-** r -- maximum number of recipients per message
-** / -- new root directory
-** The first word is the canonical name of the mailer.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** enters the mailer into the mailer table.
-*/
-
-void
-makemailer(line)
- char *line;
-{
- register char *p;
- register struct mailer *m;
- register STAB *s;
- int i;
- char fcode;
- auto char *endp;
- static int nextmailer = 0; /* "free" index into Mailer struct */
-
- /* allocate a mailer and set up defaults */
- m = (struct mailer *) xalloc(sizeof(*m));
- memset((char *) m, '\0', sizeof(*m));
- errno = 0; /* avoid bogus error text */
-
- /* collect the mailer name */
- for (p = line;
- *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
- p++)
- continue;
- if (*p != '\0')
- *p++ = '\0';
- if (line[0] == '\0')
- {
- syserr("name required for mailer");
- return;
- }
- m->m_name = newstr(line);
- m->m_qgrp = NOQGRP;
- m->m_uid = NO_UID;
- m->m_gid = NO_GID;
-
- /* now scan through and assign info from the fields */
- while (*p != '\0')
- {
- auto char *delimptr;
-
- while (*p != '\0' &&
- (*p == ',' || (isascii(*p) && isspace(*p))))
- p++;
-
- /* p now points to field code */
- fcode = *p;
- while (*p != '\0' && *p != '=' && *p != ',')
- p++;
- if (*p++ != '=')
- {
- syserr("mailer %s: `=' expected", m->m_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 'P': /* pathname */
- if (*p != '\0') /* error is issued below */
- m->m_mailer = newstr(p);
- break;
-
- case 'F': /* flags */
- for (; *p != '\0'; p++)
- {
- if (!(isascii(*p) && isspace(*p)))
- {
-#if _FFR_DEPRECATE_MAILER_FLAG_I
- if (*p == M_INTERNAL)
- sm_syslog(LOG_WARNING, NOQID,
- "WARNING: mailer=%s, flag=%c deprecated",
- m->m_name, *p);
-#endif /* _FFR_DEPRECATE_MAILER_FLAG_I */
- setbitn(bitidx(*p), m->m_flags);
- }
- }
- break;
-
- case 'S': /* sender rewriting ruleset */
- case 'R': /* recipient rewriting ruleset */
- i = strtorwset(p, &endp, ST_ENTER);
- if (i < 0)
- return;
- if (fcode == 'S')
- m->m_sh_rwset = m->m_se_rwset = i;
- else
- m->m_rh_rwset = m->m_re_rwset = i;
-
- p = endp;
- if (*p++ == '/')
- {
- i = strtorwset(p, NULL, ST_ENTER);
- if (i < 0)
- return;
- if (fcode == 'S')
- m->m_sh_rwset = i;
- else
- m->m_rh_rwset = i;
- }
- break;
-
- case 'E': /* end of line string */
- if (*p == '\0')
- syserr("mailer %s: null end-of-line string",
- m->m_name);
- else
- m->m_eol = newstr(p);
- break;
-
- case 'A': /* argument vector */
- if (*p != '\0') /* error is issued below */
- m->m_argv = makeargv(p);
- break;
-
- case 'M': /* maximum message size */
- m->m_maxsize = atol(p);
- break;
-
- case 'm': /* maximum messages per connection */
- m->m_maxdeliveries = atoi(p);
- break;
-
- case 'r': /* max recipient per envelope */
- m->m_maxrcpt = atoi(p);
- break;
-
- case 'L': /* maximum line length */
- m->m_linelimit = atoi(p);
- if (m->m_linelimit < 0)
- m->m_linelimit = 0;
- break;
-
- case 'N': /* run niceness */
- m->m_nice = atoi(p);
- break;
-
- case 'D': /* working directory */
- if (*p == '\0')
- syserr("mailer %s: null working directory",
- m->m_name);
- else
- m->m_execdir = newstr(p);
- break;
-
- case 'C': /* default charset */
- if (*p == '\0')
- syserr("mailer %s: null charset", m->m_name);
- else
- m->m_defcharset = newstr(p);
- break;
-
- case 'Q': /* queue for this mailer */
- if (*p == '\0')
- {
- syserr("mailer %s: null queue", m->m_name);
- break;
- }
- s = stab(p, ST_QUEUE, ST_FIND);
- if (s == NULL)
- syserr("mailer %s: unknown queue %s",
- m->m_name, p);
- else
- m->m_qgrp = s->s_quegrp->qg_index;
- break;
-
- case 'T': /* MTA-Name/Address/Diagnostic types */
- /* extract MTA name type; default to "dns" */
- m->m_mtatype = newstr(p);
- p = strchr(m->m_mtatype, '/');
- if (p != NULL)
- {
- *p++ = '\0';
- if (*p == '\0')
- p = NULL;
- }
- if (*m->m_mtatype == '\0')
- m->m_mtatype = "dns";
-
- /* extract address type; default to "rfc822" */
- m->m_addrtype = p;
- if (p != NULL)
- p = strchr(p, '/');
- if (p != NULL)
- {
- *p++ = '\0';
- if (*p == '\0')
- p = NULL;
- }
- if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
- m->m_addrtype = "rfc822";
-
- /* extract diagnostic type; default to "smtp" */
- m->m_diagtype = p;
- if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
- m->m_diagtype = "smtp";
- break;
-
- case 'U': /* user id */
- if (isascii(*p) && !isdigit(*p))
- {
- char *q = p;
- struct passwd *pw;
-
- while (*p != '\0' && isascii(*p) &&
- (isalnum(*p) || strchr("-_", *p) != NULL))
- p++;
- while (isascii(*p) && isspace(*p))
- *p++ = '\0';
- if (*p != '\0')
- *p++ = '\0';
- if (*q == '\0')
- {
- syserr("mailer %s: null user name",
- m->m_name);
- break;
- }
- pw = sm_getpwnam(q);
- if (pw == NULL)
- {
- syserr("readcf: mailer U= flag: unknown user %s", q);
- break;
- }
- else
- {
- m->m_uid = pw->pw_uid;
- m->m_gid = pw->pw_gid;
- }
- }
- else
- {
- auto char *q;
-
- m->m_uid = strtol(p, &q, 0);
- p = q;
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p != '\0')
- p++;
- }
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- break;
- if (isascii(*p) && !isdigit(*p))
- {
- char *q = p;
- struct group *gr;
-
- while (isascii(*p) && isalnum(*p))
- p++;
- *p++ = '\0';
- if (*q == '\0')
- {
- syserr("mailer %s: null group name",
- m->m_name);
- break;
- }
- gr = getgrnam(q);
- if (gr == NULL)
- {
- syserr("readcf: mailer U= flag: unknown group %s", q);
- break;
- }
- else
- m->m_gid = gr->gr_gid;
- }
- else
- {
- 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;
- }
-
-#if !HASRRESVPORT
- if (bitnset(M_SECURE_PORT, m->m_flags))
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "M%s: Warning: F=%c set on system that doesn't support rresvport()\n",
- m->m_name, M_SECURE_PORT);
- }
-#endif /* !HASRRESVPORT */
-
-#if !HASNICE
- if (m->m_nice != 0)
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "M%s: Warning: N= set on system that doesn't support nice()\n",
- m->m_name);
- }
-#endif /* !HASNICE */
-
- /* do some rationality checking */
- if (m->m_argv == NULL)
- {
- syserr("M%s: A= argument required", m->m_name);
- return;
- }
- if (m->m_mailer == NULL)
- {
- syserr("M%s: P= argument required", m->m_name);
- return;
- }
-
- if (nextmailer >= MAXMAILERS)
- {
- syserr("too many mailers defined (%d max)", MAXMAILERS);
- return;
- }
-
- if (m->m_maxrcpt <= 0)
- m->m_maxrcpt = DEFAULT_MAX_RCPT;
-
- /* do some heuristic cleanup for back compatibility */
- if (bitnset(M_LIMITS, m->m_flags))
- {
- if (m->m_linelimit == 0)
- m->m_linelimit = SMTPLINELIM;
- if (ConfigLevel < 2)
- setbitn(M_7BITS, m->m_flags);
- }
-
- if (strcmp(m->m_mailer, "[TCP]") == 0)
- {
- syserr("M%s: P=[TCP] must be replaced by P=[IPC]", m->m_name);
- return;
- }
-
- if (strcmp(m->m_mailer, "[IPC]") == 0)
- {
- /* Use the second argument for host or path to socket */
- if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
- m->m_argv[1][0] == '\0')
- {
- syserr("M%s: too few parameters for %s mailer",
- m->m_name, m->m_mailer);
- return;
- }
- if (strcmp(m->m_argv[0], "TCP") != 0
-#if NETUNIX
- && strcmp(m->m_argv[0], "FILE") != 0
-#endif /* NETUNIX */
- )
- {
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "M%s: Warning: first argument in %s mailer must be %s\n",
- m->m_name, m->m_mailer,
-#if NETUNIX
- "TCP or FILE"
-#else /* NETUNIX */
- "TCP"
-#endif /* NETUNIX */
- );
- }
- if (m->m_mtatype == NULL)
- m->m_mtatype = "dns";
- if (m->m_addrtype == NULL)
- m->m_addrtype = "rfc822";
- if (m->m_diagtype == NULL)
- {
- if (m->m_argv[0] != NULL &&
- strcmp(m->m_argv[0], "FILE") == 0)
- m->m_diagtype = "x-unix";
- else
- m->m_diagtype = "smtp";
- }
- }
- else if (strcmp(m->m_mailer, "[FILE]") == 0)
- {
- /* Use the second argument for filename */
- if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
- m->m_argv[2] != NULL)
- {
- syserr("M%s: too %s parameters for [FILE] mailer",
- m->m_name,
- (m->m_argv[0] == NULL ||
- m->m_argv[1] == NULL) ? "few" : "many");
- return;
- }
- else if (strcmp(m->m_argv[0], "FILE") != 0)
- {
- syserr("M%s: first argument in [FILE] mailer must be FILE",
- m->m_name);
- return;
- }
- }
-
- if (m->m_eol == NULL)
- {
- char **pp;
-
- /* default for SMTP is \r\n; use \n for local delivery */
- for (pp = m->m_argv; *pp != NULL; pp++)
- {
- for (p = *pp; *p != '\0'; )
- {
- if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
- break;
- }
- if (*p != '\0')
- break;
- }
- if (*pp == NULL)
- m->m_eol = "\r\n";
- else
- m->m_eol = "\n";
- }
-
- /* enter the mailer into the symbol table */
- s = stab(m->m_name, ST_MAILER, ST_ENTER);
- if (s->s_mailer != NULL)
- {
- i = s->s_mailer->m_mno;
- sm_free(s->s_mailer); /* XXX */
- }
- else
- {
- i = nextmailer++;
- }
- Mailer[i] = s->s_mailer = m;
- m->m_mno = i;
-}
-/*
-** MUNCHSTRING -- translate a string into internal form.
-**
-** Parameters:
-** p -- the string to munch.
-** delimptr -- if non-NULL, set to the pointer of the
-** field delimiter character.
-** delim -- the delimiter for the field.
-**
-** 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 *
-munchstring(p, delimptr, delim)
- register char *p;
- char **delimptr;
- int delim;
-{
- register char *q;
- bool backslash = false;
- bool quotemode = false;
- static char buf[MAXLINE];
-
- for (q = buf; *p != '\0' && q < &buf[sizeof(buf) - 1]; p++)
- {
- if (backslash)
- {
- /* everything is roughly literal */
- backslash = false;
- switch (*p)
- {
- case 'r': /* carriage return */
- *q++ = '\r';
- continue;
-
- case 'n': /* newline */
- *q++ = '\n';
- continue;
-
- case 'f': /* form feed */
- *q++ = '\f';
- continue;
-
- case 'b': /* backspace */
- *q++ = '\b';
- continue;
- }
- *q++ = *p;
- }
- else
- {
- if (*p == '\\')
- backslash = true;
- else if (*p == '"')
- quotemode = !quotemode;
- else if (quotemode || *p != delim)
- *q++ = *p;
- else
- break;
- }
- }
-
- if (delimptr != NULL)
- *delimptr = p;
- *q++ = '\0';
- return buf;
-}
-/*
-** EXTRQUOTSTR -- extract a (quoted) string.
-**
-** This routine deals with quoted (") strings and escaped
-** spaces (\\ ).
-**
-** Parameters:
-** p -- source string.
-** delimptr -- if non-NULL, set to the pointer of the
-** field delimiter character.
-** delimbuf -- delimiters for the field.
-** st -- if non-NULL, store the return value (whether the
-** string was correctly quoted) here.
-**
-** Returns:
-** the extracted string.
-**
-** Side Effects:
-** the returned string is a local static buffer.
-** it must be copied before the function is called again.
-*/
-
-static char *
-extrquotstr(p, delimptr, delimbuf, st)
- register char *p;
- char **delimptr;
- char *delimbuf;
- bool *st;
-{
- register char *q;
- bool backslash = false;
- bool quotemode = false;
- static char buf[MAXLINE];
-
- for (q = buf; *p != '\0' && q < &buf[sizeof(buf) - 1]; p++)
- {
- if (backslash)
- {
- backslash = false;
- if (*p != ' ')
- *q++ = '\\';
- }
- if (*p == '\\')
- backslash = true;
- else if (*p == '"')
- quotemode = !quotemode;
- else if (quotemode ||
- strchr(delimbuf, (int) *p) == NULL)
- *q++ = *p;
- else
- break;
- }
-
- if (delimptr != NULL)
- *delimptr = p;
- *q++ = '\0';
- if (st != NULL)
- *st = !(quotemode || backslash);
- return buf;
-}
-/*
-** MAKEARGV -- break up a string into words
-**
-** Parameters:
-** p -- the string to break up.
-**
-** Returns:
-** a char **argv (dynamically allocated)
-**
-** Side Effects:
-** munges p.
-*/
-
-static char **
-makeargv(p)
- register char *p;
-{
- char *q;
- int i;
- char **avp;
- char *argv[MAXPV + 1];
-
- /* take apart the words */
- i = 0;
- while (*p != '\0' && i < MAXPV)
- {
- q = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- while (isascii(*p) && isspace(*p))
- *p++ = '\0';
- argv[i++] = newstr(q);
- }
- argv[i++] = NULL;
-
- /* now make a copy of the argv */
- avp = (char **) xalloc(sizeof(*avp) * i);
- memmove((char *) avp, (char *) argv, sizeof(*avp) * i);
-
- return avp;
-}
-/*
-** PRINTRULES -- print rewrite rules (for debugging)
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** prints rewrite rules.
-*/
-
-void
-printrules()
-{
- register struct rewrite *rwp;
- register int ruleset;
-
- for (ruleset = 0; ruleset < 10; ruleset++)
- {
- if (RewriteRules[ruleset] == NULL)
- continue;
- sm_dprintf("\n----Rule Set %d:", ruleset);
-
- for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
- {
- sm_dprintf("\nLHS:");
- printav(sm_debug_file(), rwp->r_lhs);
- sm_dprintf("RHS:");
- printav(sm_debug_file(), rwp->r_rhs);
- }
- }
-}
-/*
-** PRINTMAILER -- print mailer structure (for debugging)
-**
-** Parameters:
-** fp -- output file
-** m -- the mailer to print
-**
-** Returns:
-** none.
-*/
-
-void
-printmailer(fp, m)
- SM_FILE_T *fp;
- register MAILER *m;
-{
- int j;
-
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "mailer %d (%s): P=%s S=", m->m_mno, m->m_name,
- m->m_mailer);
- if (RuleSetNames[m->m_se_rwset] == NULL)
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d/",
- m->m_se_rwset);
- else
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s/",
- RuleSetNames[m->m_se_rwset]);
- if (RuleSetNames[m->m_sh_rwset] == NULL)
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d R=",
- m->m_sh_rwset);
- else
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s R=",
- RuleSetNames[m->m_sh_rwset]);
- if (RuleSetNames[m->m_re_rwset] == NULL)
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d/",
- m->m_re_rwset);
- else
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s/",
- RuleSetNames[m->m_re_rwset]);
- if (RuleSetNames[m->m_rh_rwset] == NULL)
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d ",
- m->m_rh_rwset);
- else
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s ",
- RuleSetNames[m->m_rh_rwset]);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "M=%ld U=%d:%d F=",
- m->m_maxsize, (int) m->m_uid, (int) m->m_gid);
- for (j = '\0'; j <= '\177'; j++)
- if (bitnset(j, m->m_flags))
- (void) sm_io_putc(fp, SM_TIME_DEFAULT, j);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " L=%d E=",
- m->m_linelimit);
- xputs(fp, m->m_eol);
- if (m->m_defcharset != NULL)
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " C=%s",
- m->m_defcharset);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " T=%s/%s/%s",
- m->m_mtatype == NULL
- ? "<undefined>" : m->m_mtatype,
- m->m_addrtype == NULL
- ? "<undefined>" : m->m_addrtype,
- m->m_diagtype == NULL
- ? "<undefined>" : m->m_diagtype);
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " r=%d", m->m_maxrcpt);
- if (m->m_argv != NULL)
- {
- char **a = m->m_argv;
-
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " A=");
- while (*a != NULL)
- {
- if (a != m->m_argv)
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- " ");
- xputs(fp, *a++);
- }
- }
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\n");
-}
-/*
-** SETOPTION -- set global processing option
-**
-** Parameters:
-** opt -- option name.
-** val -- option value (as a text string).
-** safe -- set if this came from a configuration file.
-** Some options (if set from the command line) will
-** reset the user id to avoid security problems.
-** sticky -- if set, don't let other setoptions override
-** this value.
-** e -- the main envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets options as implied by the arguments.
-*/
-
-static BITMAP256 StickyOpt; /* set if option is stuck */
-
-#if NAMED_BIND
-
-static struct resolverflags
-{
- char *rf_name; /* name of the flag */
- long rf_bits; /* bits to set/clear */
-} ResolverFlags[] =
-{
- { "debug", RES_DEBUG },
- { "aaonly", RES_AAONLY },
- { "usevc", RES_USEVC },
- { "primary", RES_PRIMARY },
- { "igntc", RES_IGNTC },
- { "recurse", RES_RECURSE },
- { "defnames", RES_DEFNAMES },
- { "stayopen", RES_STAYOPEN },
- { "dnsrch", RES_DNSRCH },
-# ifdef RES_USE_INET6
- { "use_inet6", RES_USE_INET6 },
-# endif /* RES_USE_INET6 */
- { "true", 0 }, /* avoid error on old syntax */
- { NULL, 0 }
-};
-
-#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 */
-
-static struct optioninfo
-{
- char *o_name; /* long name of option */
- unsigned char o_code; /* short name of option */
- unsigned short o_flags; /* option flags */
-} OptionTab[] =
-{
-#if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE)
- { "RemoteMode", '>', OI_NONE },
-#endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */
- { "SevenBitInput", '7', OI_SAFE },
- { "EightBitMode", '8', OI_SAFE },
- { "AliasFile", 'A', OI_NONE },
- { "AliasWait", 'a', OI_NONE },
- { "BlankSub", 'B', OI_NONE },
- { "MinFreeBlocks", 'b', OI_SAFE },
- { "CheckpointInterval", 'C', OI_SAFE },
- { "HoldExpensive", 'c', OI_NONE },
- { "DeliveryMode", 'd', OI_SAFE },
- { "ErrorHeader", 'E', OI_NONE },
- { "ErrorMode", 'e', OI_SAFE },
- { "TempFileMode", 'F', OI_NONE },
- { "SaveFromLine", 'f', OI_NONE },
- { "MatchGECOS", 'G', OI_NONE },
-
- /* no long name, just here to avoid problems in setoption */
- { "", 'g', OI_NONE },
- { "HelpFile", 'H', OI_NONE },
- { "MaxHopCount", 'h', OI_NONE },
- { "ResolverOptions", 'I', OI_NONE },
- { "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 },
-
- /* no long name, just here to avoid problems in setoption */
- { "", 'M', OI_NONE },
- { "CheckAliases", 'n', OI_NONE },
- { "OldStyleHeaders", 'o', OI_SAFE },
- { "DaemonPortOptions", 'O', OI_NONE },
- { "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, OI_SAFE },
-#define O_HOSTSFILE 0x82
- { "HostsFile", O_HOSTSFILE, OI_NONE },
-#define O_MQA 0x83
- { "MinQueueAge", O_MQA, OI_SAFE },
-#define O_DEFCHARSET 0x85
- { "DefaultCharSet", O_DEFCHARSET, OI_SAFE },
-#define O_SSFILE 0x86
- { "ServiceSwitchFile", O_SSFILE, OI_NONE },
-#define O_DIALDELAY 0x87
- { "DialDelay", O_DIALDELAY, OI_SAFE },
-#define O_NORCPTACTION 0x88
- { "NoRecipientAction", O_NORCPTACTION, OI_SAFE },
-#define O_SAFEFILEENV 0x89
- { "SafeFileEnvironment", O_SAFEFILEENV, OI_NONE },
-#define O_MAXMSGSIZE 0x8a
- { "MaxMessageSize", O_MAXMSGSIZE, OI_NONE },
-#define O_COLONOKINADDR 0x8b
- { "ColonOkInAddr", O_COLONOKINADDR, OI_SAFE },
-#define O_MAXQUEUERUN 0x8c
- { "MaxQueueRunSize", O_MAXQUEUERUN, OI_SAFE },
-#define O_MAXCHILDREN 0x8d
- { "MaxDaemonChildren", O_MAXCHILDREN, OI_NONE },
-#define O_KEEPCNAMES 0x8e
- { "DontExpandCnames", O_KEEPCNAMES, OI_NONE },
-#define O_MUSTQUOTE 0x8f
- { "MustQuoteChars", O_MUSTQUOTE, OI_NONE },
-#define O_SMTPGREETING 0x90
- { "SmtpGreetingMessage", O_SMTPGREETING, OI_NONE },
-#define O_UNIXFROM 0x91
- { "UnixFromLine", O_UNIXFROM, OI_NONE },
-#define O_OPCHARS 0x92
- { "OperatorChars", O_OPCHARS, OI_NONE },
-#define O_DONTINITGRPS 0x93
- { "DontInitGroups", O_DONTINITGRPS, OI_NONE },
-#define O_SLFH 0x94
- { "SingleLineFromHeader", O_SLFH, OI_SAFE },
-#define O_ABH 0x95
- { "AllowBogusHELO", O_ABH, OI_SAFE },
-#define O_CONNTHROT 0x97
- { "ConnectionRateThrottle", O_CONNTHROT, OI_NONE },
-#define O_UGW 0x99
- { "UnsafeGroupWrites", O_UGW, OI_NONE },
-#define O_DBLBOUNCE 0x9a
- { "DoubleBounceAddress", O_DBLBOUNCE, OI_NONE },
-#define O_HSDIR 0x9b
- { "HostStatusDirectory", O_HSDIR, OI_NONE },
-#define O_SINGTHREAD 0x9c
- { "SingleThreadDelivery", O_SINGTHREAD, OI_NONE },
-#define O_RUNASUSER 0x9d
- { "RunAsUser", O_RUNASUSER, OI_NONE },
-#define O_DSN_RRT 0x9e
- { "RrtImpliesDsn", O_DSN_RRT, OI_NONE },
-#define O_PIDFILE 0x9f
- { "PidFile", O_PIDFILE, OI_NONE },
-#define O_DONTBLAMESENDMAIL 0xa0
- { "DontBlameSendmail", O_DONTBLAMESENDMAIL, OI_NONE },
-#define O_DPI 0xa1
- { "DontProbeInterfaces", O_DPI, OI_NONE },
-#define O_MAXRCPT 0xa2
- { "MaxRecipientsPerMessage", O_MAXRCPT, OI_SAFE },
-#define O_DEADLETTER 0xa3
- { "DeadLetterDrop", O_DEADLETTER, OI_NONE },
-#if _FFR_DONTLOCKFILESFORREAD_OPTION
-# define O_DONTLOCK 0xa4
- { "DontLockFilesForRead", O_DONTLOCK, OI_NONE },
-#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
-#define O_MAXALIASRCSN 0xa5
- { "MaxAliasRecursion", O_MAXALIASRCSN, OI_NONE },
-#define O_CNCTONLYTO 0xa6
- { "ConnectOnlyTo", O_CNCTONLYTO, OI_NONE },
-#define O_TRUSTUSER 0xa7
- { "TrustedUser", O_TRUSTUSER, OI_NONE },
-#define O_MAXMIMEHDRLEN 0xa8
- { "MaxMimeHeaderLength", O_MAXMIMEHDRLEN, OI_NONE },
-#define O_CONTROLSOCKET 0xa9
- { "ControlSocketName", O_CONTROLSOCKET, OI_NONE },
-#define O_MAXHDRSLEN 0xaa
- { "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 },
-#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 },
-#define O_INPUTMILTER 0xbb
- { "InputMailFilters", O_INPUTMILTER, OI_NONE },
-#define O_MILTER 0xbc
- { "Milter", O_MILTER, OI_SUBOPT },
-#define O_SASLOPTS 0xbd
- { "AuthOptions", O_SASLOPTS, OI_NONE },
-#define O_QUEUE_FILE_MODE 0xbe
- { "QueueFileMode", O_QUEUE_FILE_MODE, OI_NONE },
-#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 },
-#define O_TLS_SRV_OPTS 0xc2
- { "TLSSrvOptions", O_TLS_SRV_OPTS, OI_NONE },
-#define O_RCPTTHROT 0xc3
- { "BadRcptThrottle", O_RCPTTHROT, OI_SAFE },
-#define O_DLVR_MIN 0xc4
- { "DeliverByMin", O_DLVR_MIN, OI_NONE },
-#define O_MAXQUEUECHILDREN 0xc5
- { "MaxQueueChildren", O_MAXQUEUECHILDREN, OI_NONE },
-#define O_MAXRUNNERSPERQUEUE 0xc6
- { "MaxRunnersPerQueue", O_MAXRUNNERSPERQUEUE, OI_NONE },
-#define O_DIRECTSUBMODIFIERS 0xc7
- { "DirectSubmissionModifiers", O_DIRECTSUBMODIFIERS, OI_NONE },
-#define O_NICEQUEUERUN 0xc8
- { "NiceQueueRun", O_NICEQUEUERUN, OI_NONE },
-#define O_SHMKEY 0xc9
- { "SharedMemoryKey", O_SHMKEY, OI_NONE },
-#define O_SASLBITS 0xca
- { "AuthMaxBits", O_SASLBITS, OI_NONE },
-#define O_MBDB 0xcb
- { "MailboxDatabase", O_MBDB, OI_NONE },
-#define O_MSQ 0xcc
- { "UseMSP", O_MSQ, OI_NONE },
-#define O_DELAY_LA 0xcd
- { "DelayLA", O_DELAY_LA, OI_NONE },
-#define O_FASTSPLIT 0xce
- { "FastSplit", O_FASTSPLIT, OI_NONE },
-#define O_SOFTBOUNCE 0xcf
- { "SoftBounce", O_SOFTBOUNCE, OI_NONE },
-#define O_SHMKEYFILE 0xd0
- { "SharedMemoryKeyFile", O_SHMKEYFILE, OI_NONE },
-#define O_REJECTLOGINTERVAL 0xd1
- { "RejectLogInterval", O_REJECTLOGINTERVAL, OI_NONE },
-#define O_REQUIRES_DIR_FSYNC 0xd2
- { "RequiresDirfsync", O_REQUIRES_DIR_FSYNC, OI_NONE },
-#define O_CONNECTION_RATE_WINDOW_SIZE 0xd3
- { "ConnectionRateWindowSize", O_CONNECTION_RATE_WINDOW_SIZE, OI_NONE },
-#define O_CRLFILE 0xd4
- { "CRLFile", O_CRLFILE, OI_NONE },
-#define O_FALLBACKSMARTHOST 0xd5
- { "FallbackSmartHost", O_FALLBACKSMARTHOST, OI_NONE },
-#define O_SASLREALM 0xd6
- { "AuthRealm", O_SASLREALM, OI_NONE },
-#if _FFR_CRLPATH
-# define O_CRLPATH 0xd7
- { "CRLPath", O_CRLPATH, OI_NONE },
-#endif /* _FFR_CRLPATH */
-#define O_HELONAME 0xd8
- { "HeloName", O_HELONAME, OI_NONE },
-#if _FFR_MEMSTAT
-# define O_REFUSELOWMEM 0xd9
- { "RefuseLowMem", O_REFUSELOWMEM, OI_NONE },
-# define O_QUEUELOWMEM 0xda
- { "QueueLowMem", O_QUEUELOWMEM, OI_NONE },
-# define O_MEMRESOURCE 0xdb
- { "MemoryResource", O_MEMRESOURCE, OI_NONE },
-#endif /* _FFR_MEMSTAT */
-#define O_MAXNOOPCOMMANDS 0xdc
- { "MaxNOOPCommands", O_MAXNOOPCOMMANDS, OI_NONE },
-#if _FFR_MSG_ACCEPT
-# define O_MSG_ACCEPT 0xdd
- { "MessageAccept", O_MSG_ACCEPT, OI_NONE },
-#endif /* _FFR_MSG_ACCEPT */
-#if _FFR_QUEUE_RUN_PARANOIA
-# define O_CHK_Q_RUNNERS 0xde
- { "CheckQueueRunners", O_CHK_Q_RUNNERS, OI_NONE },
-#endif /* _FFR_QUEUE_RUN_PARANOIA */
-#if _FFR_EIGHT_BIT_ADDR_OK
-# if !ALLOW_255
-# ERROR FFR_EIGHT_BIT_ADDR_OK requires _ALLOW_255
-# endif /* !ALLOW_255 */
-# define O_EIGHT_BIT_ADDR_OK 0xdf
- { "EightBitAddrOK", O_EIGHT_BIT_ADDR_OK, OI_NONE },
-#endif /* _FFR_EIGHT_BIT_ADDR_OK */
-#if _FFR_ADDR_TYPE_MODES
-# define O_ADDR_TYPE_MODES 0xe0
- { "AddrTypeModes", O_ADDR_TYPE_MODES, OI_NONE },
-#endif /* _FFR_ADDR_TYPE_MODES */
-
- { NULL, '\0', OI_NONE }
-};
-
-# define CANONIFY(val)
-
-# define SET_OPT_DEFAULT(opt, val) opt = val
-
-/* set a string option by expanding the value and assigning it */
-/* WARNING this belongs ONLY into a case statement! */
-#define SET_STRING_EXP(str) \
- expand(val, exbuf, sizeof(exbuf), e); \
- newval = sm_pstrdup_x(exbuf); \
- if (str != NULL) \
- sm_free(str); \
- CANONIFY(newval); \
- str = newval; \
- break
-
-#define OPTNAME o->o_name == NULL ? "<unknown>" : o->o_name
-
-void
-setoption(opt, val, safe, sticky, e)
- int opt;
- char *val;
- bool safe;
- bool sticky;
- register ENVELOPE *e;
-{
- register char *p;
- register struct optioninfo *o;
- char *subopt;
- int mid;
- bool can_setuid = RunAsUid == 0;
- auto char *ep;
- char buf[50];
- extern bool Warn_Q_option;
-#if _FFR_ALLOW_SASLINFO
- extern unsigned int SubmitMode;
-#endif /* _FFR_ALLOW_SASLINFO */
-#if STARTTLS || SM_CONF_SHM
- char *newval;
- char exbuf[MAXLINE];
-#endif /* STARTTLS || SM_CONF_SHM */
-
- errno = 0;
- if (opt == ' ')
- {
- /* full word options */
- struct optioninfo *sel;
-
- p = strchr(val, '=');
- if (p == NULL)
- p = &val[strlen(val)];
- while (*--p == ' ')
- continue;
- while (*++p == ' ')
- *p = '\0';
- if (p == val)
- {
- syserr("readcf: null option name");
- return;
- }
- if (*p == '=')
- *p++ = '\0';
- while (*p == ' ')
- p++;
- subopt = strchr(val, '.');
- if (subopt != NULL)
- *subopt++ = '\0';
- sel = NULL;
- for (o = OptionTab; o->o_name != NULL; o++)
- {
- if (sm_strncasecmp(o->o_name, val, strlen(val)) != 0)
- continue;
- if (strlen(o->o_name) == strlen(val))
- {
- /* completely specified -- this must be it */
- sel = NULL;
- break;
- }
- if (sel != NULL)
- break;
- sel = o;
- }
- if (sel != NULL && o->o_name == NULL)
- o = sel;
- else if (o->o_name == NULL)
- {
- syserr("readcf: unknown option name %s", val);
- return;
- }
- else if (sel != NULL)
- {
- syserr("readcf: ambiguous option name %s (matches %s and %s)",
- val, sel->o_name, o->o_name);
- return;
- }
- if (strlen(val) != strlen(o->o_name))
- {
- int oldVerbose = Verbose;
-
- Verbose = 1;
- message("Option %s used as abbreviation for %s",
- val, o->o_name);
- Verbose = oldVerbose;
- }
- opt = o->o_code;
- val = p;
- }
- else
- {
- for (o = OptionTab; o->o_name != NULL; o++)
- {
- if (o->o_code == opt)
- break;
- }
- if (o->o_name == NULL)
- {
- syserr("readcf: unknown option name 0x%x", opt & 0xff);
- return;
- }
- subopt = NULL;
- }
-
- if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags))
- {
- if (tTd(37, 1))
- sm_dprintf("setoption: %s does not support suboptions, ignoring .%s\n",
- OPTNAME, subopt);
- subopt = NULL;
- }
-
- if (tTd(37, 1))
- {
- sm_dprintf(isascii(opt) && isprint(opt) ?
- "setoption %s (%c)%s%s=" :
- "setoption %s (0x%x)%s%s=",
- OPTNAME, opt, subopt == NULL ? "" : ".",
- subopt == NULL ? "" : subopt);
- xputs(sm_debug_file(), val);
- }
-
- /*
- ** See if this option is preset for us.
- */
-
- if (!sticky && bitnset(opt, StickyOpt))
- {
- if (tTd(37, 1))
- sm_dprintf(" (ignored)\n");
- return;
- }
-
- /*
- ** Check to see if this option can be specified by this user.
- */
-
- if (!safe && RealUid == 0)
- safe = true;
- if (!safe && !bitset(OI_SAFE, o->o_flags))
- {
- if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
- {
- int dp;
-
- if (tTd(37, 1))
- sm_dprintf(" (unsafe)");
- dp = drop_privileges(true);
- setstat(dp);
- }
- }
- if (tTd(37, 1))
- sm_dprintf("\n");
-
- switch (opt & 0xff)
- {
- case '7': /* force seven-bit input */
- SevenBitInput = atobool(val);
- break;
-
- case '8': /* handling of 8-bit input */
-#if MIME8TO7
- switch (*val)
- {
- case 'p': /* pass 8 bit, convert MIME */
- MimeMode = MM_CVTMIME|MM_PASS8BIT;
- break;
-
- case 'm': /* convert 8-bit, convert MIME */
- MimeMode = MM_CVTMIME|MM_MIME8BIT;
- break;
-
- case 's': /* strict adherence */
- MimeMode = MM_CVTMIME;
- break;
-
-# if 0
- case 'r': /* reject 8-bit, don't convert MIME */
- MimeMode = 0;
- break;
-
- case 'j': /* "just send 8" */
- MimeMode = MM_PASS8BIT;
- break;
-
- case 'a': /* encode 8 bit if available */
- MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
- break;
-
- case 'c': /* convert 8 bit to MIME, never 7 bit */
- MimeMode = MM_MIME8BIT;
- break;
-# endif /* 0 */
-
- default:
- syserr("Unknown 8-bit mode %c", *val);
- finis(false, true, EX_USAGE);
- }
-#else /* MIME8TO7 */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s requires MIME8TO7 support\n",
- OPTNAME);
-#endif /* MIME8TO7 */
- break;
-
- case 'A': /* set default alias file */
- if (val[0] == '\0')
- {
- char *al;
-
- SET_OPT_DEFAULT(al, "aliases");
- setalias(al);
- }
- else
- setalias(val);
- break;
-
- case 'a': /* look N minutes for "@:@" in alias file */
- if (val[0] == '\0')
- SafeAlias = 5 MINUTES;
- else
- SafeAlias = convtime(val, 'm');
- break;
-
- case 'B': /* substitution for blank character */
- SpaceSub = val[0];
- if (SpaceSub == '\0')
- SpaceSub = ' ';
- break;
-
- case 'b': /* min blocks free on queue fs/max msg size */
- p = strchr(val, '/');
- if (p != NULL)
- {
- *p++ = '\0';
- MaxMessageSize = atol(p);
- }
- MinBlocksFree = atol(val);
- break;
-
- case 'c': /* don't connect to "expensive" mailers */
- NoConnect = atobool(val);
- break;
-
- case 'C': /* checkpoint every N addresses */
- if (safe || CheckpointInterval > atoi(val))
- CheckpointInterval = atoi(val);
- break;
-
- case 'd': /* delivery mode */
- switch (*val)
- {
- case '\0':
- set_delivery_mode(SM_DELIVER, e);
- break;
-
- case SM_QUEUE: /* queue only */
- case SM_DEFER: /* queue only and defer map lookups */
- case SM_DELIVER: /* do everything */
- case SM_FORK: /* fork after verification */
-#if _FFR_DM_ONE
- /* deliver first TA in background, then queue */
- case SM_DM_ONE:
-#endif /* _FFR_DM_ONE */
- set_delivery_mode(*val, e);
- break;
-
- default:
- syserr("Unknown delivery mode %c", *val);
- finis(false, true, EX_USAGE);
- }
- break;
-
- case 'E': /* error message header/header file */
- if (*val != '\0')
- ErrMsgFile = newstr(val);
- break;
-
- case 'e': /* set error processing mode */
- switch (*val)
- {
- case EM_QUIET: /* be silent about it */
- case EM_MAIL: /* mail back */
- case EM_BERKNET: /* do berknet error processing */
- case EM_WRITE: /* write back (or mail) */
- case EM_PRINT: /* print errors normally (default) */
- e->e_errormode = *val;
- break;
- }
- break;
-
- case 'F': /* file mode */
- FileMode = atooct(val) & 0777;
- break;
-
- case 'f': /* save Unix-style From lines on front */
- SaveFrom = atobool(val);
- break;
-
- case 'G': /* match recipients against GECOS field */
- MatchGecos = atobool(val);
- break;
-
- case 'g': /* default gid */
- g_opt:
- if (isascii(*val) && isdigit(*val))
- DefGid = atoi(val);
- else
- {
- register struct group *gr;
-
- DefGid = -1;
- gr = getgrnam(val);
- if (gr == NULL)
- syserr("readcf: option %c: unknown group %s",
- opt, val);
- else
- DefGid = gr->gr_gid;
- }
- break;
-
- case 'H': /* help file */
- if (val[0] == '\0')
- {
- SET_OPT_DEFAULT(HelpFile, "helpfile");
- }
- else
- {
- CANONIFY(val);
- HelpFile = newstr(val);
- }
- break;
-
- case 'h': /* maximum hop count */
- MaxHopCount = atoi(val);
- break;
-
- case 'I': /* use internet domain name server */
-#if NAMED_BIND
- for (p = val; *p != 0; )
- {
- bool clearmode;
- char *q;
- struct resolverflags *rfp;
-
- while (*p == ' ')
- p++;
- if (*p == '\0')
- break;
- clearmode = false;
- if (*p == '-')
- clearmode = true;
- else if (*p != '+')
- p--;
- p++;
- q = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
- if (sm_strcasecmp(q, "HasWildcardMX") == 0)
- {
- HasWildcardMX = !clearmode;
- continue;
- }
- if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0)
- {
- WorkAroundBrokenAAAA = !clearmode;
- continue;
- }
- for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
- {
- if (sm_strcasecmp(q, rfp->rf_name) == 0)
- break;
- }
- if (rfp->rf_name == NULL)
- syserr("readcf: I option value %s unrecognized", q);
- else if (clearmode)
- _res.options &= ~rfp->rf_bits;
- else
- _res.options |= rfp->rf_bits;
- }
- if (tTd(8, 2))
- sm_dprintf("_res.options = %x, HasWildcardMX = %d\n",
- (unsigned int) _res.options, HasWildcardMX);
-#else /* NAMED_BIND */
- usrerr("name server (I option) specified but BIND not compiled in");
-#endif /* NAMED_BIND */
- break;
-
- case 'i': /* ignore dot lines in message */
- IgnrDot = atobool(val);
- break;
-
- case 'j': /* send errors in MIME (RFC 1341) format */
- SendMIMEErrors = atobool(val);
- break;
-
- case 'J': /* .forward search path */
- CANONIFY(val);
- ForwardPath = newstr(val);
- break;
-
- case 'k': /* connection cache size */
- MaxMciCache = atoi(val);
- if (MaxMciCache < 0)
- MaxMciCache = 0;
- break;
-
- case 'K': /* connection cache timeout */
- MciCacheTimeout = convtime(val, 'm');
- break;
-
- case 'l': /* use Errors-To: header */
- UseErrorsTo = atobool(val);
- break;
-
- case 'L': /* log level */
- if (safe || LogLevel < atoi(val))
- LogLevel = atoi(val);
- break;
-
- case 'M': /* define macro */
- sticky = false;
- mid = macid_parse(val, &ep);
- if (mid == 0)
- break;
- p = newstr(ep);
- if (!safe)
- cleanstrcpy(p, p, strlen(p) + 1);
- macdefine(&CurEnv->e_macro, A_TEMP, mid, p);
- break;
-
- case 'm': /* send to me too */
- MeToo = atobool(val);
- break;
-
- case 'n': /* validate RHS in newaliases */
- CheckAliases = atobool(val);
- break;
-
- /* 'N' available -- was "net name" */
-
- case 'O': /* daemon options */
- if (!setdaemonoptions(val))
- syserr("too many daemons defined (%d max)", MAXDAEMONS);
- break;
-
- case 'o': /* assume old style headers */
- if (atobool(val))
- CurEnv->e_flags |= EF_OLDSTYLE;
- else
- CurEnv->e_flags &= ~EF_OLDSTYLE;
- break;
-
- case 'p': /* select privacy level */
- p = val;
- for (;;)
- {
- register struct prival *pv;
- extern struct prival PrivacyValues[];
-
- while (isascii(*p) && (isspace(*p) || ispunct(*p)))
- p++;
- if (*p == '\0')
- break;
- val = p;
- while (isascii(*p) && isalnum(*p))
- p++;
- if (*p != '\0')
- *p++ = '\0';
-
- for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
- {
- if (sm_strcasecmp(val, pv->pv_name) == 0)
- break;
- }
- if (pv->pv_name == NULL)
- syserr("readcf: Op line: %s unrecognized", val);
- else
- PrivacyFlags |= pv->pv_flag;
- }
- sticky = false;
- break;
-
- case 'P': /* postmaster copy address for returned mail */
- PostMasterCopy = newstr(val);
- break;
-
- case 'q': /* slope of queue only function */
- QueueFactor = atoi(val);
- break;
-
- case 'Q': /* queue directory */
- if (val[0] == '\0')
- {
- QueueDir = "mqueue";
- }
- else
- {
- QueueDir = newstr(val);
- }
- if (RealUid != 0 && !safe)
- Warn_Q_option = true;
- break;
-
- case 'R': /* don't prune routes */
- DontPruneRoutes = atobool(val);
- break;
-
- case 'r': /* read timeout */
- if (subopt == NULL)
- inittimeouts(val, sticky);
- else
- settimeout(subopt, val, sticky);
- break;
-
- case 'S': /* status file */
- if (val[0] == '\0')
- {
- SET_OPT_DEFAULT(StatFile, "statistics");
- }
- else
- {
- CANONIFY(val);
- StatFile = newstr(val);
- }
- break;
-
- case 's': /* be super safe, even if expensive */
- if (tolower(*val) == 'i')
- SuperSafe = SAFE_INTERACTIVE;
- else if (tolower(*val) == 'p')
-#if MILTER
- SuperSafe = SAFE_REALLY_POSTMILTER;
-#else /* MILTER */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n");
-#endif /* MILTER */
- else
- SuperSafe = atobool(val) ? SAFE_REALLY : SAFE_NO;
- break;
-
- case 'T': /* queue timeout */
- p = strchr(val, '/');
- if (p != NULL)
- {
- *p++ = '\0';
- settimeout("queuewarn", p, sticky);
- }
- settimeout("queuereturn", val, sticky);
- break;
-
- case 't': /* time zone name */
- TimeZoneSpec = newstr(val);
- break;
-
- case 'U': /* location of user database */
- UdbSpec = newstr(val);
- break;
-
- case 'u': /* set default uid */
- for (p = val; *p != '\0'; p++)
- {
-# if _FFR_DOTTED_USERNAMES
- if (*p == '/' || *p == ':')
-# else /* _FFR_DOTTED_USERNAMES */
- if (*p == '.' || *p == '/' || *p == ':')
-# endif /* _FFR_DOTTED_USERNAMES */
- {
- *p++ = '\0';
- break;
- }
- }
- if (isascii(*val) && isdigit(*val))
- {
- DefUid = atoi(val);
- setdefuser();
- }
- else
- {
- register struct passwd *pw;
-
- DefUid = -1;
- pw = sm_getpwnam(val);
- if (pw == NULL)
- {
- syserr("readcf: option u: unknown user %s", val);
- break;
- }
- else
- {
- DefUid = pw->pw_uid;
- DefGid = pw->pw_gid;
- DefUser = newstr(pw->pw_name);
- }
- }
-
-# ifdef UID_MAX
- if (DefUid > UID_MAX)
- {
- syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
- (long)DefUid, (long)UID_MAX);
- break;
- }
-# endif /* UID_MAX */
-
- /* handle the group if it is there */
- if (*p == '\0')
- break;
- val = p;
- goto g_opt;
-
- case 'V': /* fallback MX host */
- if (val[0] != '\0')
- FallbackMX = newstr(val);
- break;
-
- case 'v': /* run in verbose mode */
- Verbose = atobool(val) ? 1 : 0;
- break;
-
- case 'w': /* if we are best MX, try host directly */
- TryNullMXList = atobool(val);
- break;
-
- /* 'W' available -- was wizard password */
-
- case 'x': /* load avg at which to auto-queue msgs */
- QueueLA = atoi(val);
- break;
-
- case 'X': /* load avg at which to auto-reject connections */
- RefuseLA = atoi(val);
- break;
-
- case O_DELAY_LA: /* load avg at which to delay connections */
- DelayLA = atoi(val);
- break;
-
- case 'y': /* work recipient factor */
- WkRecipFact = atoi(val);
- break;
-
- case 'Y': /* fork jobs during queue runs */
- ForkQueueRuns = atobool(val);
- break;
-
- case 'z': /* work message class factor */
- WkClassFact = atoi(val);
- break;
-
- case 'Z': /* work time factor */
- WkTimeFact = atoi(val);
- break;
-
-
-#if _FFR_QUEUE_GROUP_SORTORDER
- /* coordinate this with makequeue() */
-#endif /* _FFR_QUEUE_GROUP_SORTORDER */
- case O_QUEUESORTORD: /* queue sorting order */
- switch (*val)
- {
- case 'f': /* File Name */
- case 'F':
- QueueSortOrder = QSO_BYFILENAME;
- break;
-
- case 'h': /* Host first */
- case 'H':
- QueueSortOrder = QSO_BYHOST;
- break;
-
- case 'm': /* Modification time */
- case 'M':
- QueueSortOrder = QSO_BYMODTIME;
- break;
-
- case 'p': /* Priority order */
- case 'P':
- QueueSortOrder = QSO_BYPRIORITY;
- break;
-
- case 't': /* Submission time */
- case 'T':
- QueueSortOrder = QSO_BYTIME;
- break;
-
- case 'r': /* Random */
- case 'R':
- QueueSortOrder = QSO_RANDOM;
- break;
-
-#if _FFR_RHS
- case 's': /* Shuffled host name */
- case 'S':
- QueueSortOrder = QSO_BYSHUFFLE;
- break;
-#endif /* _FFR_RHS */
-
- case 'n': /* none */
- case 'N':
- QueueSortOrder = QSO_NONE;
- break;
-
- default:
- syserr("Invalid queue sort order \"%s\"", val);
- }
- break;
-
- case O_HOSTSFILE: /* pathname of /etc/hosts file */
- CANONIFY(val);
- HostsFile = newstr(val);
- break;
-
- case O_MQA: /* minimum queue age between deliveries */
- MinQueueAge = convtime(val, 'm');
- break;
-
- case O_DEFCHARSET: /* default character set for mimefying */
- DefaultCharSet = newstr(denlstring(val, true, true));
- break;
-
- case O_SSFILE: /* service switch file */
- CANONIFY(val);
- ServiceSwitchFile = newstr(val);
- break;
-
- case O_DIALDELAY: /* delay for dial-on-demand operation */
- DialDelay = convtime(val, 's');
- break;
-
- case O_NORCPTACTION: /* what to do if no recipient */
- if (sm_strcasecmp(val, "none") == 0)
- NoRecipientAction = NRA_NO_ACTION;
- else if (sm_strcasecmp(val, "add-to") == 0)
- NoRecipientAction = NRA_ADD_TO;
- else if (sm_strcasecmp(val, "add-apparently-to") == 0)
- NoRecipientAction = NRA_ADD_APPARENTLY_TO;
- else if (sm_strcasecmp(val, "add-bcc") == 0)
- NoRecipientAction = NRA_ADD_BCC;
- else if (sm_strcasecmp(val, "add-to-undisclosed") == 0)
- NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
- else
- syserr("Invalid NoRecipientAction: %s", val);
- break;
-
- case O_SAFEFILEENV: /* chroot() environ for writing to files */
- if (*val == '\0')
- break;
-
- /* strip trailing slashes */
- p = val + strlen(val) - 1;
- while (p >= val && *p == '/')
- *p-- = '\0';
-
- if (*val == '\0')
- break;
-
- SafeFileEnv = newstr(val);
- break;
-
- case O_MAXMSGSIZE: /* maximum message size */
- MaxMessageSize = atol(val);
- break;
-
- case O_COLONOKINADDR: /* old style handling of colon addresses */
- ColonOkInAddr = atobool(val);
- break;
-
- case O_MAXQUEUERUN: /* max # of jobs in a single queue run */
- MaxQueueRun = atoi(val);
- break;
-
- case O_MAXCHILDREN: /* max # of children of daemon */
- MaxChildren = atoi(val);
- break;
-
- case O_MAXQUEUECHILDREN: /* max # of children of daemon */
- MaxQueueChildren = atoi(val);
- break;
-
- case O_MAXRUNNERSPERQUEUE: /* max # runners in a queue group */
- MaxRunnersPerQueue = atoi(val);
- break;
-
- case O_NICEQUEUERUN: /* nice queue runs */
-#if !HASNICE
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: NiceQueueRun set on system that doesn't support nice()\n");
-#endif /* !HASNICE */
-
- /* XXX do we want to check the range? > 0 ? */
- NiceQueueRun = atoi(val);
- break;
-
- case O_SHMKEY: /* shared memory key */
-#if SM_CONF_SHM
- ShmKey = atol(val);
-#else /* SM_CONF_SHM */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
- OPTNAME);
-#endif /* SM_CONF_SHM */
- break;
-
- case O_SHMKEYFILE: /* shared memory key file */
-#if SM_CONF_SHM
- SET_STRING_EXP(ShmKeyFile);
-#else /* SM_CONF_SHM */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
- OPTNAME);
- break;
-#endif /* SM_CONF_SHM */
-
-#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 */
- (void) sm_strlcpy(buf, "@,;:\\()[]", sizeof(buf));
- if (strlen(val) < sizeof(buf) - 10)
- (void) sm_strlcat(buf, val, sizeof(buf));
- else
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: MustQuoteChars too long, ignored.\n");
- MustQuoteChars = newstr(buf);
- break;
-
- case O_SMTPGREETING: /* SMTP greeting message (old $e macro) */
- SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
- break;
-
- case O_UNIXFROM: /* UNIX From_ line (old $l macro) */
- UnixFromLine = newstr(munchstring(val, NULL, '\0'));
- break;
-
- case O_OPCHARS: /* operator characters (old $o macro) */
- if (OperatorChars != NULL)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: OperatorChars is being redefined.\n It should only be set before ruleset definitions.\n");
- OperatorChars = newstr(munchstring(val, NULL, '\0'));
- break;
-
- case O_DONTINITGRPS: /* don't call initgroups(3) */
- DontInitGroups = atobool(val);
- break;
-
- case O_SLFH: /* make sure from fits on one line */
- SingleLineFromHeader = atobool(val);
- break;
-
- case O_ABH: /* allow HELO commands with syntax errors */
- AllowBogusHELO = atobool(val);
- break;
-
- case O_CONNTHROT: /* connection rate throttle */
- ConnRateThrottle = atoi(val);
- break;
-
- case O_UGW: /* group writable files are unsafe */
- if (!atobool(val))
- {
- setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE,
- DontBlameSendmail);
- setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE,
- DontBlameSendmail);
- }
- break;
-
- case O_DBLBOUNCE: /* address to which to send double bounces */
- DoubleBounceAddr = newstr(val);
- break;
-
- case O_HSDIR: /* persistent host status directory */
- if (val[0] != '\0')
- {
- CANONIFY(val);
- HostStatDir = newstr(val);
- }
- break;
-
- case O_SINGTHREAD: /* single thread deliveries (requires hsdir) */
- SingleThreadDelivery = atobool(val);
- break;
-
- case O_RUNASUSER: /* run bulk of code as this user */
- for (p = val; *p != '\0'; p++)
- {
-# if _FFR_DOTTED_USERNAMES
- if (*p == '/' || *p == ':')
-# else /* _FFR_DOTTED_USERNAMES */
- if (*p == '.' || *p == '/' || *p == ':')
-# endif /* _FFR_DOTTED_USERNAMES */
- {
- *p++ = '\0';
- break;
- }
- }
- if (isascii(*val) && isdigit(*val))
- {
- if (can_setuid)
- RunAsUid = atoi(val);
- }
- else
- {
- register struct passwd *pw;
-
- pw = sm_getpwnam(val);
- if (pw == NULL)
- {
- syserr("readcf: option RunAsUser: unknown user %s", val);
- break;
- }
- else if (can_setuid)
- {
- if (*p == '\0')
- RunAsUserName = newstr(val);
- RunAsUid = pw->pw_uid;
- RunAsGid = pw->pw_gid;
- }
- else if (EffGid == pw->pw_gid)
- RunAsGid = pw->pw_gid;
- else if (UseMSP && *p == '\0')
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "WARNING: RunAsUser for MSP ignored, check group ids (egid=%d, want=%d)\n",
- (int) EffGid,
- (int) pw->pw_gid);
- }
-# ifdef UID_MAX
- if (RunAsUid > UID_MAX)
- {
- syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
- (long) RunAsUid, (long) UID_MAX);
- break;
- }
-# endif /* UID_MAX */
- if (*p != '\0')
- {
- if (isascii(*p) && isdigit(*p))
- {
- gid_t runasgid;
-
- runasgid = (gid_t) atoi(p);
- if (can_setuid || EffGid == runasgid)
- RunAsGid = runasgid;
- else if (UseMSP)
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "WARNING: RunAsUser for MSP ignored, check group ids (egid=%d, want=%d)\n",
- (int) EffGid,
- (int) runasgid);
- }
- else
- {
- register struct group *gr;
-
- gr = getgrnam(p);
- if (gr == NULL)
- syserr("readcf: option RunAsUser: unknown group %s",
- p);
- else if (can_setuid || EffGid == gr->gr_gid)
- RunAsGid = gr->gr_gid;
- else if (UseMSP)
- (void) sm_io_fprintf(smioout,
- SM_TIME_DEFAULT,
- "WARNING: RunAsUser for MSP ignored, check group ids (egid=%d, want=%d)\n",
- (int) EffGid,
- (int) gr->gr_gid);
- }
- }
- if (tTd(47, 5))
- sm_dprintf("readcf: RunAsUser = %d:%d\n",
- (int) RunAsUid, (int) RunAsGid);
- break;
-
- case O_DSN_RRT:
- RrtImpliesDsn = atobool(val);
- break;
-
- case O_PIDFILE:
- PSTRSET(PidFile, val);
- break;
-
- case O_DONTBLAMESENDMAIL:
- p = val;
- for (;;)
- {
- register struct dbsval *dbs;
- extern struct dbsval DontBlameSendmailValues[];
-
- while (isascii(*p) && (isspace(*p) || ispunct(*p)))
- p++;
- if (*p == '\0')
- break;
- val = p;
- while (isascii(*p) && isalnum(*p))
- p++;
- if (*p != '\0')
- *p++ = '\0';
-
- for (dbs = DontBlameSendmailValues;
- dbs->dbs_name != NULL; dbs++)
- {
- if (sm_strcasecmp(val, dbs->dbs_name) == 0)
- break;
- }
- if (dbs->dbs_name == NULL)
- syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
- else if (dbs->dbs_flag == DBS_SAFE)
- clrbitmap(DontBlameSendmail);
- else
- setbitn(dbs->dbs_flag, DontBlameSendmail);
- }
- sticky = false;
- break;
-
- case O_DPI:
- if (sm_strcasecmp(val, "loopback") == 0)
- DontProbeInterfaces = DPI_SKIPLOOPBACK;
- else if (atobool(val))
- DontProbeInterfaces = DPI_PROBENONE;
- else
- DontProbeInterfaces = DPI_PROBEALL;
- break;
-
- case O_MAXRCPT:
- MaxRcptPerMsg = atoi(val);
- break;
-
- case O_RCPTTHROT:
- BadRcptThrottle = atoi(val);
- break;
-
- case O_DEADLETTER:
- CANONIFY(val);
- PSTRSET(DeadLetterDrop, val);
- break;
-
-#if _FFR_DONTLOCKFILESFORREAD_OPTION
- case O_DONTLOCK:
- DontLockReadFiles = atobool(val);
- break;
-#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
-
- case O_MAXALIASRCSN:
- MaxAliasRecursion = atoi(val);
- break;
-
- case O_CNCTONLYTO:
- /* XXX should probably use gethostbyname */
-#if NETINET || NETINET6
- ConnectOnlyTo.sa.sa_family = AF_UNSPEC;
-# if NETINET6
- if (anynet_pton(AF_INET6, val,
- &ConnectOnlyTo.sin6.sin6_addr) != 1)
- ConnectOnlyTo.sa.sa_family = AF_INET6;
- else
-# endif /* NETINET6 */
-# if NETINET
- {
- ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
- if (ConnectOnlyTo.sin.sin_addr.s_addr != INADDR_NONE)
- ConnectOnlyTo.sa.sa_family = AF_INET;
- }
-
-# endif /* NETINET */
- if (ConnectOnlyTo.sa.sa_family == AF_UNSPEC)
- {
- syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
- val);
- break;
- }
-#endif /* NETINET || NETINET6 */
- break;
-
- case O_TRUSTUSER:
-# if !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING)
- if (!UseMSP)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "readcf: option TrustedUser may cause problems on systems\n which do not support fchown() if UseMSP is not set.\n");
-# endif /* !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING) */
- if (isascii(*val) && isdigit(*val))
- TrustedUid = atoi(val);
- else
- {
- register struct passwd *pw;
-
- TrustedUid = 0;
- pw = sm_getpwnam(val);
- if (pw == NULL)
- {
- syserr("readcf: option TrustedUser: unknown user %s", val);
- break;
- }
- else
- TrustedUid = pw->pw_uid;
- }
-
-# ifdef UID_MAX
- if (TrustedUid > UID_MAX)
- {
- syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
- (long) TrustedUid, (long) UID_MAX);
- TrustedUid = 0;
- }
-# endif /* UID_MAX */
- break;
-
- case O_MAXMIMEHDRLEN:
- p = strchr(val, '/');
- if (p != NULL)
- *p++ = '\0';
- MaxMimeHeaderLength = atoi(val);
- if (p != NULL && *p != '\0')
- MaxMimeFieldLength = atoi(p);
- else
- MaxMimeFieldLength = MaxMimeHeaderLength / 2;
-
- if (MaxMimeHeaderLength <= 0)
- MaxMimeHeaderLength = 0;
- else if (MaxMimeHeaderLength < 128)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
-
- if (MaxMimeFieldLength <= 0)
- MaxMimeFieldLength = 0;
- else if (MaxMimeFieldLength < 40)
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
-
- /*
- ** Headers field values now include leading space, so let's
- ** adjust the values to be "backward compatible".
- */
-
- if (MaxMimeHeaderLength > 0)
- MaxMimeHeaderLength++;
- if (MaxMimeFieldLength > 0)
- MaxMimeFieldLength++;
- break;
-
- case O_CONTROLSOCKET:
- PSTRSET(ControlSocketName, val);
- break;
-
- case O_MAXHDRSLEN:
- MaxHeadersLength = atoi(val);
-
- if (MaxHeadersLength > 0 &&
- MaxHeadersLength < (MAXHDRSLEN / 2))
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: MaxHeadersLength: headers length limit set lower than %d\n",
- (MAXHDRSLEN / 2));
- break;
-
- case O_PROCTITLEPREFIX:
- PSTRSET(ProcTitlePrefix, val);
- break;
-
-#if SASL
- case O_SASLINFO:
-# if _FFR_ALLOW_SASLINFO
- /*
- ** Allow users to select their own authinfo file
- ** under certain circumstances, otherwise just ignore
- ** the option. If the option isn't ignored, several
- ** commands don't work very well, e.g., mailq.
- ** However, this is not a "perfect" solution.
- ** If mail is queued, the authentication info
- ** will not be used in subsequent delivery attempts.
- ** 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)
- break;
-# endif /* _FFR_ALLOW_SASLINFO */
- PSTRSET(SASLInfo, val);
- break;
-
- case O_SASLMECH:
- if (AuthMechanisms != NULL)
- sm_free(AuthMechanisms); /* XXX */
- if (*val != '\0')
- AuthMechanisms = newstr(val);
- else
- AuthMechanisms = NULL;
- break;
-
- case O_SASLREALM:
- if (AuthRealm != NULL)
- sm_free(AuthRealm);
- if (*val != '\0')
- AuthRealm = newstr(val);
- else
- AuthRealm = NULL;
- break;
-
- case O_SASLOPTS:
- while (val != NULL && *val != '\0')
- {
- switch (*val)
- {
- case 'A':
- SASLOpts |= SASL_AUTH_AUTH;
- break;
-
- 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;
-
-# if SASL >= 20101
- case 'm':
- SASLOpts |= SASL_SEC_MUTUAL_AUTH;
- break;
-# endif /* SASL >= 20101 */
-
- case 'p':
- SASLOpts |= SASL_SEC_NOPLAINTEXT;
- break;
-
- case 'y':
- SASLOpts |= SASL_SEC_NOANONYMOUS;
- break;
-
- case ' ': /* ignore */
- case '\t': /* ignore */
- case ',': /* ignore */
- break;
-
- default:
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s unknown parameter '%c'\n",
- OPTNAME,
- (isascii(*val) &&
- isprint(*val))
- ? *val : '?');
- break;
- }
- ++val;
- val = strpbrk(val, ", \t");
- if (val != NULL)
- ++val;
- }
- break;
-
- case O_SASLBITS:
- MaxSLBits = atoi(val);
- break;
-
-#else /* SASL */
- case O_SASLINFO:
- case O_SASLMECH:
- case O_SASLREALM:
- case O_SASLOPTS:
- case O_SASLBITS:
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s requires SASL support (-DSASL)\n",
- OPTNAME);
- break;
-#endif /* SASL */
-
-#if STARTTLS
- case O_SRVCERTFILE:
- SET_STRING_EXP(SrvCertFile);
- case O_SRVKEYFILE:
- SET_STRING_EXP(SrvKeyFile);
- case O_CLTCERTFILE:
- SET_STRING_EXP(CltCertFile);
- case O_CLTKEYFILE:
- SET_STRING_EXP(CltKeyFile);
- case O_CACERTFILE:
- SET_STRING_EXP(CACertFile);
- case O_CACERTPATH:
- SET_STRING_EXP(CACertPath);
- case O_DHPARAMS:
- SET_STRING_EXP(DHParams);
-# if _FFR_TLS_1
- case O_DHPARAMS5:
- SET_STRING_EXP(DHParams5);
- case O_CIPHERLIST:
- SET_STRING_EXP(CipherList);
-# endif /* _FFR_TLS_1 */
- case O_CRLFILE:
-# if OPENSSL_VERSION_NUMBER > 0x00907000L
- SET_STRING_EXP(CRLFile);
-# else /* OPENSSL_VERSION_NUMBER > 0x00907000L */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s requires at least OpenSSL 0.9.7\n",
- OPTNAME);
- break;
-# endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
-
-# if _FFR_CRLPATH
- case O_CRLPATH:
-# if OPENSSL_VERSION_NUMBER > 0x00907000L
- SET_STRING_EXP(CRLPath);
-# else /* OPENSSL_VERSION_NUMBER > 0x00907000L */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s requires at least OpenSSL 0.9.7\n",
- OPTNAME);
- break;
-# endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
-# endif /* _FFR_CRLPATH */
-
- /*
- ** XXX How about options per daemon/client instead of globally?
- ** This doesn't work well for some options, e.g., no server cert,
- ** but fine for others.
- **
- ** XXX Some people may want different certs per server.
- **
- ** See also srvfeatures()
- */
-
- case O_TLS_SRV_OPTS:
- while (val != NULL && *val != '\0')
- {
- switch (*val)
- {
- case 'V':
- TLS_Srv_Opts |= TLS_I_NO_VRFY;
- break;
-# if _FFR_TLS_1
- /*
- ** Server without a cert? That works only if
- ** AnonDH is enabled as cipher, which is not in the
- ** default list. Hence the CipherList option must
- ** be available. Moreover: which clients support this
- ** besides sendmail with this setting?
- */
-
- case 'C':
- TLS_Srv_Opts &= ~TLS_I_SRV_CERT;
- break;
-# endif /* _FFR_TLS_1 */
- case ' ': /* ignore */
- case '\t': /* ignore */
- case ',': /* ignore */
- break;
- default:
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s unknown parameter '%c'\n",
- OPTNAME,
- (isascii(*val) &&
- isprint(*val))
- ? *val : '?');
- break;
- }
- ++val;
- val = strpbrk(val, ", \t");
- if (val != NULL)
- ++val;
- }
- break;
-
- case O_RANDFILE:
- PSTRSET(RandFile, 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_CRLFILE:
-# if _FFR_CRLPATH
- case O_CRLPATH:
-# endif /* _FFR_CRLPATH */
- case O_RANDFILE:
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s requires TLS support\n",
- OPTNAME);
- break;
-
-#endif /* STARTTLS */
-
- case O_CLIENTPORT:
- setclientoptions(val);
- break;
-
- case O_DF_BUFSIZE:
- DataFileBufferSize = atoi(val);
- break;
-
- case O_XF_BUFSIZE:
- XscriptFileBufferSize = atoi(val);
- break;
-
- case O_LDAPDEFAULTSPEC:
-#if LDAPMAP
- ldapmap_set_defaults(val);
-#else /* LDAPMAP */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
- OPTNAME);
-#endif /* LDAPMAP */
- break;
-
- case O_INPUTMILTER:
-#if MILTER
- InputFilterList = newstr(val);
-#else /* MILTER */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s requires Milter support (-DMILTER)\n",
- OPTNAME);
-#endif /* MILTER */
- break;
-
- case O_MILTER:
-#if MILTER
- milter_set_option(subopt, val, sticky);
-#else /* MILTER */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Warning: Option: %s requires Milter support (-DMILTER)\n",
- OPTNAME);
-#endif /* MILTER */
- break;
-
- case O_QUEUE_FILE_MODE: /* queue file mode */
- QueueFileMode = atooct(val) & 0777;
- break;
-
- case O_DLVR_MIN: /* deliver by minimum time */
- DeliverByMin = convtime(val, 's');
- break;
-
- /* modifiers {daemon_flags} for direct submissions */
- case O_DIRECTSUBMODIFIERS:
- {
- BITMAP256 m; /* ignored */
- extern ENVELOPE BlankEnvelope;
-
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{daemon_flags}"),
- getmodifiers(val, m));
- }
- break;
-
- case O_FASTSPLIT:
- FastSplit = atoi(val);
- break;
-
- case O_MBDB:
- Mbdb = newstr(val);
- break;
-
- case O_MSQ:
- UseMSP = atobool(val);
- break;
-
- case O_SOFTBOUNCE:
- SoftBounce = atobool(val);
- break;
-
- case O_REJECTLOGINTERVAL: /* time btwn log msgs while refusing */
- RejectLogInterval = convtime(val, 'h');
- break;
-
- case O_REQUIRES_DIR_FSYNC:
-#if REQUIRES_DIR_FSYNC
- RequiresDirfsync = atobool(val);
-#else /* REQUIRES_DIR_FSYNC */
- /* silently ignored... required for cf file option */
-#endif /* REQUIRES_DIR_FSYNC */
- break;
-
- case O_CONNECTION_RATE_WINDOW_SIZE:
- ConnectionRateWindowSize = convtime(val, 's');
- break;
-
- case O_FALLBACKSMARTHOST: /* fallback smart host */
- if (val[0] != '\0')
- FallbackSmartHost = newstr(val);
- break;
-
- case O_HELONAME:
- HeloName = newstr(val);
- break;
-
-#if _FFR_MEMSTAT
- case O_REFUSELOWMEM:
- RefuseLowMem = atoi(val);
- break;
- case O_QUEUELOWMEM:
- QueueLowMem = atoi(val);
- break;
- case O_MEMRESOURCE:
- MemoryResource = newstr(val);
- break;
-#endif /* _FFR_MEMSTAT */
-
- case O_MAXNOOPCOMMANDS:
- MaxNOOPCommands = atoi(val);
- break;
-
-#if _FFR_MSG_ACCEPT
- case O_MSG_ACCEPT:
- MessageAccept = newstr(val);
- break;
-#endif /* _FFR_MSG_ACCEPT */
-
-#if _FFR_QUEUE_RUN_PARANOIA
- case O_CHK_Q_RUNNERS:
- CheckQueueRunners = atoi(val);
- break;
-#endif /* _FFR_QUEUE_RUN_PARANOIA */
-
-#if _FFR_EIGHT_BIT_ADDR_OK
- case O_EIGHT_BIT_ADDR_OK:
- EightBitAddrOK = atobool(val);
- break;
-#endif /* _FFR_EIGHT_BIT_ADDR_OK */
-
-#if _FFR_ADDR_TYPE_MODES
- case O_ADDR_TYPE_MODES:
- AddrTypeModes = atobool(val);
- break;
-#endif /* _FFR_ADDR_TYPE_MODES */
-
- default:
- if (tTd(37, 1))
- {
- if (isascii(opt) && isprint(opt))
- sm_dprintf("Warning: option %c unknown\n", opt);
- else
- sm_dprintf("Warning: option 0x%x unknown\n", opt);
- }
- break;
- }
-
- /*
- ** 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);
-}
-/*
-** SETCLASS -- set a string into a class
-**
-** Parameters:
-** class -- the class to put the string in.
-** str -- the string to enter
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** puts the word into the symbol table.
-*/
-
-void
-setclass(class, str)
- int class;
- char *str;
-{
- register STAB *s;
-
- if ((str[0] & 0377) == MATCHCLASS)
- {
- int mid;
-
- str++;
- mid = macid(str);
- if (mid == 0)
- return;
-
- if (tTd(37, 8))
- sm_dprintf("setclass(%s, $=%s)\n",
- macname(class), macname(mid));
- copy_class(mid, class);
- }
- else
- {
- if (tTd(37, 8))
- sm_dprintf("setclass(%s, %s)\n", macname(class), str);
-
- s = stab(str, ST_CLASS, ST_ENTER);
- setbitn(bitidx(class), s->s_class);
- }
-}
-/*
-** MAKEMAPENTRY -- create a map entry
-**
-** Parameters:
-** line -- the config file line
-**
-** Returns:
-** A pointer to the map that has been created.
-** NULL if there was a syntax error.
-**
-** Side Effects:
-** Enters the map into the dictionary.
-*/
-
-MAP *
-makemapentry(line)
- char *line;
-{
- register char *p;
- char *mapname;
- char *classname;
- register STAB *s;
- STAB *class;
-
- for (p = line; isascii(*p) && isspace(*p); p++)
- continue;
- if (!(isascii(*p) && isalnum(*p)))
- {
- syserr("readcf: config K line: no map name");
- return NULL;
- }
-
- mapname = p;
- while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
- continue;
- if (*p != '\0')
- *p++ = '\0';
- while (isascii(*p) && isspace(*p))
- p++;
- if (!(isascii(*p) && isalnum(*p)))
- {
- syserr("readcf: config K line, map %s: no map class", mapname);
- return NULL;
- }
- classname = p;
- while (isascii(*++p) && isalnum(*p))
- continue;
- if (*p != '\0')
- *p++ = '\0';
- while (isascii(*p) && isspace(*p))
- p++;
-
- /* look up the class */
- class = stab(classname, ST_MAPCLASS, ST_FIND);
- if (class == NULL)
- {
- syserr("readcf: map %s: class %s not available", mapname,
- classname);
- return NULL;
- }
-
- /* enter the map */
- s = stab(mapname, ST_MAP, ST_ENTER);
- s->s_map.map_class = &class->s_mapclass;
- s->s_map.map_mname = newstr(mapname);
-
- if (class->s_mapclass.map_parse(&s->s_map, p))
- s->s_map.map_mflags |= MF_VALID;
-
- if (tTd(37, 5))
- {
- sm_dprintf("map %s, class %s, flags %lx, file %s,\n",
- s->s_map.map_mname, s->s_map.map_class->map_cname,
- s->s_map.map_mflags, s->s_map.map_file);
- sm_dprintf("\tapp %s, domain %s, rebuild %s\n",
- s->s_map.map_app, s->s_map.map_domain,
- s->s_map.map_rebuild);
- }
- return &s->s_map;
-}
-/*
-** STRTORWSET -- convert string to rewriting set number
-**
-** Parameters:
-** p -- the pointer to the string to decode.
-** endp -- if set, store the trailing delimiter here.
-** stabmode -- ST_ENTER to create this entry, ST_FIND if
-** it must already exist.
-**
-** Returns:
-** The appropriate ruleset number.
-** -1 if it is not valid (error already printed)
-*/
-
-int
-strtorwset(p, endp, stabmode)
- char *p;
- char **endp;
- int stabmode;
-{
- int ruleset;
- static int nextruleset = MAXRWSETS;
-
- while (isascii(*p) && isspace(*p))
- p++;
- if (!isascii(*p))
- {
- syserr("invalid ruleset name: \"%.20s\"", p);
- return -1;
- }
- if (isdigit(*p))
- {
- ruleset = strtol(p, endp, 10);
- if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
- {
- syserr("bad ruleset %d (%d max)",
- ruleset, MAXRWSETS / 2);
- ruleset = -1;
- }
- }
- else
- {
- STAB *s;
- char delim;
- char *q = NULL;
-
- q = p;
- while (*p != '\0' && isascii(*p) &&
- (isalnum(*p) || *p == '_'))
- p++;
- if (q == p || !(isascii(*q) && isalpha(*q)))
- {
- /* no valid characters */
- syserr("invalid ruleset name: \"%.20s\"", q);
- return -1;
- }
- while (isascii(*p) && isspace(*p))
- *p++ = '\0';
- delim = *p;
- if (delim != '\0')
- *p = '\0';
- s = stab(q, ST_RULESET, stabmode);
- if (delim != '\0')
- *p = delim;
-
- if (s == NULL)
- return -1;
-
- if (stabmode == ST_ENTER && delim == '=')
- {
- while (isascii(*++p) && isspace(*p))
- continue;
- if (!(isascii(*p) && isdigit(*p)))
- {
- syserr("bad ruleset definition \"%s\" (number required after `=')", q);
- ruleset = -1;
- }
- else
- {
- ruleset = strtol(p, endp, 10);
- if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
- {
- syserr("bad ruleset number %d in \"%s\" (%d max)",
- ruleset, q, MAXRWSETS / 2);
- ruleset = -1;
- }
- }
- }
- else
- {
- if (endp != NULL)
- *endp = p;
- if (s->s_ruleset >= 0)
- ruleset = s->s_ruleset;
- else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
- {
- syserr("%s: too many named rulesets (%d max)",
- q, MAXRWSETS / 2);
- ruleset = -1;
- }
- }
- 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)
- {
- s->s_ruleset = ruleset;
- }
- if (stabmode == ST_ENTER && ruleset >= 0)
- {
- char *h = NULL;
-
- if (RuleSetNames[ruleset] != NULL)
- sm_free(RuleSetNames[ruleset]); /* XXX */
- 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 */
- unsigned 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 },
-#define TO_LHLO 0x20
- { "lhlo", TO_LHLO },
-#define TO_AUTH 0x21
- { "auth", TO_AUTH },
-#define TO_STARTTLS 0x22
- { "starttls", TO_STARTTLS },
-#define TO_ACONNECT 0x23
- { "aconnect", TO_ACONNECT },
-#define TO_QUEUEWARN_DSN 0x24
- { "queuewarn.dsn", TO_QUEUEWARN_DSN },
-#define TO_QUEUERETURN_DSN 0x25
- { "queuereturn.dsn", TO_QUEUERETURN_DSN },
- { NULL, 0 },
-};
-
-
-static void
-settimeout(name, val, sticky)
- char *name;
- char *val;
- bool sticky;
-{
- register struct timeoutinfo *to;
- int i, addopts;
- time_t toval;
-
- if (tTd(37, 2))
- sm_dprintf("settimeout(%s = %s)", name, val);
-
- for (to = TimeOutTab; to->to_name != NULL; to++)
- {
- if (sm_strcasecmp(to->to_name, name) == 0)
- break;
- }
-
- if (to->to_name == NULL)
- {
- errno = 0; /* avoid bogus error text */
- syserr("settimeout: invalid timeout %s", name);
- return;
- }
-
- /*
- ** See if this option is preset for us.
- */
-
- if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
- {
- if (tTd(37, 2))
- sm_dprintf(" (ignored)\n");
- return;
- }
-
- if (tTd(37, 2))
- sm_dprintf("\n");
-
- toval = convtime(val, 'm');
- addopts = 0;
-
- 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_ACONNECT:
- TimeOuts.to_aconnect = 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;
- TimeOuts.to_q_warning[TOC_DSN] = toval;
- addopts = 2;
- 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_QUEUEWARN_DSN:
- toval = convtime(val, 'h');
- TimeOuts.to_q_warning[TOC_DSN] = 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;
- TimeOuts.to_q_return[TOC_DSN] = toval;
- addopts = 2;
- 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_QUEUERETURN_DSN:
- toval = convtime(val, 'd');
- TimeOuts.to_q_return[TOC_DSN] = 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;
- addopts = 2;
- 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;
- addopts = 2;
- 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;
-
- case TO_LHLO:
- TimeOuts.to_lhlo = toval;
- break;
-
-#if SASL
- case TO_AUTH:
- TimeOuts.to_auth = toval;
- break;
-#endif /* SASL */
-
-#if STARTTLS
- case TO_STARTTLS:
- TimeOuts.to_starttls = toval;
- break;
-#endif /* STARTTLS */
-
- default:
- syserr("settimeout: invalid timeout %s", name);
- break;
- }
-
- if (sticky)
- {
- for (i = 0; i <= addopts; i++)
- setbitn(to->to_code + i, 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.
-**
-** Side Effects:
-** Initializes the TimeOuts structure
-*/
-
-void
-inittimeouts(val, sticky)
- register char *val;
- bool sticky;
-{
- register char *p;
-
- if (tTd(37, 2))
- sm_dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
- if (val == NULL)
- {
- TimeOuts.to_connect = (time_t) 0 SECONDS;
- TimeOuts.to_aconnect = (time_t) 0 SECONDS;
- TimeOuts.to_iconnect = (time_t) 0 SECONDS;
- TimeOuts.to_initial = (time_t) 5 MINUTES;
- TimeOuts.to_helo = (time_t) 5 MINUTES;
- TimeOuts.to_mail = (time_t) 10 MINUTES;
- TimeOuts.to_rcpt = (time_t) 1 HOUR;
- TimeOuts.to_datainit = (time_t) 5 MINUTES;
- TimeOuts.to_datablock = (time_t) 1 HOUR;
- TimeOuts.to_datafinal = (time_t) 1 HOUR;
- TimeOuts.to_rset = (time_t) 5 MINUTES;
- TimeOuts.to_quit = (time_t) 2 MINUTES;
- TimeOuts.to_nextcommand = (time_t) 1 HOUR;
- TimeOuts.to_miscshort = (time_t) 2 MINUTES;
-#if IDENTPROTO
- TimeOuts.to_ident = (time_t) 5 SECONDS;
-#else /* IDENTPROTO */
- TimeOuts.to_ident = (time_t) 0 SECONDS;
-#endif /* IDENTPROTO */
- TimeOuts.to_fileopen = (time_t) 60 SECONDS;
- TimeOuts.to_control = (time_t) 2 MINUTES;
- TimeOuts.to_lhlo = (time_t) 2 MINUTES;
-#if SASL
- TimeOuts.to_auth = (time_t) 10 MINUTES;
-#endif /* SASL */
-#if STARTTLS
- TimeOuts.to_starttls = (time_t) 1 HOUR;
-#endif /* STARTTLS */
- if (tTd(37, 5))
- {
- sm_dprintf("Timeouts:\n");
- sm_dprintf(" connect = %ld\n",
- (long) TimeOuts.to_connect);
- sm_dprintf(" aconnect = %ld\n",
- (long) TimeOuts.to_aconnect);
- sm_dprintf(" initial = %ld\n",
- (long) TimeOuts.to_initial);
- sm_dprintf(" helo = %ld\n", (long) TimeOuts.to_helo);
- sm_dprintf(" mail = %ld\n", (long) TimeOuts.to_mail);
- sm_dprintf(" rcpt = %ld\n", (long) TimeOuts.to_rcpt);
- sm_dprintf(" datainit = %ld\n",
- (long) TimeOuts.to_datainit);
- sm_dprintf(" datablock = %ld\n",
- (long) TimeOuts.to_datablock);
- sm_dprintf(" datafinal = %ld\n",
- (long) TimeOuts.to_datafinal);
- sm_dprintf(" rset = %ld\n", (long) TimeOuts.to_rset);
- sm_dprintf(" quit = %ld\n", (long) TimeOuts.to_quit);
- sm_dprintf(" nextcommand = %ld\n",
- (long) TimeOuts.to_nextcommand);
- sm_dprintf(" miscshort = %ld\n",
- (long) TimeOuts.to_miscshort);
- sm_dprintf(" ident = %ld\n", (long) TimeOuts.to_ident);
- sm_dprintf(" fileopen = %ld\n",
- (long) TimeOuts.to_fileopen);
- sm_dprintf(" lhlo = %ld\n",
- (long) TimeOuts.to_lhlo);
- sm_dprintf(" control = %ld\n",
- (long) TimeOuts.to_control);
- }
- return;
- }
-
- for (;; val = p)
- {
- while (isascii(*val) && isspace(*val))
- val++;
- if (*val == '\0')
- break;
- for (p = val; *p != '\0' && *p != ','; p++)
- continue;
- if (*p != '\0')
- *p++ = '\0';
-
- if (isascii(*val) && isdigit(*val))
- {
- /* old syntax -- set everything */
- TimeOuts.to_mail = convtime(val, 'm');
- TimeOuts.to_rcpt = TimeOuts.to_mail;
- TimeOuts.to_datainit = TimeOuts.to_mail;
- 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
- {
- register char *q = strchr(val, ':');
-
- if (q == NULL && (q = strchr(val, '=')) == NULL)
- {
- /* syntax error */
- continue;
- }
- *q++ = '\0';
- settimeout(val, q, sticky);
- }
- }
-}
diff --git a/contrib/sendmail/src/recipient.c b/contrib/sendmail/src/recipient.c
deleted file mode 100644
index 4064632..0000000
--- a/contrib/sendmail/src/recipient.c
+++ /dev/null
@@ -1,2072 +0,0 @@
-/*
- * Copyright (c) 1998-2003, 2006 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: recipient.c,v 8.349 2007/07/10 17:01:22 ca Exp $")
-
-static void includetimeout __P((int));
-static ADDRESS *self_reference __P((ADDRESS *));
-static int sortexpensive __P((ADDRESS *, ADDRESS *));
-static int sortbysignature __P((ADDRESS *, ADDRESS *));
-static int sorthost __P((ADDRESS *, ADDRESS *));
-
-typedef int sortfn_t __P((ADDRESS *, ADDRESS *));
-
-/*
-** SORTHOST -- strcmp()-like func for host portion of an ADDRESS
-**
-** Parameters:
-** xx -- first ADDRESS
-** yy -- second ADDRESS
-**
-** Returns:
-** <0 when xx->q_host is less than yy->q_host
-** >0 when xx->q_host is greater than yy->q_host
-** 0 when equal
-*/
-
-static int
-sorthost(xx, yy)
- register ADDRESS *xx;
- register ADDRESS *yy;
-{
-#if _FFR_HOST_SORT_REVERSE
- /* XXX maybe compare hostnames from the end? */
- return sm_strrevcasecmp(xx->q_host, yy->q_host);
-#else /* _FFR_HOST_SORT_REVERSE */
- return sm_strcasecmp(xx->q_host, yy->q_host);
-#endif /* _FFR_HOST_SORT_REVERSE */
-}
-
-/*
-** SORTEXPENSIVE -- strcmp()-like func for expensive mailers
-**
-** The mailer has been noted already as "expensive" for 'xx'. This
-** will give a result relative to 'yy'. Expensive mailers get rated
-** "greater than" non-expensive mailers because during the delivery phase
-** it will get queued -- no use it getting in the way of less expensive
-** recipients. We avoid an MX RR lookup when both 'xx' and 'yy' are
-** expensive since an MX RR lookup happens when extracted from the queue
-** later.
-**
-** Parameters:
-** xx -- first ADDRESS
-** yy -- second ADDRESS
-**
-** Returns:
-** <0 when xx->q_host is less than yy->q_host and both are
-** expensive
-** >0 when xx->q_host is greater than yy->q_host, or when
-** 'yy' is non-expensive
-** 0 when equal (by expense and q_host)
-*/
-
-static int
-sortexpensive(xx, yy)
- ADDRESS *xx;
- ADDRESS *yy;
-{
- if (!bitnset(M_EXPENSIVE, yy->q_mailer->m_flags))
- return 1; /* xx should go later */
-#if _FFR_HOST_SORT_REVERSE
- /* XXX maybe compare hostnames from the end? */
- return sm_strrevcasecmp(xx->q_host, yy->q_host);
-#else /* _FFR_HOST_SORT_REVERSE */
- return sm_strcasecmp(xx->q_host, yy->q_host);
-#endif /* _FFR_HOST_SORT_REVERSE */
-}
-
-/*
-** SORTBYSIGNATURE -- a strcmp()-like func for q_mailer and q_host in ADDRESS
-**
-** Parameters:
-** xx -- first ADDRESS
-** yy -- second ADDRESS
-**
-** Returns:
-** 0 when the "signature"'s are same
-** <0 when xx->q_signature is less than yy->q_signature
-** >0 when xx->q_signature is greater than yy->q_signature
-**
-** Side Effect:
-** May set ADDRESS pointer for q_signature if not already set.
-*/
-
-static int
-sortbysignature(xx, yy)
- ADDRESS *xx;
- ADDRESS *yy;
-{
- register int ret;
-
- /* Let's avoid redoing the signature over and over again */
- if (xx->q_signature == NULL)
- xx->q_signature = hostsignature(xx->q_mailer, xx->q_host);
- if (yy->q_signature == NULL)
- yy->q_signature = hostsignature(yy->q_mailer, yy->q_host);
- ret = strcmp(xx->q_signature, yy->q_signature);
-
- /*
- ** If the two signatures are the same then we will return a sort
- ** value based on 'q_user'. But note that we have reversed xx and yy
- ** on purpose. This additional compare helps reduce the number of
- ** sameaddr() calls and loops in recipient() for the case when
- ** the rcpt list has been provided already in-order.
- */
-
- if (ret == 0)
- return strcmp(yy->q_user, xx->q_user);
- else
- return ret;
-}
-
-/*
-** SENDTOLIST -- Designate a send list.
-**
-** The parameter is a comma-separated list of people to send to.
-** This routine arranges to send to all of them.
-**
-** Parameters:
-** list -- the send list.
-** ctladdr -- the address template for the person to
-** send to -- effective uid/gid are important.
-** This is typically the alias that caused this
-** expansion.
-** sendq -- a pointer to the head of a queue to put
-** these people into.
-** aliaslevel -- the current alias nesting depth -- to
-** diagnose loops.
-** e -- the envelope in which to add these recipients.
-**
-** Returns:
-** The number of addresses actually on the list.
-*/
-
-/* q_flags bits inherited from ctladdr */
-#define QINHERITEDBITS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY)
-
-int
-sendtolist(list, ctladdr, sendq, aliaslevel, e)
- char *list;
- ADDRESS *ctladdr;
- ADDRESS **sendq;
- int aliaslevel;
- register ENVELOPE *e;
-{
- register char *p;
- register ADDRESS *SM_NONVOLATILE al; /* list of addresses to send to */
- SM_NONVOLATILE char delimiter; /* the address delimiter */
- SM_NONVOLATILE int naddrs;
- SM_NONVOLATILE int i;
- char *endp;
- char *oldto = e->e_to;
- char *SM_NONVOLATILE bufp;
- char buf[MAXNAME + 1];
-
- if (list == NULL)
- {
- syserr("sendtolist: null list");
- return 0;
- }
-
- if (tTd(25, 1))
- {
- sm_dprintf("sendto: %s\n ctladdr=", list);
- printaddr(sm_debug_file(), ctladdr, false);
- }
-
- /* heuristic to determine old versus new style addresses */
- if (ctladdr == NULL &&
- (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) || ctladdr != NULL)
- delimiter = ',';
-
- al = NULL;
- 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 = sm_malloc_x(i);
- endp = bufp + i;
-
- SM_TRY
- {
- (void) sm_strlcpy(bufp, denlstring(list, false, true), i);
-
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
- for (p = bufp; *p != '\0'; )
- {
- auto char *delimptr;
- register ADDRESS *a;
-
- SM_ASSERT(p < endp);
-
- /* parse the address */
- while ((isascii(*p) && isspace(*p)) || *p == ',')
- p++;
- SM_ASSERT(p < endp);
- a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter,
- &delimptr, e, true);
- p = delimptr;
- SM_ASSERT(p < endp);
- if (a == NULL)
- continue;
- a->q_next = al;
- a->q_alias = ctladdr;
-
- /* arrange to inherit attributes from parent */
- if (ctladdr != NULL)
- {
- ADDRESS *b;
-
- /* self reference test */
- if (sameaddr(ctladdr, a))
- {
- if (tTd(27, 5))
- {
- sm_dprintf("sendtolist: QSELFREF ");
- printaddr(sm_debug_file(), ctladdr, false);
- }
- ctladdr->q_flags |= QSELFREF;
- }
-
- /* check for address loops */
- b = self_reference(a);
- if (b != NULL)
- {
- b->q_flags |= QSELFREF;
- if (tTd(27, 5))
- {
- sm_dprintf("sendtolist: QSELFREF ");
- printaddr(sm_debug_file(), b, false);
- }
- if (a != b)
- {
- if (tTd(27, 5))
- {
- sm_dprintf("sendtolist: QS_DONTSEND ");
- printaddr(sm_debug_file(), a, false);
- }
- a->q_state = QS_DONTSEND;
- b->q_flags |= a->q_flags & QNOTREMOTE;
- continue;
- }
- }
-
- /* full name */
- if (a->q_fullname == NULL)
- a->q_fullname = ctladdr->q_fullname;
-
- /* various flag bits */
- a->q_flags &= ~QINHERITEDBITS;
- a->q_flags |= ctladdr->q_flags & QINHERITEDBITS;
-
- /* DSN recipient information */
- a->q_finalrcpt = ctladdr->q_finalrcpt;
- a->q_orcpt = ctladdr->q_orcpt;
- }
-
- al = a;
- }
-
- /* arrange to send to everyone on the local send list */
- while (al != NULL)
- {
- register ADDRESS *a = al;
-
- al = a->q_next;
- a = recipient(a, sendq, aliaslevel, e);
- naddrs++;
- }
- }
- SM_FINALLY
- {
- e->e_to = oldto;
- if (bufp != buf)
- sm_free(bufp);
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL);
- }
- SM_END_TRY
- return naddrs;
-}
-
-#if MILTER
-/*
-** REMOVEFROMLIST -- Remove addresses from a send list.
-**
-** The parameter is a comma-separated list of recipients to remove.
-** Note that it only deletes matching addresses. If those addresses
-** have been expanded 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;
-{
- SM_NONVOLATILE char delimiter; /* the address delimiter */
- SM_NONVOLATILE int naddrs;
- SM_NONVOLATILE int i;
- char *p;
- char *oldto = e->e_to;
- char *SM_NONVOLATILE bufp;
- char buf[MAXNAME + 1];
-
- if (list == NULL)
- {
- syserr("removefromlist: null list");
- return 0;
- }
-
- if (tTd(25, 1))
- sm_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 = sm_malloc_x(i);
-
- SM_TRY
- {
- (void) sm_strlcpy(bufp, denlstring(list, false, true), i);
-
-#if _FFR_ADDR_TYPE_MODES
- if (AddrTypeModes)
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
- "e r d");
- else
-#endif /* _FFR_ADDR_TYPE_MODES */
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
- for (p = bufp; *p != '\0'; )
- {
- ADDRESS a; /* parsed address to be removed */
- ADDRESS *q;
- ADDRESS **pq;
- char *delimptr;
-
- /* parse the address */
- while ((isascii(*p) && isspace(*p)) || *p == ',')
- p++;
- if (parseaddr(p, &a, RF_COPYALL|RF_RM_ADDR,
- delimiter, &delimptr, e, true) == 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) ||
- strcmp(q->q_paddr, a.q_paddr) == 0))
- {
- if (tTd(25, 5))
- {
- sm_dprintf("removefromlist: QS_REMOVED ");
- printaddr(sm_debug_file(), &a, false);
- }
- q->q_state = QS_REMOVED;
- naddrs++;
- break;
- }
- }
- }
- }
- SM_FINALLY
- {
- e->e_to = oldto;
- if (bufp != buf)
- sm_free(bufp);
- macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL);
- }
- SM_END_TRY
- return naddrs;
-}
-#endif /* MILTER */
-
-/*
-** RECIPIENT -- Designate a message recipient
-** Saves the named person for future mailing (after some checks).
-**
-** Parameters:
-** new -- the (preparsed) address header for the recipient.
-** sendq -- a pointer to the head of a queue to put the
-** recipient in. Duplicate suppression is done
-** in this queue.
-** aliaslevel -- the current alias nesting depth.
-** e -- the current envelope.
-**
-** Returns:
-** The actual address in the queue. This will be "a" if
-** the address is not a duplicate, else the original address.
-**
-*/
-
-ADDRESS *
-recipient(new, sendq, aliaslevel, e)
- register ADDRESS *new;
- register ADDRESS **sendq;
- int aliaslevel;
- register ENVELOPE *e;
-{
- register ADDRESS *q;
- ADDRESS **pq;
- ADDRESS **prev;
- register struct mailer *m;
- register char *p;
- int i, buflen;
- bool quoted; /* set if the addr has a quote bit */
- bool insert;
- int findusercount;
- bool initialdontsend;
- char *buf;
- char buf0[MAXNAME + 1]; /* unquoted image of the user name */
- sortfn_t *sortfn;
-
- p = NULL;
- quoted = false;
- insert = false;
- findusercount = 0;
- initialdontsend = QS_IS_DEAD(new->q_state);
- e->e_to = new->q_paddr;
- m = new->q_mailer;
- errno = 0;
- if (aliaslevel == 0)
- new->q_flags |= QPRIMARY;
- if (tTd(26, 1))
- {
- sm_dprintf("\nrecipient (%d): ", aliaslevel);
- printaddr(sm_debug_file(), new, false);
- }
-
- /* if this is primary, use it as original recipient */
- if (new->q_alias == NULL)
- {
- if (e->e_origrcpt == NULL)
- e->e_origrcpt = new->q_paddr;
- else if (e->e_origrcpt != new->q_paddr)
- e->e_origrcpt = "";
- }
-
- /* find parent recipient for finalrcpt and orcpt */
- for (q = new; q->q_alias != NULL; q = q->q_alias)
- continue;
-
- /* find final recipient DSN address */
- if (new->q_finalrcpt == NULL &&
- e->e_from.q_mailer != NULL)
- {
- char frbuf[MAXLINE];
-
- p = e->e_from.q_mailer->m_addrtype;
- if (p == NULL)
- p = "rfc822";
- if (sm_strcasecmp(p, "rfc822") != 0)
- {
- (void) sm_snprintf(frbuf, sizeof(frbuf), "%s; %.800s",
- q->q_mailer->m_addrtype,
- q->q_user);
- }
- else if (strchr(q->q_user, '@') != NULL)
- {
- (void) sm_snprintf(frbuf, sizeof(frbuf), "%s; %.800s",
- p, q->q_user);
- }
- else if (strchr(q->q_paddr, '@') != NULL)
- {
- char *qp;
- bool b;
-
- qp = q->q_paddr;
-
- /* strip brackets from address */
- b = false;
- if (*qp == '<')
- {
- b = qp[strlen(qp) - 1] == '>';
- if (b)
- qp[strlen(qp) - 1] = '\0';
- qp++;
- }
- (void) sm_snprintf(frbuf, sizeof(frbuf), "%s; %.800s",
- p, qp);
-
- /* undo damage */
- if (b)
- qp[strlen(qp)] = '>';
- }
- else
- {
- (void) sm_snprintf(frbuf, sizeof(frbuf),
- "%s; %.700s@%.100s",
- p, q->q_user, MyHostName);
- }
- new->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool, frbuf);
- }
-
-#if _FFR_GEN_ORCPT
- /* set ORCPT DSN arg if not already set */
- if (new->q_orcpt == NULL)
- {
- /* check for an existing ORCPT */
- if (q->q_orcpt != NULL)
- new->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) sm_strlcpyn(obuf, sizeof(obuf), 2, p, ";");
-
- 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), "=");
-
- if (sm_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')
- new->q_orcpt =
- sm_rpool_strdup_x(e->e_rpool, obuf);
- }
- }
-#endif /* _FFR_GEN_ORCPT */
-
- /* break aliasing loops */
- if (aliaslevel > MaxAliasRecursion)
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.4.6";
- if (new->q_alias != NULL)
- {
- new->q_alias->q_state = QS_BADADDR;
- new->q_alias->q_status = "5.4.6";
- }
- if ((SuprErrs || !LogUsrErrs) && LogLevel > 0)
- {
- sm_syslog(LOG_ERR, e->e_id,
- "aliasing/forwarding loop broken: %s (%d aliases deep; %d max)",
- FileName != NULL ? FileName : "", aliaslevel,
- MaxAliasRecursion);
- }
- usrerrenh(new->q_status,
- "554 aliasing/forwarding loop broken (%d aliases deep; %d max)",
- aliaslevel, MaxAliasRecursion);
- return new;
- }
-
- /*
- ** Finish setting up address structure.
- */
-
- /* get unquoted user for file, program or user.name check */
- i = strlen(new->q_user);
- if (i >= sizeof(buf0))
- {
- buflen = i + 1;
- buf = xalloc(buflen);
- }
- else
- {
- buf = buf0;
- buflen = sizeof(buf0);
- }
- (void) sm_strlcpy(buf, new->q_user, buflen);
- for (p = buf; *p != '\0' && !quoted; p++)
- {
- if (*p == '\\')
- quoted = true;
- }
- stripquotes(buf);
-
- /* check for direct mailing to restricted mailers */
- if (m == ProgMailer)
- {
- if (new->q_alias == NULL || UseMSP ||
- bitset(EF_UNSAFE, e->e_flags))
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.7.1";
- usrerrenh(new->q_status,
- "550 Cannot mail directly to programs");
- }
- else if (bitset(QBOGUSSHELL, new->q_alias->q_flags))
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.7.1";
- if (new->q_alias->q_ruser == NULL)
- usrerrenh(new->q_status,
- "550 UID %d is an unknown user: cannot mail to programs",
- new->q_alias->q_uid);
- else
- usrerrenh(new->q_status,
- "550 User %s@%s doesn't have a valid shell for mailing to programs",
- new->q_alias->q_ruser, MyHostName);
- }
- else if (bitset(QUNSAFEADDR, new->q_alias->q_flags))
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.7.1";
- new->q_rstatus = "550 Unsafe for mailing to programs";
- usrerrenh(new->q_status,
- "550 Address %s is unsafe for mailing to programs",
- new->q_alias->q_paddr);
- }
- }
-
- /*
- ** Look up this person in the recipient list.
- ** 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 QS_DUPLICATE state will be set in the send list.
- ** [Please note: the emphasis is on "hack."]
- */
-
- prev = NULL;
-
- /*
- ** If this message is going to the queue or FastSplit is set
- ** and it is the first try and the envelope hasn't split, then we
- ** avoid doing an MX RR lookup now because one will be done when the
- ** message is extracted from the queue later. It can go to the queue
- ** because all messages are going to the queue or this mailer of
- ** the current recipient is marked expensive.
- */
-
- if (UseMSP || WILL_BE_QUEUED(e->e_sendmode) ||
- (!bitset(EF_SPLIT, e->e_flags) && e->e_ntries == 0 &&
- FastSplit > 0))
- sortfn = sorthost;
- else if (NoConnect && bitnset(M_EXPENSIVE, new->q_mailer->m_flags))
- sortfn = sortexpensive;
- else
- sortfn = sortbysignature;
-
- for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
- {
- /*
- ** If address is "less than" it should be inserted now.
- ** If address is "greater than" current comparison it'll
- ** insert later in the list; so loop again (if possible).
- ** If address is "equal" (different equal than sameaddr()
- ** call) then check if sameaddr() will be true.
- ** Because this list is now sorted, it'll mean fewer
- ** comparisons and fewer loops which is important for more
- ** recipients.
- */
-
- i = (*sortfn)(new, q);
- if (i == 0) /* equal */
- {
- /*
- ** Sortbysignature() has said that the two have
- ** equal MX RR's and the same user. Calling sameaddr()
- ** now checks if the two hosts are as identical as the
- ** MX RR's are (which might not be the case)
- ** before saying these are the identical addresses.
- */
-
- if (sameaddr(q, new) &&
- (bitset(QRCPTOK, q->q_flags) ||
- !bitset(QPRIMARY, q->q_flags)))
- {
- if (tTd(26, 1))
- {
- sm_dprintf("%s in sendq: ",
- new->q_paddr);
- printaddr(sm_debug_file(), q, false);
- }
- if (!bitset(QPRIMARY, q->q_flags))
- {
- if (!QS_IS_DEAD(new->q_state))
- message("duplicate suppressed");
- else
- q->q_state = QS_DUPLICATE;
- q->q_flags |= new->q_flags;
- }
- else if (bitset(QSELFREF, q->q_flags)
- || q->q_state == QS_REMOVED)
- {
- /*
- ** If an earlier milter removed the
- ** address, a later one can still add
- ** it back.
- */
-
- q->q_state = new->q_state;
- q->q_flags |= new->q_flags;
- }
- new = q;
- goto done;
- }
- }
- else if (i < 0) /* less than */
- {
- insert = true;
- break;
- }
- prev = pq;
- }
-
- /* pq should point to an address, never NULL */
- SM_ASSERT(pq != NULL);
-
- /* add address on list */
- if (insert)
- {
- /*
- ** insert before 'pq'. Only possible when at least 1
- ** ADDRESS is in the list already.
- */
-
- new->q_next = *pq;
- if (prev == NULL)
- *sendq = new; /* To be the first ADDRESS */
- else
- (*prev)->q_next = new;
- }
- else
- {
- /*
- ** Place in list at current 'pq' position. Possible
- ** when there are 0 or more ADDRESS's in the list.
- */
-
- new->q_next = NULL;
- *pq = new;
- }
-
- /* added a new address: clear split flag */
- e->e_flags &= ~EF_SPLIT;
-
- /*
- ** Alias the name and handle special mailer types.
- */
-
- trylocaluser:
- if (tTd(29, 7))
- {
- sm_dprintf("at trylocaluser: ");
- printaddr(sm_debug_file(), new, false);
- }
-
- if (!QS_IS_OK(new->q_state))
- {
- if (QS_IS_UNDELIVERED(new->q_state))
- e->e_nrcpts++;
- goto testselfdestruct;
- }
-
- if (m == InclMailer)
- {
- new->q_state = QS_INCLUDED;
- if (new->q_alias == NULL || UseMSP ||
- bitset(EF_UNSAFE, e->e_flags))
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.7.1";
- usrerrenh(new->q_status,
- "550 Cannot mail directly to :include:s");
- }
- else
- {
- int ret;
-
- message("including file %s", new->q_user);
- ret = include(new->q_user, false, new,
- sendq, aliaslevel, e);
- if (transienterror(ret))
- {
- if (LogLevel > 2)
- sm_syslog(LOG_ERR, e->e_id,
- "include %s: transient error: %s",
- shortenstring(new->q_user,
- MAXSHORTSTR),
- sm_errstring(ret));
- new->q_state = QS_QUEUEUP;
- usrerr("451 4.2.4 Cannot open %s: %s",
- shortenstring(new->q_user,
- MAXSHORTSTR),
- sm_errstring(ret));
- }
- else if (ret != 0)
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.2.4";
- usrerrenh(new->q_status,
- "550 Cannot open %s: %s",
- shortenstring(new->q_user,
- MAXSHORTSTR),
- sm_errstring(ret));
- }
- }
- }
- else if (m == FileMailer)
- {
- /* check if allowed */
- if (new->q_alias == NULL || UseMSP ||
- bitset(EF_UNSAFE, e->e_flags))
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.7.1";
- usrerrenh(new->q_status,
- "550 Cannot mail directly to files");
- }
- else if (bitset(QBOGUSSHELL, new->q_alias->q_flags))
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.7.1";
- if (new->q_alias->q_ruser == NULL)
- usrerrenh(new->q_status,
- "550 UID %d is an unknown user: cannot mail to files",
- new->q_alias->q_uid);
- else
- usrerrenh(new->q_status,
- "550 User %s@%s doesn't have a valid shell for mailing to files",
- new->q_alias->q_ruser, MyHostName);
- }
- else if (bitset(QUNSAFEADDR, new->q_alias->q_flags))
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.7.1";
- new->q_rstatus = "550 Unsafe for mailing to files";
- usrerrenh(new->q_status,
- "550 Address %s is unsafe for mailing to files",
- new->q_alias->q_paddr);
- }
- }
-
- /* try aliasing */
- if (!quoted && QS_IS_OK(new->q_state) &&
- bitnset(M_ALIASABLE, m->m_flags))
- alias(new, sendq, aliaslevel, e);
-
-#if USERDB
- /* if not aliased, look it up in the user database */
- if (!bitset(QNOTREMOTE, new->q_flags) &&
- QS_IS_SENDABLE(new->q_state) &&
- bitnset(M_CHECKUDB, m->m_flags))
- {
- if (udbexpand(new, sendq, aliaslevel, e) == EX_TEMPFAIL)
- {
- new->q_state = QS_QUEUEUP;
- if (e->e_message == NULL)
- e->e_message = sm_rpool_strdup_x(e->e_rpool,
- "Deferred: user database error");
- if (new->q_message == NULL)
- new->q_message = "Deferred: user database error";
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id,
- "deferred: udbexpand: %s",
- sm_errstring(errno));
- message("queued (user database error): %s",
- sm_errstring(errno));
- e->e_nrcpts++;
- goto testselfdestruct;
- }
- }
-#endif /* USERDB */
-
- /*
- ** If we have a level two config file, then pass the name through
- ** Ruleset 5 before sending it off. Ruleset 5 has the right
- ** to rewrite it to another mailer. This gives us a hook
- ** after local aliasing has been done.
- */
-
- if (tTd(29, 5))
- {
- sm_dprintf("recipient: testing local? cl=%d, rr5=%p\n\t",
- ConfigLevel, RewriteRules[5]);
- printaddr(sm_debug_file(), new, false);
- }
- if (ConfigLevel >= 2 && RewriteRules[5] != NULL &&
- bitnset(M_TRYRULESET5, m->m_flags) &&
- !bitset(QNOTREMOTE, new->q_flags) &&
- QS_IS_OK(new->q_state))
- {
- maplocaluser(new, sendq, aliaslevel + 1, e);
- }
-
- /*
- ** If it didn't get rewritten to another mailer, go ahead
- ** and deliver it.
- */
-
- if (QS_IS_OK(new->q_state) &&
- bitnset(M_HASPWENT, m->m_flags))
- {
- auto bool fuzzy;
- SM_MBDB_T user;
- int status;
-
- /* warning -- finduser may trash buf */
- status = finduser(buf, &fuzzy, &user);
- switch (status)
- {
- case EX_TEMPFAIL:
- new->q_state = QS_QUEUEUP;
- new->q_status = "4.5.2";
- giveresponse(EX_TEMPFAIL, new->q_status, m, NULL,
- new->q_alias, (time_t) 0, e, new);
- break;
- default:
- new->q_state = QS_BADADDR;
- new->q_status = "5.1.1";
- new->q_rstatus = "550 5.1.1 User unknown";
- giveresponse(EX_NOUSER, new->q_status, m, NULL,
- new->q_alias, (time_t) 0, e, new);
- break;
- case EX_OK:
- if (fuzzy)
- {
- /* name was a fuzzy match */
- new->q_user = sm_rpool_strdup_x(e->e_rpool,
- user.mbdb_name);
- if (findusercount++ > 3)
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.4.6";
- usrerrenh(new->q_status,
- "554 aliasing/forwarding loop for %s broken",
- user.mbdb_name);
- goto done;
- }
-
- /* see if it aliases */
- (void) sm_strlcpy(buf, user.mbdb_name, buflen);
- goto trylocaluser;
- }
- if (*user.mbdb_homedir == '\0')
- new->q_home = NULL;
- else if (strcmp(user.mbdb_homedir, "/") == 0)
- new->q_home = "";
- else
- new->q_home = sm_rpool_strdup_x(e->e_rpool,
- user.mbdb_homedir);
- if (user.mbdb_uid != SM_NO_UID)
- {
- new->q_uid = user.mbdb_uid;
- new->q_gid = user.mbdb_gid;
- new->q_flags |= QGOODUID;
- }
- new->q_ruser = sm_rpool_strdup_x(e->e_rpool,
- user.mbdb_name);
- if (user.mbdb_fullname[0] != '\0')
- new->q_fullname = sm_rpool_strdup_x(e->e_rpool,
- user.mbdb_fullname);
- if (!usershellok(user.mbdb_name, user.mbdb_shell))
- {
- new->q_flags |= QBOGUSSHELL;
- }
- if (bitset(EF_VRFYONLY, e->e_flags))
- {
- /* don't do any more now */
- new->q_state = QS_VERIFIED;
- }
- else if (!quoted)
- forward(new, sendq, aliaslevel, e);
- }
- }
- if (!QS_IS_DEAD(new->q_state))
- e->e_nrcpts++;
-
- testselfdestruct:
- new->q_flags |= QTHISPASS;
- if (tTd(26, 8))
- {
- sm_dprintf("testselfdestruct: ");
- printaddr(sm_debug_file(), new, false);
- if (tTd(26, 10))
- {
- sm_dprintf("SENDQ:\n");
- printaddr(sm_debug_file(), *sendq, true);
- sm_dprintf("----\n");
- }
- }
- if (new->q_alias == NULL && new != &e->e_from &&
- QS_IS_DEAD(new->q_state))
- {
- for (q = *sendq; q != NULL; q = q->q_next)
- {
- if (!QS_IS_DEAD(q->q_state))
- break;
- }
- if (q == NULL)
- {
- new->q_state = QS_BADADDR;
- new->q_status = "5.4.6";
- usrerrenh(new->q_status,
- "554 aliasing/forwarding loop broken");
- }
- }
-
- done:
- new->q_flags |= QTHISPASS;
- if (buf != buf0)
- sm_free(buf); /* XXX leak if above code raises exception */
-
- /*
- ** If we are at the top level, check to see if this has
- ** expanded to exactly one address. If so, it can inherit
- ** the primaryness of the address.
- **
- ** While we're at it, clear the QTHISPASS bits.
- */
-
- if (aliaslevel == 0)
- {
- int nrcpts = 0;
- ADDRESS *only = NULL;
-
- for (q = *sendq; q != NULL; q = q->q_next)
- {
- if (bitset(QTHISPASS, q->q_flags) &&
- QS_IS_SENDABLE(q->q_state))
- {
- nrcpts++;
- only = q;
- }
- q->q_flags &= ~QTHISPASS;
- }
- if (nrcpts == 1)
- {
- /* check to see if this actually got a new owner */
- q = only;
- while ((q = q->q_alias) != NULL)
- {
- if (q->q_owner != NULL)
- break;
- }
- if (q == NULL)
- only->q_flags |= QPRIMARY;
- }
- else if (!initialdontsend && nrcpts > 0)
- {
- /* arrange for return receipt */
- e->e_flags |= EF_SENDRECEIPT;
- new->q_flags |= QEXPANDED;
- if (e->e_xfp != NULL &&
- bitset(QPINGONSUCCESS, new->q_flags))
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "%s... expanded to multiple addresses\n",
- new->q_paddr);
- }
- }
- new->q_flags |= QRCPTOK;
- (void) sm_snprintf(buf0, sizeof(buf0), "%d", e->e_nrcpts);
- macdefine(&e->e_macro, A_TEMP, macid("{nrcpts}"), buf0);
- return new;
-}
-
-/*
-** FINDUSER -- find the password entry for a user.
-**
-** This looks a lot like getpwnam, except that it may want to
-** do some fancier pattern matching in /etc/passwd.
-**
-** This routine contains most of the time of many sendmail runs.
-** It deserves to be optimized.
-**
-** Parameters:
-** name -- the name to match against.
-** fuzzyp -- an outarg that is set to true if this entry
-** was found using the fuzzy matching algorithm;
-** set to false otherwise.
-** user -- structure to fill in if user is found
-**
-** Returns:
-** On success, fill in *user, set *fuzzyp and return EX_OK.
-** If the user was not found, return EX_NOUSER.
-** On error, return EX_TEMPFAIL or EX_OSERR.
-**
-** Side Effects:
-** may modify name.
-*/
-
-int
-finduser(name, fuzzyp, user)
- char *name;
- bool *fuzzyp;
- SM_MBDB_T *user;
-{
-#if MATCHGECOS
- register struct passwd *pw;
-#endif /* MATCHGECOS */
- register char *p;
- bool tryagain;
- int status;
-
- if (tTd(29, 4))
- sm_dprintf("finduser(%s): ", name);
-
- *fuzzyp = false;
-
-#if HESIOD
- /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
- for (p = name; *p != '\0'; p++)
- if (!isascii(*p) || !isdigit(*p))
- break;
- if (*p == '\0')
- {
- if (tTd(29, 4))
- sm_dprintf("failed (numeric input)\n");
- return EX_NOUSER;
- }
-#endif /* HESIOD */
-
- /* look up this login name using fast path */
- status = sm_mbdb_lookup(name, user);
- if (status != EX_NOUSER)
- {
- if (tTd(29, 4))
- sm_dprintf("%s (non-fuzzy)\n", sm_strexit(status));
- return status;
- }
-
- /* try mapping it to lower case */
- tryagain = false;
- for (p = name; *p != '\0'; p++)
- {
- if (isascii(*p) && isupper(*p))
- {
- *p = tolower(*p);
- tryagain = true;
- }
- }
- if (tryagain && (status = sm_mbdb_lookup(name, user)) != EX_NOUSER)
- {
- if (tTd(29, 4))
- sm_dprintf("%s (lower case)\n", sm_strexit(status));
- *fuzzyp = true;
- return status;
- }
-
-#if MATCHGECOS
- /* see if fuzzy matching allowed */
- if (!MatchGecos)
- {
- if (tTd(29, 4))
- sm_dprintf("not found (fuzzy disabled)\n");
- return EX_NOUSER;
- }
-
- /* search for a matching full name instead */
- for (p = name; *p != '\0'; p++)
- {
- if (*p == (SpaceSub & 0177) || *p == '_')
- *p = ' ';
- }
- (void) setpwent();
- while ((pw = getpwent()) != NULL)
- {
- char buf[MAXNAME + 1];
-
-# if 0
- if (sm_strcasecmp(pw->pw_name, name) == 0)
- {
- if (tTd(29, 4))
- sm_dprintf("found (case wrapped)\n");
- break;
- }
-# endif /* 0 */
-
- sm_pwfullname(pw->pw_gecos, pw->pw_name, buf, sizeof(buf));
- if (strchr(buf, ' ') != NULL && sm_strcasecmp(buf, name) == 0)
- {
- if (tTd(29, 4))
- sm_dprintf("fuzzy matches %s\n", pw->pw_name);
- message("sending to login name %s", pw->pw_name);
- break;
- }
- }
- if (pw != NULL)
- *fuzzyp = true;
- else if (tTd(29, 4))
- sm_dprintf("no fuzzy match found\n");
-# if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */
- endpwent();
-# endif /* DEC_OSF_BROKEN_GETPWENT */
- if (pw == NULL)
- return EX_NOUSER;
- sm_mbdb_frompw(user, pw);
- return EX_OK;
-#else /* MATCHGECOS */
- if (tTd(29, 4))
- sm_dprintf("not found (fuzzy disabled)\n");
- return EX_NOUSER;
-#endif /* MATCHGECOS */
-}
-
-/*
-** WRITABLE -- predicate returning if the file is writable.
-**
-** This routine must duplicate the algorithm in sys/fio.c.
-** Unfortunately, we cannot use the access call since we
-** won't necessarily be the real uid when we try to
-** actually open the file.
-**
-** Notice that ANY file with ANY execute bit is automatically
-** not writable. This is also enforced by mailfile.
-**
-** Parameters:
-** filename -- the file name to check.
-** ctladdr -- the controlling address for this file.
-** flags -- SFF_* flags to control the function.
-**
-** Returns:
-** true -- if we will be able to write this file.
-** false -- if we cannot write this file.
-**
-** Side Effects:
-** none.
-*/
-
-bool
-writable(filename, ctladdr, flags)
- char *filename;
- ADDRESS *ctladdr;
- long flags;
-{
- uid_t euid = 0;
- gid_t egid = 0;
- char *user = NULL;
-
- if (tTd(44, 5))
- sm_dprintf("writable(%s, 0x%lx)\n", filename, flags);
-
- /*
- ** File does exist -- check that it is writable.
- */
-
- if (geteuid() != 0)
- {
- euid = geteuid();
- egid = getegid();
- user = NULL;
- }
- else if (ctladdr != NULL)
- {
- euid = ctladdr->q_uid;
- egid = ctladdr->q_gid;
- user = ctladdr->q_user;
- }
- else if (bitset(SFF_RUNASREALUID, flags))
- {
- euid = RealUid;
- egid = RealGid;
- user = RealUserName;
- }
- else if (FileMailer != NULL && !bitset(SFF_ROOTOK, flags))
- {
- if (FileMailer->m_uid == NO_UID)
- {
- euid = DefUid;
- user = DefUser;
- }
- else
- {
- euid = FileMailer->m_uid;
- user = NULL;
- }
- if (FileMailer->m_gid == NO_GID)
- egid = DefGid;
- else
- egid = FileMailer->m_gid;
- }
- else
- {
- euid = egid = 0;
- user = NULL;
- }
- if (!bitset(SFF_ROOTOK, flags))
- {
- if (euid == 0)
- {
- euid = DefUid;
- user = DefUser;
- }
- if (egid == 0)
- egid = DefGid;
- }
- if (geteuid() == 0 &&
- (ctladdr == NULL || !bitset(QGOODUID, ctladdr->q_flags)))
- flags |= SFF_SETUIDOK;
-
- if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
- flags |= SFF_NOSLINK;
- if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
- flags |= SFF_NOHLINK;
-
- errno = safefile(filename, euid, egid, user, flags, S_IWRITE, NULL);
- return errno == 0;
-}
-
-/*
-** INCLUDE -- handle :include: specification.
-**
-** Parameters:
-** fname -- filename to include.
-** forwarding -- if true, we are reading a .forward file.
-** if false, it's a :include: file.
-** ctladdr -- address template to use to fill in these
-** addresses -- effective user/group id are
-** the important things.
-** sendq -- a pointer to the head of the send queue
-** to put these addresses in.
-** aliaslevel -- the alias nesting depth.
-** e -- the current envelope.
-**
-** Returns:
-** open error status
-**
-** Side Effects:
-** reads the :include: file and sends to everyone
-** listed in that file.
-**
-** Security Note:
-** If you have restricted chown (that is, you can't
-** give a file away), it is reasonable to allow programs
-** and files called from this :include: file to be to be
-** run as the owner of the :include: file. This is bogus
-** if there is any chance of someone giving away a file.
-** We assume that pre-POSIX systems can give away files.
-**
-** There is an additional restriction that if you
-** forward to a :include: file, it will not take on
-** the ownership of the :include: file. This may not
-** be necessary, but shouldn't hurt.
-*/
-
-static jmp_buf CtxIncludeTimeout;
-
-int
-include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
- char *fname;
- bool forwarding;
- ADDRESS *ctladdr;
- ADDRESS **sendq;
- int aliaslevel;
- ENVELOPE *e;
-{
- SM_FILE_T *volatile fp = NULL;
- char *oldto = e->e_to;
- char *oldfilename = FileName;
- int oldlinenumber = LineNumber;
- register SM_EVENT *ev = NULL;
- int nincludes;
- int mode;
- volatile bool maxreached = false;
- register ADDRESS *ca;
- volatile uid_t saveduid;
- volatile gid_t savedgid;
- volatile uid_t uid;
- volatile gid_t gid;
- char *volatile user;
- int rval = 0;
- volatile long sfflags = SFF_REGONLY;
- register char *p;
- bool safechown = false;
- volatile bool safedir = false;
- struct stat st;
- char buf[MAXLINE];
-
- if (tTd(27, 2))
- sm_dprintf("include(%s)\n", fname);
- if (tTd(27, 4))
- sm_dprintf(" ruid=%d euid=%d\n",
- (int) getuid(), (int) geteuid());
- if (tTd(27, 14))
- {
- sm_dprintf("ctladdr ");
- printaddr(sm_debug_file(), ctladdr, false);
- }
-
- if (tTd(27, 9))
- sm_dprintf("include: old uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
-
- if (forwarding)
- {
- sfflags |= SFF_MUSTOWN|SFF_ROOTOK;
- if (!bitnset(DBS_GROUPWRITABLEFORWARDFILE, DontBlameSendmail))
- sfflags |= SFF_NOGWFILES;
- if (!bitnset(DBS_WORLDWRITABLEFORWARDFILE, DontBlameSendmail))
- sfflags |= SFF_NOWWFILES;
- }
- else
- {
- if (!bitnset(DBS_GROUPWRITABLEINCLUDEFILE, DontBlameSendmail))
- sfflags |= SFF_NOGWFILES;
- if (!bitnset(DBS_WORLDWRITABLEINCLUDEFILE, DontBlameSendmail))
- sfflags |= SFF_NOWWFILES;
- }
-
- /*
- ** 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))
- sm_dprintf("include: not safe (euid=%d, RunAsUid=%d)\n",
- (int) geteuid(), (int) RunAsUid);
- ctladdr->q_flags |= QUNSAFEADDR;
- }
-
- ca = getctladdr(ctladdr);
- if (ca == NULL ||
- (ca->q_uid == DefUid && ca->q_gid == 0))
- {
- uid = DefUid;
- gid = DefGid;
- user = DefUser;
- }
- else
- {
- uid = ca->q_uid;
- gid = ca->q_gid;
- user = ca->q_user;
- }
-#if MAILER_SETUID_METHOD != USE_SETUID
- saveduid = geteuid();
- savedgid = getegid();
- if (saveduid == 0)
- {
- if (!DontInitGroups)
- {
- if (initgroups(user, gid) == -1)
- {
- rval = EAGAIN;
- syserr("include: initgroups(%s, %d) failed",
- user, gid);
- goto resetuid;
- }
- }
- else
- {
- GIDSET_T gidset[1];
-
- gidset[0] = gid;
- if (setgroups(1, gidset) == -1)
- {
- rval = EAGAIN;
- syserr("include: setgroups() failed");
- goto resetuid;
- }
- }
-
- if (gid != 0 && setgid(gid) < -1)
- {
- rval = EAGAIN;
- syserr("setgid(%d) failure", gid);
- goto resetuid;
- }
- if (uid != 0)
- {
-# if MAILER_SETUID_METHOD == USE_SETEUID
- if (seteuid(uid) < 0)
- {
- rval = EAGAIN;
- syserr("seteuid(%d) failure (real=%d, eff=%d)",
- uid, (int) getuid(), (int) geteuid());
- goto resetuid;
- }
-# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
-# if MAILER_SETUID_METHOD == USE_SETREUID
- if (setreuid(0, uid) < 0)
- {
- rval = EAGAIN;
- syserr("setreuid(0, %d) failure (real=%d, eff=%d)",
- uid, (int) getuid(), (int) geteuid());
- goto resetuid;
- }
-# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
- }
- }
-#endif /* MAILER_SETUID_METHOD != USE_SETUID */
-
- if (tTd(27, 9))
- sm_dprintf("include: new uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
-
- /*
- ** If home directory is remote mounted but server is down,
- ** this can hang or give errors; use a timeout to avoid this
- */
-
- if (setjmp(CtxIncludeTimeout) != 0)
- {
- ctladdr->q_state = QS_QUEUEUP;
- errno = 0;
-
- /* return pseudo-error code */
- rval = E_SM_OPENTIMEOUT;
- goto resetuid;
- }
- if (TimeOuts.to_fileopen > 0)
- ev = sm_setevent(TimeOuts.to_fileopen, includetimeout, 0);
- else
- ev = NULL;
-
-
- /* check for writable parent directory */
- p = strrchr(fname, '/');
- if (p != NULL)
- {
- int ret;
-
- *p = '\0';
- ret = safedirpath(fname, uid, gid, user,
- sfflags|SFF_SAFEDIRPATH, 0, 0);
- if (ret == 0)
- {
- /* in safe directory: relax chown & link rules */
- safedir = true;
- sfflags |= SFF_NOPATHCHECK;
- }
- else
- {
- if (bitnset((forwarding ?
- DBS_FORWARDFILEINUNSAFEDIRPATH :
- DBS_INCLUDEFILEINUNSAFEDIRPATH),
- DontBlameSendmail))
- sfflags |= SFF_NOPATHCHECK;
- else if (bitnset((forwarding ?
- DBS_FORWARDFILEINGROUPWRITABLEDIRPATH :
- DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH),
- DontBlameSendmail) &&
- ret == E_SM_GWDIR)
- {
- 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
- sfflags |= SFF_SAFEDIRPATH;
- }
- else
- sfflags |= SFF_SAFEDIRPATH;
- if (ret > E_PSEUDOBASE &&
- !bitnset((forwarding ?
- DBS_FORWARDFILEINUNSAFEDIRPATHSAFE :
- DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE),
- DontBlameSendmail))
- {
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: unsafe directory path, marked unsafe",
- shortenstring(fname, MAXSHORTSTR));
- ctladdr->q_flags |= QUNSAFEADDR;
- }
- }
- *p = '/';
- }
-
- /* allow links only in unwritable directories */
- if (!safedir &&
- !bitnset((forwarding ?
- DBS_LINKEDFORWARDFILEINWRITABLEDIR :
- DBS_LINKEDINCLUDEFILEINWRITABLEDIR),
- DontBlameSendmail))
- sfflags |= SFF_NOLINK;
-
- rval = safefile(fname, uid, gid, user, sfflags, S_IREAD, &st);
- if (rval != 0)
- {
- /* don't use this :include: file */
- if (tTd(27, 4))
- sm_dprintf("include: not safe (uid=%d): %s\n",
- (int) uid, sm_errstring(rval));
- }
- else if ((fp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, fname,
- SM_IO_RDONLY, NULL)) == NULL)
- {
- rval = errno;
- if (tTd(27, 4))
- sm_dprintf("include: open: %s\n", sm_errstring(rval));
- }
- else if (filechanged(fname, sm_io_getinfo(fp,SM_IO_WHAT_FD, NULL), &st))
- {
- rval = E_SM_FILECHANGE;
- if (tTd(27, 4))
- sm_dprintf("include: file changed after open\n");
- }
- if (ev != NULL)
- sm_clrevent(ev);
-
-resetuid:
-
-#if HASSETREUID || USESETEUID
- if (saveduid == 0)
- {
- if (uid != 0)
- {
-# if USESETEUID
- if (seteuid(0) < 0)
- syserr("!seteuid(0) failure (real=%d, eff=%d)",
- (int) getuid(), (int) geteuid());
-# else /* USESETEUID */
- if (setreuid(-1, 0) < 0)
- syserr("!setreuid(-1, 0) failure (real=%d, eff=%d)",
- (int) getuid(), (int) geteuid());
- if (setreuid(RealUid, 0) < 0)
- syserr("!setreuid(%d, 0) failure (real=%d, eff=%d)",
- (int) RealUid, (int) getuid(),
- (int) geteuid());
-# endif /* USESETEUID */
- }
- if (setgid(savedgid) < 0)
- syserr("!setgid(%d) failure (real=%d eff=%d)",
- (int) savedgid, (int) getgid(),
- (int) getegid());
- }
-#endif /* HASSETREUID || USESETEUID */
-
- if (tTd(27, 9))
- sm_dprintf("include: reset uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
-
- if (rval == E_SM_OPENTIMEOUT)
- usrerr("451 4.4.1 open timeout on %s", fname);
-
- if (fp == NULL)
- return rval;
-
- if (fstat(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), &st) < 0)
- {
- rval = errno;
- syserr("Cannot fstat %s!", fname);
- (void) sm_io_close(fp, SM_TIME_DEFAULT);
- return rval;
- }
-
- /* if path was writable, check to avoid file giveaway tricks */
- safechown = chownsafe(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), safedir);
- if (tTd(27, 6))
- sm_dprintf("include: parent of %s is %s, chown is %ssafe\n",
- fname, safedir ? "safe" : "dangerous",
- safechown ? "" : "un");
-
- /* if no controlling user or coming from an alias delivery */
- if (safechown &&
- (ca == NULL ||
- (ca->q_uid == DefUid && ca->q_gid == 0)))
- {
- ctladdr->q_uid = st.st_uid;
- ctladdr->q_gid = st.st_gid;
- ctladdr->q_flags |= QGOODUID;
- }
- if (ca != NULL && ca->q_uid == st.st_uid)
- {
- /* optimization -- avoid getpwuid if we already have info */
- ctladdr->q_flags |= ca->q_flags & QBOGUSSHELL;
- ctladdr->q_ruser = ca->q_ruser;
- }
- else if (!forwarding)
- {
- register struct passwd *pw;
-
- pw = sm_getpwuid(st.st_uid);
- if (pw == NULL)
- {
- ctladdr->q_uid = st.st_uid;
- ctladdr->q_flags |= QBOGUSSHELL;
- }
- else
- {
- char *sh;
-
- ctladdr->q_ruser = sm_rpool_strdup_x(e->e_rpool,
- pw->pw_name);
- if (safechown)
- sh = pw->pw_shell;
- else
- sh = "/SENDMAIL/ANY/SHELL/";
- if (!usershellok(pw->pw_name, sh))
- {
- if (LogLevel > 11)
- 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");
- if (safechown)
- ctladdr->q_flags |= QBOGUSSHELL;
- else
- ctladdr->q_flags |= QUNSAFEADDR;
- }
- }
- }
-
- if (bitset(EF_VRFYONLY, e->e_flags))
- {
- /* don't do any more now */
- ctladdr->q_state = QS_VERIFIED;
- e->e_nrcpts++;
- (void) sm_io_close(fp, SM_TIME_DEFAULT);
- return rval;
- }
-
- /*
- ** Check to see if some bad guy can write this file
- **
- ** Group write checking could be more clever, e.g.,
- ** guessing as to which groups are actually safe ("sys"
- ** may be; "user" probably is not).
- */
-
- mode = S_IWOTH;
- if (!bitnset((forwarding ?
- DBS_GROUPWRITABLEFORWARDFILESAFE :
- DBS_GROUPWRITABLEINCLUDEFILESAFE),
- DontBlameSendmail))
- mode |= S_IWGRP;
-
- if (bitset(mode, st.st_mode))
- {
- if (tTd(27, 6))
- sm_dprintf("include: %s is %s writable, marked unsafe\n",
- shortenstring(fname, MAXSHORTSTR),
- bitset(S_IWOTH, st.st_mode) ? "world"
- : "group");
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: %s writable %s file, marked unsafe",
- shortenstring(fname, MAXSHORTSTR),
- bitset(S_IWOTH, st.st_mode) ? "world" : "group",
- forwarding ? "forward" : ":include:");
- ctladdr->q_flags |= QUNSAFEADDR;
- }
-
- /* read the file -- each line is a comma-separated list. */
- FileName = fname;
- LineNumber = 0;
- ctladdr->q_flags &= ~QSELFREF;
- nincludes = 0;
- while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL &&
- !maxreached)
- {
- fixcrlf(buf, true);
- LineNumber++;
- if (buf[0] == '#' || buf[0] == '\0')
- continue;
-
- /* <sp>#@# introduces a comment anywhere */
- /* for Japanese character sets */
- for (p = buf; (p = strchr(++p, '#')) != NULL; )
- {
- if (p[1] == '@' && p[2] == '#' &&
- isascii(p[-1]) && isspace(p[-1]) &&
- (p[3] == '\0' || (isascii(p[3]) && isspace(p[3]))))
- {
- --p;
- while (p > buf && isascii(p[-1]) &&
- isspace(p[-1]))
- --p;
- p[0] = '\0';
- break;
- }
- }
- if (buf[0] == '\0')
- continue;
-
- e->e_to = NULL;
- message("%s to %s",
- forwarding ? "forwarding" : "sending", buf);
- if (forwarding && LogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "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 */
-#if 0
- /* additional: (?) */
- ctladdr->q_state = QS_DONTSEND;
-#endif /* 0 */
-
- syserr("Attempt to forward to more than %d addresses (in %s)!",
- MaxForwardEntries, fname);
- maxreached = true;
- }
- }
-
- if (sm_io_error(fp) && tTd(27, 3))
- sm_dprintf("include: read error: %s\n", sm_errstring(errno));
- if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags))
- {
- if (aliaslevel <= MaxAliasRecursion ||
- ctladdr->q_state != QS_BADADDR)
- {
- ctladdr->q_state = QS_DONTSEND;
- if (tTd(27, 5))
- {
- sm_dprintf("include: QS_DONTSEND ");
- printaddr(sm_debug_file(), ctladdr, false);
- }
- }
- }
-
- (void) sm_io_close(fp, SM_TIME_DEFAULT);
- FileName = oldfilename;
- LineNumber = oldlinenumber;
- e->e_to = oldto;
- return rval;
-}
-
-static void
-includetimeout(ignore)
- int ignore;
-{
- /*
- ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
- ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
- ** DOING.
- */
-
- errno = ETIMEDOUT;
- longjmp(CtxIncludeTimeout, 1);
-}
-
-/*
-** SENDTOARGV -- send to an argument vector.
-**
-** Parameters:
-** argv -- argument vector to send to.
-** e -- the current envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** puts all addresses on the argument vector onto the
-** send queue.
-*/
-
-void
-sendtoargv(argv, e)
- register char **argv;
- register ENVELOPE *e;
-{
- register char *p;
-
- while ((p = *argv++) != NULL)
- (void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e);
-}
-
-/*
-** GETCTLADDR -- get controlling address from an address header.
-**
-** If none, get one corresponding to the effective userid.
-**
-** Parameters:
-** a -- the address to find the controller of.
-**
-** Returns:
-** the controlling address.
-*/
-
-ADDRESS *
-getctladdr(a)
- register ADDRESS *a;
-{
- while (a != NULL && !bitset(QGOODUID, a->q_flags))
- a = a->q_alias;
- return a;
-}
-
-/*
-** SELF_REFERENCE -- check to see if an address references itself
-**
-** The check is done through a chain of aliases. If it is part of
-** a loop, break the loop at the "best" address, that is, the one
-** that exists as a real user.
-**
-** This is to handle the case of:
-** awc: Andrew.Chang
-** Andrew.Chang: awc@mail.server
-** which is a problem only on mail.server.
-**
-** Parameters:
-** a -- the address to check.
-**
-** Returns:
-** The address that should be retained.
-*/
-
-static ADDRESS *
-self_reference(a)
- ADDRESS *a;
-{
- ADDRESS *b; /* top entry in self ref loop */
- ADDRESS *c; /* entry that point to a real mail box */
-
- if (tTd(27, 1))
- sm_dprintf("self_reference(%s)\n", a->q_paddr);
-
- for (b = a->q_alias; b != NULL; b = b->q_alias)
- {
- if (sameaddr(a, b))
- break;
- }
-
- if (b == NULL)
- {
- if (tTd(27, 1))
- sm_dprintf("\t... no self ref\n");
- return NULL;
- }
-
- /*
- ** Pick the first address that resolved to a real mail box
- ** i.e has a mbdb entry. The returned value will be marked
- ** QSELFREF in recipient(), which in turn will disable alias()
- ** from marking it as QS_IS_DEAD(), which mean it will be used
- ** as a deliverable address.
- **
- ** The 2 key thing to note here are:
- ** 1) we are in a recursive call sequence:
- ** alias->sendtolist->recipient->alias
- ** 2) normally, when we return back to alias(), the address
- ** will be marked QS_EXPANDED, since alias() assumes the
- ** expanded form will be used instead of the current address.
- ** This behaviour is turned off if the address is marked
- ** QSELFREF. We set QSELFREF when we return to recipient().
- */
-
- c = a;
- while (c != NULL)
- {
- if (tTd(27, 10))
- sm_dprintf(" %s", c->q_user);
- if (bitnset(M_HASPWENT, c->q_mailer->m_flags))
- {
- SM_MBDB_T user;
-
- if (tTd(27, 2))
- sm_dprintf("\t... getpwnam(%s)... ", c->q_user);
- if (sm_mbdb_lookup(c->q_user, &user) == EX_OK)
- {
- if (tTd(27, 2))
- sm_dprintf("found\n");
-
- /* ought to cache results here */
- if (sameaddr(b, c))
- return b;
- else
- return c;
- }
- if (tTd(27, 2))
- sm_dprintf("failed\n");
- }
- else
- {
- /* if local delivery, compare usernames */
- if (bitnset(M_LOCALMAILER, c->q_mailer->m_flags) &&
- b->q_mailer == c->q_mailer)
- {
- if (tTd(27, 2))
- sm_dprintf("\t... local match (%s)\n",
- c->q_user);
- if (sameaddr(b, c))
- return b;
- else
- return c;
- }
- }
- if (tTd(27, 10))
- sm_dprintf("\n");
- c = c->q_alias;
- }
-
- if (tTd(27, 1))
- sm_dprintf("\t... cannot break loop for \"%s\"\n", a->q_paddr);
-
- return NULL;
-}
diff --git a/contrib/sendmail/src/sasl.c b/contrib/sendmail/src/sasl.c
deleted file mode 100644
index 6f9e4a5..0000000
--- a/contrib/sendmail/src/sasl.c
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (c) 2001-2002 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sm/gen.h>
-SM_RCSID("@(#)$Id: sasl.c,v 8.22 2006/08/15 23:24:57 ca Exp $")
-
-#if SASL
-# include <stdlib.h>
-# include <sendmail.h>
-# include <errno.h>
-
-/*
-** In order to ensure that storage leaks are tracked, and to prevent
-** conflicts between the sm_heap package and sasl, we tell sasl to
-** use the following heap allocation functions. Unfortunately,
-** the sasl package incorrectly specifies the size of a block
-** using unsigned long: for portability, it should be size_t.
-*/
-
-void *sm_sasl_malloc __P((unsigned long));
-static void *sm_sasl_calloc __P((unsigned long, unsigned long));
-static void *sm_sasl_realloc __P((void *, unsigned long));
-void sm_sasl_free __P((void *));
-
-/*
-** SASLv1:
-** We can't use an rpool for Cyrus-SASL memory management routines,
-** since the encryption/decryption routines in Cyrus-SASL
-** allocate/deallocate a buffer each time. Since rpool
-** don't release memory until the very end, memory consumption is
-** proportional to the size of an e-mail, which is unacceptable.
-*/
-
-/*
-** SM_SASL_MALLOC -- malloc() for SASL
-**
-** Parameters:
-** size -- size of requested memory.
-**
-** Returns:
-** pointer to memory.
-*/
-
-void *
-sm_sasl_malloc(size)
- unsigned long size;
-{
- return sm_malloc((size_t) size);
-}
-
-/*
-** SM_SASL_CALLOC -- calloc() for SASL
-**
-** Parameters:
-** nelem -- number of elements.
-** elemsize -- size of each element.
-**
-** Returns:
-** pointer to memory.
-**
-** Notice:
-** this isn't currently used by SASL.
-*/
-
-static void *
-sm_sasl_calloc(nelem, elemsize)
- unsigned long nelem;
- unsigned long elemsize;
-{
- size_t size;
- void *p;
-
- size = (size_t) nelem * (size_t) elemsize;
- p = sm_malloc(size);
- if (p == NULL)
- return NULL;
- memset(p, '\0', size);
- return p;
-}
-
-/*
-** SM_SASL_REALLOC -- realloc() for SASL
-**
-** Parameters:
-** p -- pointer to old memory.
-** size -- size of requested memory.
-**
-** Returns:
-** pointer to new memory.
-*/
-
-static void *
-sm_sasl_realloc(o, size)
- void *o;
- unsigned long size;
-{
- return sm_realloc(o, (size_t) size);
-}
-
-/*
-** SM_SASL_FREE -- free() for SASL
-**
-** Parameters:
-** p -- pointer to free.
-**
-** Returns:
-** none
-*/
-
-void
-sm_sasl_free(p)
- void *p;
-{
- sm_free(p);
-}
-
-/*
-** SM_SASL_INIT -- sendmail specific SASL initialization
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** installs memory management routines for SASL.
-*/
-
-void
-sm_sasl_init()
-{
- sasl_set_alloc(sm_sasl_malloc, sm_sasl_calloc,
- sm_sasl_realloc, sm_sasl_free);
-}
-/*
-** INTERSECT -- create the intersection between two lists
-**
-** Parameters:
-** s1, s2 -- lists of items (separated by single blanks).
-** rpool -- resource pool from which result is allocated.
-**
-** Returns:
-** the intersection of both lists.
-*/
-
-char *
-intersect(s1, s2, rpool)
- char *s1, *s2;
- SM_RPOOL_T *rpool;
-{
- char *hr, *h1, *h, *res;
- int l1, l2, rl;
-
- if (s1 == NULL || s2 == NULL) /* NULL string(s) -> NULL result */
- return NULL;
- l1 = strlen(s1);
- l2 = strlen(s2);
- rl = SM_MIN(l1, l2);
- res = (char *) sm_rpool_malloc(rpool, rl + 1);
- if (res == NULL)
- return NULL;
- *res = '\0';
- if (rl == 0) /* at least one string empty? */
- return res;
- hr = res;
- h1 = s1;
- h = s1;
-
- /* walk through s1 */
- while (h != NULL && *h1 != '\0')
- {
- /* is there something after the current word? */
- if ((h = strchr(h1, ' ')) != NULL)
- *h = '\0';
- l1 = strlen(h1);
-
- /* does the current word appear in s2 ? */
- if (iteminlist(h1, s2, " ") != NULL)
- {
- /* add a blank if not first item */
- if (hr != res)
- *hr++ = ' ';
-
- /* copy the item */
- memcpy(hr, h1, l1);
-
- /* advance pointer in result list */
- hr += l1;
- *hr = '\0';
- }
- if (h != NULL)
- {
- /* there are more items */
- *h = ' ';
- h1 = h + 1;
- }
- }
- return res;
-}
-# if SASL >= 20000
-/*
-** IPTOSTRING -- create string for SASL_IP*PORT property
-** (borrowed from lib/iptostring.c in Cyrus-IMAP)
-**
-** Parameters:
-** addr -- (pointer to) socket address
-** addrlen -- length of socket address
-** out -- output string (result)
-** outlen -- maximum length of output string
-**
-** Returns:
-** true iff successful.
-**
-** Side Effects:
-** creates output string if successful.
-** sets errno if unsuccessful.
-*/
-
-# include <arpa/inet.h>
-
-# ifndef NI_MAXHOST
-# define NI_MAXHOST 1025
-# endif
-# ifndef NI_MAXSERV
-# define NI_MAXSERV 32
-# endif
-
-bool
-iptostring(addr, addrlen, out, outlen)
- SOCKADDR *addr;
- SOCKADDR_LEN_T addrlen;
- char *out;
- unsigned outlen;
-{
- char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
-# if NETINET6
- int niflags;
-# endif /* NETINET6 */
-
- if (addr == NULL || out == NULL)
- {
- errno = EINVAL;
- return false;
- }
-
-# if NETINET6
- niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
-# ifdef NI_WITHSCOPEID
- if (addr->sa.sa_family == AF_INET6)
- niflags |= NI_WITHSCOPEID;
-# endif /* NI_WITHSCOPEID */
- if (getnameinfo((struct sockaddr *) addr, addrlen,
- hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), niflags) != 0)
- return false;
-# else /* NETINET6 */
- if (addr->sa.sa_family != AF_INET)
- {
- errno = EINVAL;
- return false;
- }
- if (sm_strlcpy(hbuf, inet_ntoa(addr->sin.sin_addr), sizeof(hbuf))
- >= sizeof(hbuf))
- {
- errno = ENOMEM;
- return false;
- }
- sm_snprintf(pbuf, sizeof(pbuf), "%d", ntohs(addr->sin.sin_port));
-# endif /* NETINET6 */
-
- if (outlen < strlen(hbuf) + strlen(pbuf) + 2)
- {
- errno = ENOMEM;
- return false;
- }
- sm_snprintf(out, outlen, "%s;%s", hbuf, pbuf);
- return true;
-}
-# endif /* SASL >= 20000 */
-#endif /* SASL */
diff --git a/contrib/sendmail/src/savemail.c b/contrib/sendmail/src/savemail.c
deleted file mode 100644
index cf72e8d..0000000
--- a/contrib/sendmail/src/savemail.c
+++ /dev/null
@@ -1,1761 +0,0 @@
-/*
- * Copyright (c) 1998-2003, 2006 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: savemail.c,v 8.313 2006/11/29 00:20:41 ca Exp $")
-
-static bool errbody __P((MCI *, ENVELOPE *, char *));
-static bool pruneroute __P((char *));
-
-/*
-** SAVEMAIL -- Save mail on error
-**
-** If mailing back errors, mail it back to the originator
-** together with an error message; otherwise, just put it in
-** dead.letter in the user's home directory (if he exists on
-** this machine).
-**
-** Parameters:
-** e -- the envelope containing the message in error.
-** sendbody -- if true, also send back the body of the
-** message; otherwise just send the header.
-**
-** Returns:
-** true if savemail panic'ed, (i.e., the data file should
-** be preserved by dropenvelope())
-**
-** Side Effects:
-** Saves the letter, by writing or mailing it back to the
-** sender, or by putting it in dead.letter in her home
-** directory.
-*/
-
-/* defines for state machine */
-#define ESM_REPORT 0 /* report to sender's terminal */
-#define ESM_MAIL 1 /* mail back to sender */
-#define ESM_QUIET 2 /* mail has already been returned */
-#define ESM_DEADLETTER 3 /* save in ~/dead.letter */
-#define ESM_POSTMASTER 4 /* return to postmaster */
-#define ESM_DEADLETTERDROP 5 /* save in DeadLetterDrop */
-#define ESM_PANIC 6 /* call loseqfile() */
-#define ESM_DONE 7 /* message is successfully delivered */
-
-bool
-savemail(e, sendbody)
- register ENVELOPE *e;
- bool sendbody;
-{
- register SM_FILE_T *fp;
- bool panic = false;
- int state;
- auto ADDRESS *q = NULL;
- register char *p;
- MCI mcibuf;
- int flags;
- long sff;
- char buf[MAXLINE + 1];
- char dlbuf[MAXPATHLEN];
- SM_MBDB_T user;
-
-
- if (tTd(6, 1))
- {
- sm_dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=",
- e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
- ExitStat);
- printaddr(sm_debug_file(), &e->e_from, false);
- }
-
- if (e->e_id == NULL)
- {
- /* can't return a message with no id */
- return panic;
- }
-
- /*
- ** In the unhappy event we don't know who to return the mail
- ** to, make someone up.
- */
-
- if (e->e_from.q_paddr == NULL)
- {
- e->e_sender = "Postmaster";
- if (parseaddr(e->e_sender, &e->e_from,
- RF_COPYPARSE|RF_SENDERADDR,
- '\0', NULL, e, false) == NULL)
- {
- syserr("553 5.3.5 Cannot parse Postmaster!");
- finis(true, true, EX_SOFTWARE);
- }
- }
- e->e_to = NULL;
-
- /*
- ** Basic state machine.
- **
- ** This machine runs through the following states:
- **
- ** ESM_QUIET Errors have already been printed iff the
- ** sender is local.
- ** ESM_REPORT Report directly to the sender's terminal.
- ** ESM_MAIL Mail response to the sender.
- ** ESM_DEADLETTER Save response in ~/dead.letter.
- ** ESM_POSTMASTER Mail response to the postmaster.
- ** ESM_DEADLETTERDROP
- ** If DeadLetterDrop set, save it there.
- ** ESM_PANIC Save response anywhere possible.
- */
-
- /* determine starting state */
- switch (e->e_errormode)
- {
- case EM_WRITE:
- state = ESM_REPORT;
- break;
-
- case EM_BERKNET:
- case EM_MAIL:
- state = ESM_MAIL;
- break;
-
- case EM_PRINT:
- case '\0':
- state = ESM_QUIET;
- break;
-
- case EM_QUIET:
- /* no need to return anything at all */
- return panic;
-
- default:
- syserr("554 5.3.0 savemail: bogus errormode x%x",
- e->e_errormode);
- state = ESM_MAIL;
- break;
- }
-
- /* if this is already an error response, send to postmaster */
- if (bitset(EF_RESPONSE, e->e_flags))
- {
- if (e->e_parent != NULL &&
- bitset(EF_RESPONSE, e->e_parent->e_flags))
- {
- /* got an error sending a response -- can it */
- return panic;
- }
- state = ESM_POSTMASTER;
- }
-
- while (state != ESM_DONE)
- {
- if (tTd(6, 5))
- sm_dprintf(" state %d\n", state);
-
- switch (state)
- {
- case ESM_QUIET:
- if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
- state = ESM_DEADLETTER;
- else
- state = ESM_MAIL;
- break;
-
- case ESM_REPORT:
-
- /*
- ** If the user is still logged in on the same terminal,
- ** then write the error messages back to hir (sic).
- */
-
-#if USE_TTYPATH
- p = ttypath();
-#else /* USE_TTYPATH */
- p = NULL;
-#endif /* USE_TTYPATH */
-
- if (p == NULL || sm_io_reopen(SmFtStdio,
- SM_TIME_DEFAULT,
- p, SM_IO_WRONLY, NULL,
- smioout) == NULL)
- {
- state = ESM_MAIL;
- break;
- }
-
- expand("\201n", buf, sizeof(buf), e);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "\r\nMessage from %s...\r\n", buf);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Errors occurred while sending mail.\r\n");
- if (e->e_xfp != NULL)
- {
- (void) bfrewind(e->e_xfp);
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Transcript follows:\r\n");
- while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT,
- buf, sizeof(buf)) != NULL &&
- !sm_io_error(smioout))
- (void) sm_io_fputs(smioout,
- SM_TIME_DEFAULT,
- buf);
- }
- else
- {
- syserr("Cannot open %s",
- queuename(e, XSCRPT_LETTER));
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Transcript of session is unavailable.\r\n");
- }
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Original message will be saved in dead.letter.\r\n");
- state = ESM_DEADLETTER;
- break;
-
- case ESM_MAIL:
- /*
- ** If mailing back, do it.
- ** Throw away all further output. Don't alias,
- ** since this could cause loops, e.g., if joe
- ** mails to joe@x, and for some reason the network
- ** for @x is down, then the response gets sent to
- ** joe@x, which gives a response, etc. Also force
- ** the mail to be delivered even if a version of
- ** it has already been sent to the sender.
- **
- ** If this is a configuration or local software
- ** error, send to the local postmaster as well,
- ** since the originator can't do anything
- ** about it anyway. Note that this is a full
- ** copy of the message (intentionally) so that
- ** the Postmaster can forward things along.
- */
-
- if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
- {
- (void) sendtolist("postmaster", NULLADDR,
- &e->e_errorqueue, 0, e);
- }
- if (!emptyaddr(&e->e_from))
- {
- char from[TOBUFSIZE];
-
- if (sm_strlcpy(from, e->e_from.q_paddr,
- sizeof(from)) >= sizeof(from))
- {
- state = ESM_POSTMASTER;
- break;
- }
-
- if (!DontPruneRoutes)
- (void) pruneroute(from);
-
- (void) sendtolist(from, NULLADDR,
- &e->e_errorqueue, 0, e);
- }
-
- /*
- ** Deliver a non-delivery report to the
- ** Postmaster-designate (not necessarily
- ** Postmaster). This does not include the
- ** body of the message, for privacy reasons.
- ** You really shouldn't need this.
- */
-
- e->e_flags |= EF_PM_NOTIFY;
-
- /* check to see if there are any good addresses */
- for (q = e->e_errorqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_SENDABLE(q->q_state))
- break;
- }
- if (q == NULL)
- {
- /* this is an error-error */
- state = ESM_POSTMASTER;
- break;
- }
- if (returntosender(e->e_message, e->e_errorqueue,
- sendbody ? RTSF_SEND_BODY
- : RTSF_NO_BODY,
- e) == 0)
- {
- state = ESM_DONE;
- break;
- }
-
- /* didn't work -- return to postmaster */
- state = ESM_POSTMASTER;
- break;
-
- case ESM_POSTMASTER:
- /*
- ** Similar to previous case, but to system postmaster.
- */
-
- q = NULL;
- expand(DoubleBounceAddr, buf, sizeof(buf), e);
-
- /*
- ** Just drop it on the floor if DoubleBounceAddr
- ** expands to an empty string.
- */
-
- if (*buf == '\0')
- {
- state = ESM_DONE;
- break;
- }
- if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0)
- {
- syserr("553 5.3.0 cannot parse %s!", buf);
- ExitStat = EX_SOFTWARE;
- state = ESM_DEADLETTERDROP;
- break;
- }
- flags = RTSF_PM_BOUNCE;
- if (sendbody)
- flags |= RTSF_SEND_BODY;
- if (returntosender(e->e_message, q, flags, e) == 0)
- {
- state = ESM_DONE;
- break;
- }
-
- /* didn't work -- last resort */
- state = ESM_DEADLETTERDROP;
- break;
-
- case ESM_DEADLETTER:
- /*
- ** Save the message in dead.letter.
- ** If we weren't mailing back, and the user is
- ** local, we should save the message in
- ** ~/dead.letter so that the poor person doesn't
- ** have to type it over again -- and we all know
- ** what poor typists UNIX users are.
- */
-
- p = NULL;
- if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
- {
- if (e->e_from.q_home != NULL)
- p = e->e_from.q_home;
- else if (sm_mbdb_lookup(e->e_from.q_user, &user)
- == EX_OK &&
- *user.mbdb_homedir != '\0')
- p = user.mbdb_homedir;
- }
- if (p == NULL || e->e_dfp == NULL)
- {
- /* no local directory or no data file */
- state = ESM_MAIL;
- break;
- }
-
- /* we have a home directory; write dead.letter */
- macdefine(&e->e_macro, A_TEMP, 'z', p);
-
- /* get the sender for the UnixFromLine */
- p = macvalue('g', e);
- macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
-
- expand("\201z/dead.letter", dlbuf, sizeof(dlbuf), e);
- sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
- if (RealUid == 0)
- sff |= SFF_ROOTOK;
- e->e_to = dlbuf;
- if (writable(dlbuf, NULL, sff) &&
- mailfile(dlbuf, FileMailer, NULL, sff, e) == EX_OK)
- {
- int oldverb = Verbose;
-
- if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
- Verbose = 1;
- if (Verbose > 0)
- message("Saved message in %s", dlbuf);
- Verbose = oldverb;
- macdefine(&e->e_macro, A_PERM, 'g', p);
- state = ESM_DONE;
- break;
- }
- macdefine(&e->e_macro, A_PERM, 'g', p);
- state = ESM_MAIL;
- break;
-
- case ESM_DEADLETTERDROP:
- /*
- ** Log the mail in DeadLetterDrop file.
- */
-
- if (e->e_class < 0)
- {
- state = ESM_DONE;
- break;
- }
-
- if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
- DeadLetterDrop == NULL ||
- DeadLetterDrop[0] == '\0')
- {
- state = ESM_PANIC;
- break;
- }
-
- sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
- if (!writable(DeadLetterDrop, NULL, sff) ||
- (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
- FileMode, sff)) == NULL)
- {
- state = ESM_PANIC;
- break;
- }
-
- memset(&mcibuf, '\0', sizeof(mcibuf));
- mcibuf.mci_out = fp;
- mcibuf.mci_mailer = FileMailer;
- if (bitnset(M_7BITS, FileMailer->m_flags))
- mcibuf.mci_flags |= MCIF_7BIT;
-
- /* get the sender for the UnixFromLine */
- p = macvalue('g', e);
- macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
-
- if (!putfromline(&mcibuf, e) ||
- !(*e->e_puthdr)(&mcibuf, e->e_header, e,
- M87F_OUTER) ||
- !(*e->e_putbody)(&mcibuf, e, NULL) ||
- !putline("\n", &mcibuf) ||
- sm_io_flush(fp, SM_TIME_DEFAULT) == SM_IO_EOF ||
- sm_io_error(fp) ||
- sm_io_close(fp, SM_TIME_DEFAULT) < 0)
- state = ESM_PANIC;
- else
- {
- int oldverb = Verbose;
-
- if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
- Verbose = 1;
- if (Verbose > 0)
- message("Saved message in %s",
- DeadLetterDrop);
- Verbose = oldverb;
- if (LogLevel > 3)
- sm_syslog(LOG_NOTICE, e->e_id,
- "Saved message in %s",
- DeadLetterDrop);
- state = ESM_DONE;
- }
- macdefine(&e->e_macro, A_PERM, 'g', p);
- break;
-
- default:
- syserr("554 5.3.5 savemail: unknown state %d", state);
- /* FALLTHROUGH */
-
- case ESM_PANIC:
- /* leave the locked queue & transcript files around */
- loseqfile(e, "savemail panic");
- panic = true;
- errno = 0;
- syserr("554 savemail: cannot save rejected email anywhere");
- state = ESM_DONE;
- break;
- }
- }
- return panic;
-}
-/*
-** RETURNTOSENDER -- return a message to the sender with an error.
-**
-** Parameters:
-** msg -- the explanatory message.
-** returnq -- the queue of people to send the message to.
-** flags -- flags tweaking the operation:
-** RTSF_SENDBODY -- include body of message (otherwise
-** just send the header).
-** RTSF_PMBOUNCE -- this is a postmaster bounce.
-** e -- the current envelope.
-**
-** Returns:
-** zero -- if everything went ok.
-** else -- some error.
-**
-** Side Effects:
-** Returns the current message to the sender via mail.
-*/
-
-#define MAXRETURNS 6 /* max depth of returning messages */
-#define ERRORFUDGE 1024 /* nominal size of error message text */
-
-int
-returntosender(msg, returnq, flags, e)
- char *msg;
- ADDRESS *returnq;
- int flags;
- register ENVELOPE *e;
-{
- register ENVELOPE *ee;
- ENVELOPE *oldcur = CurEnv;
- ENVELOPE errenvelope;
- static int returndepth = 0;
- register ADDRESS *q;
- char *p;
- char buf[MAXNAME + 1];
-
- if (returnq == NULL)
- return -1;
-
- if (msg == NULL)
- msg = "Unable to deliver mail";
-
- if (tTd(6, 1))
- {
- sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=",
- msg, returndepth, e);
- printaddr(sm_debug_file(), returnq, true);
- if (tTd(6, 20))
- {
- sm_dprintf("Sendq=");
- printaddr(sm_debug_file(), e->e_sendqueue, true);
- }
- }
-
- if (++returndepth >= MAXRETURNS)
- {
- if (returndepth != MAXRETURNS)
- syserr("554 5.3.0 returntosender: infinite recursion on %s",
- returnq->q_paddr);
- /* don't "unrecurse" and fake a clean exit */
- /* returndepth--; */
- return 0;
- }
-
- macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
- macdefine(&e->e_macro, A_PERM, 'u', NULL);
-
- /* initialize error envelope */
- ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL));
- macdefine(&ee->e_macro, A_PERM, 'a', "\201b");
- macdefine(&ee->e_macro, A_PERM, 'r', "");
- macdefine(&ee->e_macro, A_PERM, 's', "localhost");
- macdefine(&ee->e_macro, A_PERM, '_', "localhost");
- clrsessenvelope(ee);
-
- ee->e_puthdr = putheader;
- ee->e_putbody = errbody;
- ee->e_flags |= EF_RESPONSE|EF_METOO;
- if (!bitset(EF_OLDSTYLE, e->e_flags))
- ee->e_flags &= ~EF_OLDSTYLE;
- if (bitset(EF_DONT_MIME, e->e_flags))
- {
- ee->e_flags |= EF_DONT_MIME;
-
- /*
- ** If we can't convert to MIME and we don't pass
- ** 8-bit, we can't send the body.
- */
-
- if (bitset(EF_HAS8BIT, e->e_flags) &&
- !bitset(MM_PASS8BIT, MimeMode))
- flags &= ~RTSF_SEND_BODY;
- }
-
- ee->e_sendqueue = returnq;
- ee->e_msgsize = 0;
- if (bitset(RTSF_SEND_BODY, flags) &&
- !bitset(PRIV_NOBODYRETN, PrivacyFlags))
- ee->e_msgsize = ERRORFUDGE + e->e_msgsize;
- else
- ee->e_flags |= EF_NO_BODY_RETN;
-
- if (!setnewqueue(ee))
- {
- syserr("554 5.3.0 returntosender: cannot select queue for %s",
- returnq->q_paddr);
- ExitStat = EX_UNAVAILABLE;
- returndepth--;
- return -1;
- }
- initsys(ee);
-
-#if NAMED_BIND
- _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
- _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
-#endif /* NAMED_BIND */
- for (q = returnq; q != NULL; q = q->q_next)
- {
- if (QS_IS_BADADDR(q->q_state))
- continue;
-
- q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
- q->q_flags |= QPINGONFAILURE;
-
- if (!QS_IS_DEAD(q->q_state))
- ee->e_nrcpts++;
-
- if (q->q_alias == NULL)
- addheader("To", q->q_paddr, 0, ee, true);
- }
-
- if (LogLevel > 5)
- {
- if (bitset(EF_RESPONSE, e->e_flags))
- p = "return to sender";
- else if (bitset(EF_WARNING, e->e_flags))
- p = "sender notify";
- else if (bitset(RTSF_PM_BOUNCE, flags))
- p = "postmaster notify";
- else
- p = "DSN";
- sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s",
- ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
- }
-
- if (SendMIMEErrors)
- {
- addheader("MIME-Version", "1.0", 0, ee, true);
- (void) sm_snprintf(buf, sizeof(buf), "%s.%ld/%.100s",
- ee->e_id, (long)curtime(), MyHostName);
- ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf);
- (void) sm_snprintf(buf, sizeof(buf),
-#if DSN
- "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
-#else /* DSN */
- "multipart/mixed; boundary=\"%s\"",
-#endif /* DSN */
- ee->e_msgboundary);
- addheader("Content-Type", buf, 0, ee, true);
-
- p = hvalue("Content-Transfer-Encoding", e->e_header);
- if (p != NULL && sm_strcasecmp(p, "binary") != 0)
- p = NULL;
- if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
- p = "8bit";
- if (p != NULL)
- addheader("Content-Transfer-Encoding", p, 0, ee, true);
- }
- if (strncmp(msg, "Warning:", 8) == 0)
- {
- addheader("Subject", msg, 0, ee, true);
- p = "warning-timeout";
- }
- else if (strncmp(msg, "Postmaster warning:", 19) == 0)
- {
- addheader("Subject", msg, 0, ee, true);
- p = "postmaster-warning";
- }
- else if (strcmp(msg, "Return receipt") == 0)
- {
- addheader("Subject", msg, 0, ee, true);
- p = "return-receipt";
- }
- else if (bitset(RTSF_PM_BOUNCE, flags))
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Postmaster notify: see transcript for details");
- addheader("Subject", buf, 0, ee, true);
- p = "postmaster-notification";
- }
- else
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Returned mail: see transcript for details");
- addheader("Subject", buf, 0, ee, true);
- p = "failure";
- }
- (void) sm_snprintf(buf, sizeof(buf), "auto-generated (%s)", p);
- addheader("Auto-Submitted", buf, 0, ee, true);
-
- /* fake up an address header for the from person */
- expand("\201n", buf, sizeof(buf), e);
- if (parseaddr(buf, &ee->e_from,
- RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL)
- {
- syserr("553 5.3.5 Can't parse myself!");
- ExitStat = EX_SOFTWARE;
- returndepth--;
- return -1;
- }
- ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
- ee->e_from.q_flags |= QPINGONFAILURE;
- ee->e_sender = ee->e_from.q_paddr;
-
- /* push state into submessage */
- CurEnv = ee;
- macdefine(&ee->e_macro, A_PERM, 'f', "\201n");
- macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem");
- eatheader(ee, true, true);
-
- /* mark statistics */
- markstats(ee, NULLADDR, STATS_NORMAL);
-
- /* actually deliver the error message */
- sendall(ee, SM_DELIVER);
-
- /* restore state */
- dropenvelope(ee, true, false);
- sm_rpool_free(ee->e_rpool);
- CurEnv = oldcur;
- returndepth--;
-
- /* check for delivery errors */
- if (ee->e_parent == NULL ||
- !bitset(EF_RESPONSE, ee->e_parent->e_flags))
- return 0;
- for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_ATTEMPTED(q->q_state))
- return 0;
- }
- return -1;
-}
-/*
-** ERRBODY -- output the body of an error message.
-**
-** Typically this is a copy of the transcript plus a copy of the
-** original offending message.
-**
-** Parameters:
-** mci -- the mailer connection information.
-** e -- the envelope we are working in.
-** separator -- any possible MIME separator (unused).
-**
-** Returns:
-** true iff body was written successfully
-**
-** Side Effects:
-** Outputs the body of an error message.
-*/
-
-/* ARGSUSED2 */
-static bool
-errbody(mci, e, separator)
- register MCI *mci;
- register ENVELOPE *e;
- char *separator;
-{
- bool printheader;
- bool sendbody;
- bool pm_notify;
- int save_errno;
- register SM_FILE_T *xfile;
- char *p;
- register ADDRESS *q = NULL;
- char actual[MAXLINE];
- char buf[MAXLINE];
-
- if (bitset(MCIF_INHEADER, mci->mci_flags))
- {
- if (!putline("", mci))
- goto writeerr;
- mci->mci_flags &= ~MCIF_INHEADER;
- }
- if (e->e_parent == NULL)
- {
- syserr("errbody: null parent");
- if (!putline(" ----- Original message lost -----\n", mci))
- goto writeerr;
- return true;
- }
-
- /*
- ** Output MIME header.
- */
-
- if (e->e_msgboundary != NULL)
- {
- (void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary);
- if (!putline("This is a MIME-encapsulated message", mci) ||
- !putline("", mci) ||
- !putline(buf, mci) ||
- !putline("", mci))
- goto writeerr;
- }
-
- /*
- ** Output introductory information.
- */
-
- pm_notify = false;
- p = hvalue("subject", e->e_header);
- if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
- pm_notify = true;
- else
- {
- for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_BADADDR(q->q_state))
- break;
- }
- }
- if (!pm_notify && q == NULL &&
- !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
- {
- if (!putline(" **********************************************",
- mci) ||
- !putline(" ** THIS IS A WARNING MESSAGE ONLY **",
- mci) ||
- !putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **",
- mci) ||
- !putline(" **********************************************",
- mci) ||
- !putline("", mci))
- goto writeerr;
- }
- (void) sm_snprintf(buf, sizeof(buf),
- "The original message was received at %s",
- arpadate(ctime(&e->e_parent->e_ctime)));
- if (!putline(buf, mci))
- goto writeerr;
- expand("from \201_", buf, sizeof(buf), e->e_parent);
- if (!putline(buf, mci))
- goto writeerr;
-
- /* include id in postmaster copies */
- if (pm_notify && e->e_parent->e_id != NULL)
- {
- (void) sm_strlcpyn(buf, sizeof(buf), 2, "with id ",
- e->e_parent->e_id);
- if (!putline(buf, mci))
- goto writeerr;
- }
- if (!putline("", mci))
- goto writeerr;
-
- /*
- ** Output error message header (if specified and available).
- */
-
- if (ErrMsgFile != NULL &&
- !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
- {
- if (*ErrMsgFile == '/')
- {
- long sff = SFF_ROOTOK|SFF_REGONLY;
-
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
- if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH,
- DontBlameSendmail))
- sff |= SFF_SAFEDIRPATH;
- xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
- if (xfile != NULL)
- {
- while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf,
- sizeof(buf)) != NULL)
- {
- int lbs;
- bool putok;
- char *lbp;
-
- lbs = sizeof(buf);
- lbp = translate_dollars(buf, buf, &lbs);
- expand(lbp, lbp, lbs, e);
- putok = putline(lbp, mci);
- if (lbp != buf)
- sm_free(lbp);
- if (!putok)
- goto writeerr;
- }
- (void) sm_io_close(xfile, SM_TIME_DEFAULT);
- if (!putline("\n", mci))
- goto writeerr;
- }
- }
- else
- {
- expand(ErrMsgFile, buf, sizeof(buf), e);
- if (!putline(buf, mci) || !putline("", mci))
- goto writeerr;
- }
- }
-
- /*
- ** Output message introduction
- */
-
- /* permanent fatal errors */
- printheader = true;
- for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (!QS_IS_BADADDR(q->q_state) ||
- !bitset(QPINGONFAILURE, q->q_flags))
- continue;
-
- if (printheader)
- {
- if (!putline(" ----- The following addresses had permanent fatal errors -----",
- mci))
- goto writeerr;
- printheader = false;
- }
-
- (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
- sizeof(buf));
- if (!putline(buf, mci))
- goto writeerr;
- if (q->q_rstatus != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- " (reason: %s)",
- shortenstring(exitstat(q->q_rstatus),
- MAXSHORTSTR));
- if (!putline(buf, mci))
- goto writeerr;
- }
- if (q->q_alias != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- " (expanded from: %s)",
- shortenstring(q->q_alias->q_paddr,
- MAXSHORTSTR));
- if (!putline(buf, mci))
- goto writeerr;
- }
- }
- if (!printheader && !putline("", mci))
- goto writeerr;
-
- /* transient non-fatal errors */
- printheader = true;
- for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_BADADDR(q->q_state) ||
- !bitset(QPRIMARY, q->q_flags) ||
- !bitset(QBYNDELAY, q->q_flags) ||
- !bitset(QDELAYED, q->q_flags))
- continue;
-
- if (printheader)
- {
- if (!putline(" ----- The following addresses had transient non-fatal errors -----",
- mci))
- goto writeerr;
- printheader = false;
- }
-
- (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
- sizeof(buf));
- if (!putline(buf, mci))
- goto writeerr;
- if (q->q_alias != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- " (expanded from: %s)",
- shortenstring(q->q_alias->q_paddr,
- MAXSHORTSTR));
- if (!putline(buf, mci))
- goto writeerr;
- }
- }
- if (!printheader && !putline("", mci))
- goto writeerr;
-
- /* successful delivery notifications */
- printheader = true;
- for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
- {
- if (QS_IS_BADADDR(q->q_state) ||
- !bitset(QPRIMARY, q->q_flags) ||
- bitset(QBYNDELAY, q->q_flags) ||
- bitset(QDELAYED, q->q_flags))
- continue;
- else if (bitset(QBYNRELAY, q->q_flags))
- p = "Deliver-By notify: relayed";
- else if (bitset(QBYTRACE, q->q_flags))
- p = "Deliver-By trace: relayed";
- else if (!bitset(QPINGONSUCCESS, q->q_flags))
- continue;
- else if (bitset(QRELAYED, q->q_flags))
- p = "relayed to non-DSN-aware mailer";
- else if (bitset(QDELIVERED, q->q_flags))
- {
- if (bitset(QEXPANDED, q->q_flags))
- p = "successfully delivered to mailing list";
- else
- p = "successfully delivered to mailbox";
- }
- else if (bitset(QEXPANDED, q->q_flags))
- p = "expanded by alias";
- else
- continue;
-
- if (printheader)
- {
- if (!putline(" ----- The following addresses had successful delivery notifications -----",
- mci))
- goto writeerr;
- printheader = false;
- }
-
- (void) sm_snprintf(buf, sizeof(buf), "%s (%s)",
- shortenstring(q->q_paddr, MAXSHORTSTR), p);
- if (!putline(buf, mci))
- goto writeerr;
- if (q->q_alias != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- " (expanded from: %s)",
- shortenstring(q->q_alias->q_paddr,
- MAXSHORTSTR));
- if (!putline(buf, mci))
- goto writeerr;
- }
- }
- if (!printheader && !putline("", mci))
- goto writeerr;
-
- /*
- ** Output transcript of errors
- */
-
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
- if (e->e_parent->e_xfp == NULL)
- {
- if (!putline(" ----- Transcript of session is unavailable -----\n",
- mci))
- goto writeerr;
- }
- else
- {
- printheader = true;
- (void) bfrewind(e->e_parent->e_xfp);
- if (e->e_xfp != NULL)
- (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
- while (sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT, buf,
- sizeof(buf)) != NULL)
- {
- if (printheader && !putline(" ----- Transcript of session follows -----\n",
- mci))
- goto writeerr;
- printheader = false;
- if (!putline(buf, mci))
- goto writeerr;
- }
- }
- errno = 0;
-
-#if DSN
- /*
- ** Output machine-readable version.
- */
-
- if (e->e_msgboundary != NULL)
- {
- (void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary);
- if (!putline("", mci) ||
- !putline(buf, mci) ||
- !putline("Content-Type: message/delivery-status", mci) ||
- !putline("", mci))
- goto writeerr;
-
- /*
- ** Output per-message information.
- */
-
- /* original envelope id from MAIL FROM: line */
- if (e->e_parent->e_envid != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Original-Envelope-Id: %.800s",
- xuntextify(e->e_parent->e_envid));
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- /* Reporting-MTA: is us (required) */
- (void) sm_snprintf(buf, sizeof(buf),
- "Reporting-MTA: dns; %.800s", MyHostName);
- if (!putline(buf, mci))
- goto writeerr;
-
- /* DSN-Gateway: not relevant since we are not translating */
-
- /* Received-From-MTA: shows where we got this message from */
- if (RealHostName != NULL)
- {
- /* XXX use $s for type? */
- if (e->e_parent->e_from.q_mailer == NULL ||
- (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
- p = "dns";
- (void) sm_snprintf(buf, sizeof(buf),
- "Received-From-MTA: %s; %.800s",
- p, RealHostName);
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- /* Arrival-Date: -- when it arrived here */
- (void) sm_strlcpyn(buf, sizeof(buf), 2, "Arrival-Date: ",
- arpadate(ctime(&e->e_parent->e_ctime)));
- if (!putline(buf, mci))
- goto writeerr;
-
- /* Deliver-By-Date: -- when it should have been delivered */
- if (IS_DLVR_BY(e->e_parent))
- {
- time_t dbyd;
-
- dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by;
- (void) sm_strlcpyn(buf, sizeof(buf), 2,
- "Deliver-By-Date: ",
- arpadate(ctime(&dbyd)));
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- /*
- ** Output per-address information.
- */
-
- for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
- {
- char *action;
-
- if (QS_IS_BADADDR(q->q_state))
- {
- /* RFC 1891, 6.2.6 (b) */
- if (bitset(QHASNOTIFY, q->q_flags) &&
- !bitset(QPINGONFAILURE, q->q_flags))
- continue;
- action = "failed";
- }
- else if (!bitset(QPRIMARY, q->q_flags))
- continue;
- else if (bitset(QDELIVERED, q->q_flags))
- {
- if (bitset(QEXPANDED, q->q_flags))
- action = "delivered (to mailing list)";
- else
- action = "delivered (to mailbox)";
- }
- else if (bitset(QRELAYED, q->q_flags))
- action = "relayed (to non-DSN-aware mailer)";
- else if (bitset(QEXPANDED, q->q_flags))
- action = "expanded (to multi-recipient alias)";
- else if (bitset(QDELAYED, q->q_flags))
- action = "delayed";
- else if (bitset(QBYTRACE, q->q_flags))
- action = "relayed (Deliver-By trace mode)";
- else if (bitset(QBYNDELAY, q->q_flags))
- action = "delayed (Deliver-By notify mode)";
- else if (bitset(QBYNRELAY, q->q_flags))
- action = "relayed (Deliver-By notify mode)";
- else
- continue;
-
- if (!putline("", mci))
- goto writeerr;
-
- /* Original-Recipient: -- passed from on high */
- if (q->q_orcpt != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Original-Recipient: %.800s",
- q->q_orcpt);
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- /* Figure out actual recipient */
- actual[0] = '\0';
- if (q->q_user[0] != '\0')
- {
- if (q->q_mailer != NULL &&
- q->q_mailer->m_addrtype != NULL)
- p = q->q_mailer->m_addrtype;
- else
- p = "rfc822";
-
- if (sm_strcasecmp(p, "rfc822") == 0 &&
- strchr(q->q_user, '@') == NULL)
- {
- (void) sm_snprintf(actual,
- sizeof(actual),
- "%s; %.700s@%.100s",
- p, q->q_user,
- MyHostName);
- }
- else
- {
- (void) sm_snprintf(actual,
- sizeof(actual),
- "%s; %.800s",
- p, q->q_user);
- }
- }
-
- /* Final-Recipient: -- the name from the RCPT command */
- if (q->q_finalrcpt == NULL)
- {
- /* should never happen */
- sm_syslog(LOG_ERR, e->e_id,
- "returntosender: q_finalrcpt is NULL");
-
- /* try to fall back to the actual recipient */
- if (actual[0] != '\0')
- q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool,
- actual);
- }
-
- if (q->q_finalrcpt != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Final-Recipient: %s",
- q->q_finalrcpt);
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- /* X-Actual-Recipient: -- the real problem address */
- if (actual[0] != '\0' &&
- q->q_finalrcpt != NULL &&
- !bitset(PRIV_NOACTUALRECIPIENT, PrivacyFlags) &&
- strcmp(actual, q->q_finalrcpt) != 0)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "X-Actual-Recipient: %s",
- actual);
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- /* Action: -- what happened? */
- (void) sm_strlcpyn(buf, sizeof(buf), 2, "Action: ",
- action);
- if (!putline(buf, mci))
- goto writeerr;
-
- /* Status: -- what _really_ happened? */
- if (q->q_status != NULL)
- p = q->q_status;
- else if (QS_IS_BADADDR(q->q_state))
- p = "5.0.0";
- else if (QS_IS_QUEUEUP(q->q_state))
- p = "4.0.0";
- else
- p = "2.0.0";
- (void) sm_strlcpyn(buf, sizeof(buf), 2, "Status: ", p);
- if (!putline(buf, mci))
- goto writeerr;
-
- /* Remote-MTA: -- who was I talking to? */
- if (q->q_statmta != NULL)
- {
- if (q->q_mailer == NULL ||
- (p = q->q_mailer->m_mtatype) == NULL)
- p = "dns";
- (void) sm_snprintf(buf, sizeof(buf),
- "Remote-MTA: %s; %.800s",
- p, q->q_statmta);
- p = &buf[strlen(buf) - 1];
- if (*p == '.')
- *p = '\0';
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- /* Diagnostic-Code: -- actual result from other end */
- if (q->q_rstatus != NULL)
- {
- if (q->q_mailer == NULL ||
- (p = q->q_mailer->m_diagtype) == NULL)
- p = "smtp";
- (void) sm_snprintf(buf, sizeof(buf),
- "Diagnostic-Code: %s; %.800s",
- p, q->q_rstatus);
- if (!putline(buf, mci))
- goto writeerr;
- }
-
- /* Last-Attempt-Date: -- fine granularity */
- if (q->q_statdate == (time_t) 0L)
- q->q_statdate = curtime();
- (void) sm_strlcpyn(buf, sizeof(buf), 2,
- "Last-Attempt-Date: ",
- arpadate(ctime(&q->q_statdate)));
- if (!putline(buf, mci))
- goto writeerr;
-
- /* Will-Retry-Until: -- for delayed messages only */
- if (QS_IS_QUEUEUP(q->q_state))
- {
- time_t xdate;
-
- xdate = e->e_parent->e_ctime +
- TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
- (void) sm_strlcpyn(buf, sizeof(buf), 2,
- "Will-Retry-Until: ",
- arpadate(ctime(&xdate)));
- if (!putline(buf, mci))
- goto writeerr;
- }
- }
- }
-#endif /* DSN */
-
- /*
- ** Output text of original message
- */
-
- if (!putline("", mci))
- goto writeerr;
- if (bitset(EF_HAS_DF, e->e_parent->e_flags))
- {
- sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
- !bitset(EF_NO_BODY_RETN, e->e_flags);
-
- if (e->e_msgboundary == NULL)
- {
- if (!putline(
- sendbody
- ? " ----- Original message follows -----\n"
- : " ----- Message header follows -----\n",
- mci))
- {
- goto writeerr;
- }
- }
- else
- {
- (void) sm_strlcpyn(buf, sizeof(buf), 2, "--",
- e->e_msgboundary);
-
- if (!putline(buf, mci))
- goto writeerr;
- (void) sm_strlcpyn(buf, sizeof(buf), 2, "Content-Type: ",
- sendbody ? "message/rfc822"
- : "text/rfc822-headers");
- if (!putline(buf, mci))
- goto writeerr;
-
- p = hvalue("Content-Transfer-Encoding",
- e->e_parent->e_header);
- if (p != NULL && sm_strcasecmp(p, "binary") != 0)
- p = NULL;
- if (p == NULL &&
- bitset(EF_HAS8BIT, e->e_parent->e_flags))
- p = "8bit";
- if (p != NULL)
- {
- (void) sm_snprintf(buf, sizeof(buf),
- "Content-Transfer-Encoding: %s",
- p);
- if (!putline(buf, mci))
- goto writeerr;
- }
- }
- if (!putline("", mci))
- goto writeerr;
- save_errno = errno;
- if (!putheader(mci, e->e_parent->e_header, e->e_parent,
- M87F_OUTER))
- goto writeerr;
- errno = save_errno;
- if (sendbody)
- {
- if (!putbody(mci, e->e_parent, e->e_msgboundary))
- goto writeerr;
- }
- else if (e->e_msgboundary == NULL)
- {
- if (!putline("", mci) ||
- !putline(" ----- Message body suppressed -----",
- mci))
- {
- goto writeerr;
- }
- }
- }
- else if (e->e_msgboundary == NULL)
- {
- if (!putline(" ----- No message was collected -----\n", mci))
- goto writeerr;
- }
-
- if (e->e_msgboundary != NULL)
- {
- (void) sm_strlcpyn(buf, sizeof(buf), 3, "--", e->e_msgboundary,
- "--");
- if (!putline("", mci) || !putline(buf, mci))
- goto writeerr;
- }
- if (!putline("", mci) ||
- sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF)
- goto writeerr;
-
- /*
- ** Cleanup and exit
- */
-
- if (errno != 0)
- {
- writeerr:
- syserr("errbody: I/O error");
- return false;
- }
- return true;
-}
-
-/*
-** SMTPTODSN -- convert SMTP to DSN status code
-**
-** Parameters:
-** smtpstat -- the smtp status code (e.g., 550).
-**
-** Returns:
-** The DSN version of the status code.
-**
-** Storage Management:
-** smtptodsn() returns a pointer to a character string literal,
-** which will remain valid forever, and thus does not need to
-** be copied. Current code relies on this property.
-*/
-
-char *
-smtptodsn(smtpstat)
- int smtpstat;
-{
- if (smtpstat < 0)
- return "4.4.2";
-
- switch (smtpstat)
- {
- case 450: /* Req mail action not taken: mailbox unavailable */
- return "4.2.0";
-
- case 451: /* Req action aborted: local error in processing */
- return "4.3.0";
-
- case 452: /* Req action not taken: insufficient sys storage */
- return "4.3.1";
-
- case 500: /* Syntax error, command unrecognized */
- return "5.5.2";
-
- case 501: /* Syntax error in parameters or arguments */
- return "5.5.4";
-
- case 502: /* Command not implemented */
- return "5.5.1";
-
- case 503: /* Bad sequence of commands */
- return "5.5.1";
-
- case 504: /* Command parameter not implemented */
- return "5.5.4";
-
- case 550: /* Req mail action not taken: mailbox unavailable */
- return "5.2.0";
-
- case 551: /* User not local; please try <...> */
- return "5.1.6";
-
- case 552: /* Req mail action aborted: exceeded storage alloc */
- return "5.2.2";
-
- case 553: /* Req action not taken: mailbox name not allowed */
- return "5.1.0";
-
- case 554: /* Transaction failed */
- return "5.0.0";
- }
-
- if (REPLYTYPE(smtpstat) == 2)
- return "2.0.0";
- if (REPLYTYPE(smtpstat) == 4)
- return "4.0.0";
- return "5.0.0";
-}
-/*
-** XTEXTIFY -- take regular text and turn it into DSN-style xtext
-**
-** Parameters:
-** t -- the text to convert.
-** taboo -- additional characters that must be encoded.
-**
-** Returns:
-** The xtext-ified version of the same string.
-*/
-
-char *
-xtextify(t, taboo)
- register char *t;
- char *taboo;
-{
- register char *p;
- int l;
- int nbogus;
- static char *bp = NULL;
- static int bplen = 0;
-
- if (taboo == NULL)
- taboo = "";
-
- /* figure out how long this xtext will have to be */
- nbogus = l = 0;
- for (p = t; *p != '\0'; p++)
- {
- register int c = (*p & 0xff);
-
- /* ASCII dependence here -- this is the way the spec words it */
- if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
- strchr(taboo, c) != NULL)
- nbogus++;
- l++;
- }
- if (nbogus < 0)
- {
- /* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */
- syserr("!xtextify string too long");
- }
- if (nbogus == 0)
- return t;
- l += nbogus * 2 + 1;
-
- /* now allocate space if necessary for the new string */
- if (l > bplen)
- {
- if (bp != NULL)
- sm_free(bp); /* XXX */
- bp = sm_pmalloc_x(l);
- bplen = l;
- }
-
- /* ok, copy the text with byte expansion */
- for (p = bp; *t != '\0'; )
- {
- register int c = (*t++ & 0xff);
-
- /* ASCII dependence here -- this is the way the spec words it */
- if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
- strchr(taboo, c) != NULL)
- {
- *p++ = '+';
- *p++ = "0123456789ABCDEF"[c >> 4];
- *p++ = "0123456789ABCDEF"[c & 0xf];
- }
- else
- *p++ = c;
- }
- *p = '\0';
- return bp;
-}
-/*
-** XUNTEXTIFY -- take xtext and turn it into plain text
-**
-** Parameters:
-** t -- the xtextified text.
-**
-** Returns:
-** The decoded text. No attempt is made to deal with
-** null strings in the resulting text.
-*/
-
-char *
-xuntextify(t)
- register char *t;
-{
- register char *p;
- int l;
- static char *bp = NULL;
- static int bplen = 0;
-
- /* heuristic -- if no plus sign, just return the input */
- if (strchr(t, '+') == NULL)
- return t;
-
- /* xtext is always longer than decoded text */
- l = strlen(t);
- if (l > bplen)
- {
- if (bp != NULL)
- sm_free(bp); /* XXX */
- bp = xalloc(l);
- bplen = l;
- }
-
- /* ok, copy the text with byte compression */
- for (p = bp; *t != '\0'; t++)
- {
- register int c = *t & 0xff;
-
- if (c != '+')
- {
- *p++ = c;
- continue;
- }
-
- c = *++t & 0xff;
- if (!isascii(c) || !isxdigit(c))
- {
- /* error -- first digit is not hex */
- usrerr("bogus xtext: +%c", c);
- t--;
- continue;
- }
- if (isdigit(c))
- c -= '0';
- else if (isupper(c))
- c -= 'A' - 10;
- else
- c -= 'a' - 10;
- *p = c << 4;
-
- c = *++t & 0xff;
- if (!isascii(c) || !isxdigit(c))
- {
- /* error -- second digit is not hex */
- usrerr("bogus xtext: +%x%c", *p >> 4, c);
- t--;
- continue;
- }
- if (isdigit(c))
- c -= '0';
- else if (isupper(c))
- c -= 'A' - 10;
- else
- c -= 'a' - 10;
- *p++ |= c;
- }
- *p = '\0';
- return bp;
-}
-/*
-** XTEXTOK -- check if a string is legal xtext
-**
-** Xtext is used in Delivery Status Notifications. The spec was
-** taken from RFC 1891, ``SMTP Service Extension for Delivery
-** Status Notifications''.
-**
-** Parameters:
-** s -- the string to check.
-**
-** Returns:
-** true -- if 's' is legal xtext.
-** false -- if it has any illegal characters in it.
-*/
-
-bool
-xtextok(s)
- char *s;
-{
- int c;
-
- while ((c = *s++) != '\0')
- {
- if (c == '+')
- {
- c = *s++;
- if (!isascii(c) || !isxdigit(c))
- return false;
- c = *s++;
- if (!isascii(c) || !isxdigit(c))
- return false;
- }
- else if (c < '!' || c > '~' || c == '=')
- return false;
- }
- return true;
-}
-/*
-** PRUNEROUTE -- prune an RFC-822 source route
-**
-** Trims down a source route to the last internet-registered hop.
-** This is encouraged by RFC 1123 section 5.3.3.
-**
-** Parameters:
-** addr -- the address
-**
-** Returns:
-** true -- address was modified
-** false -- address could not be pruned
-**
-** Side Effects:
-** modifies addr in-place
-*/
-
-static bool
-pruneroute(addr)
- char *addr;
-{
-#if NAMED_BIND
- char *start, *at, *comma;
- char c;
- int braclev;
- int rcode;
- int i;
- char hostbuf[BUFSIZ];
- char *mxhosts[MAXMXHOSTS + 1];
-
- /* check to see if this is really a route-addr */
- if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
- return false;
-
- /*
- ** Can't simply find the first ':' is the address might be in the
- ** form: "<@[IPv6:::1]:user@host>" and the first ':' in inside
- ** the IPv6 address.
- */
-
- start = addr;
- braclev = 0;
- while (*start != '\0')
- {
- if (*start == ':' && braclev <= 0)
- break;
- else if (*start == '[')
- braclev++;
- else if (*start == ']' && braclev > 0)
- braclev--;
- start++;
- }
- if (braclev > 0 || *start != ':')
- return false;
-
- at = strrchr(addr, '@');
- if (at == NULL || at < start)
- return false;
-
- /* slice off the angle brackets */
- i = strlen(at + 1);
- if (i >= sizeof(hostbuf))
- return false;
- (void) sm_strlcpy(hostbuf, at + 1, sizeof(hostbuf));
- hostbuf[i - 1] = '\0';
-
- while (start != NULL)
- {
- if (getmxrr(hostbuf, mxhosts, NULL, false,
- &rcode, true, NULL) > 0)
- {
- (void) sm_strlcpy(addr + 1, start + 1,
- strlen(addr) - 1);
- return true;
- }
- c = *start;
- *start = '\0';
- comma = strrchr(addr, ',');
- if (comma != NULL && comma[1] == '@' &&
- strlen(comma + 2) < sizeof(hostbuf))
- (void) sm_strlcpy(hostbuf, comma + 2, sizeof(hostbuf));
- else
- comma = NULL;
- *start = c;
- start = comma;
- }
-#endif /* NAMED_BIND */
- return false;
-}
diff --git a/contrib/sendmail/src/sendmail.8 b/contrib/sendmail/src/sendmail.8
deleted file mode 100644
index 27b44d6..0000000
--- a/contrib/sendmail/src/sendmail.8
+++ /dev/null
@@ -1,748 +0,0 @@
-.\" Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
-.\" All rights reserved.
-.\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved.
-.\" Copyright (c) 1988, 1991, 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.
-.\"
-.\"
-.\" $Id: sendmail.8,v 8.58 2007/08/02 05:42:33 ca Exp $
-.\"
-.TH SENDMAIL 8 "$Date: 2007/08/02 05:42:33 $"
-.SH NAME
-sendmail
-\- an electronic mail transport agent
-.SH SYNOPSIS
-.B sendmail
-.RI [ flags "] [" "address ..." ]
-.br
-.B newaliases
-.br
-.B mailq
-.RB [ \-v ]
-.br
-.B hoststat
-.br
-.B purgestat
-.br
-.B smtpd
-.SH DESCRIPTION
-.B Sendmail
-sends a message to one or more
-.I recipients,
-routing the message over whatever networks
-are necessary.
-.B Sendmail
-does internetwork forwarding as necessary
-to deliver the message to the correct place.
-.PP
-.B Sendmail
-is not intended as a user interface routine;
-other programs provide user-friendly
-front ends;
-.B sendmail
-is used only to deliver pre-formatted messages.
-.PP
-With no flags,
-.B sendmail
-reads its standard input
-up to an end-of-file
-or a line consisting only of a single dot
-and sends a copy of the message found there
-to all of the addresses listed.
-It determines the network(s) to use
-based on the syntax and contents of the addresses.
-.PP
-Local addresses are looked up in a file
-and aliased appropriately.
-Aliasing can be prevented by preceding the address
-with a backslash.
-Beginning with 8.10, the sender is included in any alias
-expansions, e.g.,
-if `john' sends to `group',
-and `group' includes `john' in the expansion,
-then the letter will also be delivered to `john'.
-.SS Parameters
-.TP
-.B \-Ac
-Use submit.cf even if the operation mode does not indicate
-an initial mail submission.
-.TP
-.B \-Am
-Use sendmail.cf even if the operation mode indicates
-an initial mail submission.
-.TP
-.BI \-B type
-Set the body type to
-.IR type .
-Current legal values are
-7BIT
-or
-8BITMIME.
-.TP
-.B \-ba
-Go into
-ARPANET
-mode. All input lines must end with a CR-LF,
-and all messages will be generated with a CR-LF at the end.
-Also,
-the ``From:'' and ``Sender:''
-fields are examined for the name of the sender.
-.TP
-.B \-bd
-Run as a daemon.
-.B Sendmail
-will fork and run in background
-listening on socket 25 for incoming
-SMTP
-connections.
-This is normally run from
-/etc/rc.
-.TP
-.B \-bD
-Same as
-.B \-bd
-except runs in foreground.
-.TP
-.B \-bh
-Print the persistent host status database.
-.TP
-.B \-bH
-Purge expired entries from the persistent host status database.
-.TP
-.B \-bi
-Initialize the alias database.
-.TP
-.B \-bm
-Deliver mail in the usual way (default).
-.TP
-.B \-bp
-Print a listing of the queue(s).
-.TP
-.B \-bP
-Print number of entries in the queue(s);
-only available with shared memory support.
-.TP
-.B \-bs
-Use the
-SMTP
-protocol as described in
-RFC821
-on standard input and output.
-This flag implies all the operations of the
-.B \-ba
-flag that are compatible with
-SMTP.
-.TP
-.B \-bt
-Run in address test mode.
-This mode reads addresses and shows the steps in parsing;
-it is used for debugging configuration tables.
-.TP
-.B \-bv
-Verify names only \- do not try to collect or deliver a message.
-Verify mode is normally used for validating
-users or mailing lists.
-.TP
-.BI \-C file
-Use alternate configuration file.
-.B Sendmail
-gives up any enhanced (set-user-ID or set-group-ID) privileges
-if an alternate configuration file is specified.
-.TP
-.BI "\-D " logfile
-Send debugging output to the indicated log file instead of stdout.
-.TP
-.BI \-d category . level...
-Set the debugging flag for
-.I category
-to
-.IR level .
-.I Category
-is either an integer or a name specifying the topic, and
-.I level
-an integer specifying the level of debugging output desired.
-Higher levels generally mean more output.
-More than one flag can be specified by separating them with commas.
-A list of numeric debugging categories can be found in the TRACEFLAGS file
-in the sendmail source distribution.
-.br
-The option
-.B \-d0.1
-prints the version of
-.B sendmail
-and the options it was compiled with.
-.br
-Most other categories are only useful with, and documented in,
-.BR sendmail 's
-source code.
-.ne 1i
-.TP
-.BI \-F fullname
-Set the full name of the sender.
-.TP
-.BI \-f name
-Sets the name of the ``from'' person
-(i.e., the envelope sender of the mail).
-This address may also be used in the From: header
-if that header is missing during initial submission.
-The envelope sender address is used as the recipient
-for delivery status notifications
-and may also appear in a Return-Path: header.
-.B \-f
-should only be used
-by ``trusted'' users
-(normally
-.IR root ", " daemon ,
-and
-.IR network )
-or if the person you are trying to become
-is the same as the person you are.
-Otherwise,
-an X-Authentication-Warning header
-will be added to the message.
-.TP
-.BI \-G
-Relay (gateway) submission of a message,
-e.g., when
-.BR rmail
-calls
-.B sendmail .
-.TP
-.BI \-h N
-Set the hop count to
-.IR N .
-The hop count is incremented every time the mail is
-processed.
-When it reaches a limit,
-the mail is returned with an error message,
-the victim of an aliasing loop.
-If not specified,
-``Received:'' lines in the message are counted.
-.TP
-.B \-i
-Ignore dots alone on lines by themselves in incoming messages.
-This should be set if you are reading data from a file.
-.TP
-.BI "\-L " tag
-Set the identifier used in syslog messages to the supplied
-.IR tag .
-.TP
-.BI "\-N " dsn
-Set delivery status notification conditions to
-.IR dsn ,
-which can be
-`never'
-for no notifications
-or a comma separated list of the values
-`failure'
-to be notified if delivery failed,
-`delay'
-to be notified if delivery is delayed, and
-`success'
-to be notified when the message is successfully delivered.
-.TP
-.B \-n
-Don't do aliasing.
-.TP
-\fB\-O\fP \fIoption\fR=\fIvalue\fR
-Set option
-.I option
-to the specified
-.IR value .
-This form uses long names. See below for more details.
-.TP
-.BI \-o "x value"
-Set option
-.I x
-to the specified
-.IR value .
-This form uses single character names only.
-The short names are not described in this manual page;
-see the
-.I "Sendmail Installation and Operation Guide"
-for details.
-.TP
-.BI \-p protocol
-Set the name of the protocol used to receive the message.
-This can be a simple protocol name such as ``UUCP''
-or a protocol and hostname, such as ``UUCP:ucbvax''.
-.TP
-\fB\-q\fR[\fItime\fR]
-Process saved messages in the queue at given intervals.
-If
-.I time
-is omitted, process the queue once.
-.I Time
-is given as a tagged number,
-with
-`s'
-being seconds,
-`m'
-being minutes (default),
-`h'
-being hours,
-`d'
-being days,
-and
-`w'
-being weeks.
-For example,
-`\-q1h30m'
-or
-`\-q90m'
-would both set the timeout to one hour thirty minutes.
-By default,
-.B sendmail
-will run in the background.
-This option can be used safely with
-.BR \-bd .
-.TP
-\fB\-qp\fR[\fItime\fR]
-Similar to \fB\-q\fItime\fR,
-except that instead of periodically forking a child to process the queue,
-sendmail forks a single persistent child for each queue
-that alternates between processing the queue and sleeping.
-The sleep time is given as the argument; it defaults to 1 second.
-The process will always sleep at least 5 seconds if the queue was
-empty in the previous queue run.
-.TP
-\fB\-q\fRf
-Process saved messages in the queue once and do not fork(),
-but run in the foreground.
-.TP
-\fB\-q\fRG\fIname\fR
-Process jobs in queue group called
-.I name
-only.
-.TP
-\fB\-q\fR[\fI!\fR]I\fIsubstr\fR
-Limit processed jobs to those containing
-.I substr
-as a substring of the queue id or not when
-.I !
-is specified.
-.TP
-\fB\-q\fR[\fI!\fR]Q\fIsubstr\fR
-Limit processed jobs to quarantined jobs containing
-.I substr
-as a substring of the quarantine reason or not when
-.I !
-is specified.
-.TP
-\fB\-q\fR[\fI!\fR]R\fIsubstr\fR
-Limit processed jobs to those containing
-.I substr
-as a substring of one of the recipients or not when
-.I !
-is specified.
-.TP
-\fB\-q\fR[\fI!\fR]S\fIsubstr\fR
-Limit processed jobs to those containing
-.I substr
-as a substring of the sender or not when
-.I !
-is specified.
-.TP
-\fB\-Q\fR[reason]
-Quarantine a normal queue items with the given reason or
-unquarantine quarantined queue items if no reason is given.
-This should only be used with some sort of item matching using
-as described above.
-.TP
-.BI "\-R " return
-Set the amount of the message to be returned
-if the message bounces.
-The
-.I return
-parameter can be
-`full'
-to return the entire message or
-`hdrs'
-to return only the headers.
-In the latter case also local bounces return only the headers.
-.TP
-.BI \-r name
-An alternate and obsolete form of the
-.B \-f
-flag.
-.TP
-.B \-t
-Read message for recipients.
-To:, Cc:, and Bcc: lines will be scanned for recipient addresses.
-The Bcc: line will be deleted before transmission.
-.TP
-.BI "\-V " envid
-Set the original envelope id.
-This is propagated across SMTP to servers that support DSNs
-and is returned in DSN-compliant error messages.
-.TP
-.B \-v
-Go into verbose mode.
-Alias expansions will be announced, etc.
-.TP
-.BI "\-X " logfile
-Log all traffic in and out of mailers in the indicated log file.
-This should only be used as a last resort
-for debugging mailer bugs.
-It will log a lot of data very quickly.
-.TP
-.B \-\-
-Stop processing command flags and use the rest of the arguments as
-addresses.
-.SS Options
-There are also a number of processing options that may be set.
-Normally these will only be used by a system administrator.
-Options may be set either on the command line
-using the
-.B \-o
-flag (for short names), the
-.B \-O
-flag (for long names),
-or in the configuration file.
-This is a partial list limited to those options that are likely to be useful
-on the command line
-and only shows the long names;
-for a complete list (and details), consult the
-.IR "Sendmail Installation and Operation Guide" .
-The options are:
-.TP
-.RI AliasFile= file
-Use alternate alias file.
-.TP
-HoldExpensive
-On mailers that are considered ``expensive'' to connect to,
-don't initiate immediate connection.
-This requires queueing.
-.TP
-.RI CheckpointInterval= N
-Checkpoint the queue file after every
-.I N
-successful deliveries (default 10).
-This avoids excessive duplicate deliveries
-when sending to long mailing lists
-interrupted by system crashes.
-.ne 1i
-.TP
-.RI DeliveryMode= x
-Set the delivery mode to
-.IR x .
-Delivery modes are
-`i'
-for interactive (synchronous) delivery,
-`b'
-for background (asynchronous) delivery,
-`q'
-for queue only \- i.e.,
-actual delivery is done the next time the queue is run, and
-`d'
-for deferred \- the same as
-`q'
-except that database lookups for maps which have set the \-D option
-(default for the host map) are avoided.
-.TP
-.RI ErrorMode= x
-Set error processing to mode
-.IR x .
-Valid modes are
-`m'
-to mail back the error message,
-`w'
-to ``write''
-back the error message
-(or mail it back if the sender is not logged in),
-`p'
-to print the errors on the terminal
-(default),
-`q'
-to throw away error messages
-(only exit status is returned),
-and
-`e'
-to do special processing for the BerkNet.
-If the text of the message is not mailed back
-by
-modes
-`m'
-or
-`w'
-and if the sender is local to this machine,
-a copy of the message is appended to the file
-.I dead.letter
-in the sender's home directory.
-.TP
-SaveFromLine
-Save
-UNIX-style
-From lines at the front of messages.
-.TP
-.RI MaxHopCount= N
-The maximum number of times a message is allowed to ``hop''
-before we decide it is in a loop.
-.TP
-IgnoreDots
-Do not take dots on a line by themselves
-as a message terminator.
-.TP
-SendMimeErrors
-Send error messages in MIME format.
-If not set, the DSN (Delivery Status Notification) SMTP extension
-is disabled.
-.TP
-.RI ConnectionCacheTimeout= timeout
-Set connection cache timeout.
-.TP
-.RI ConnectionCacheSize= N
-Set connection cache size.
-.TP
-.RI LogLevel= n
-The log level.
-.TP
-.RI MeToo= False
-Don't send to ``me'' (the sender) if I am in an alias expansion.
-.TP
-CheckAliases
-Validate the right hand side of aliases during a
-newaliases(1)
-command.
-.TP
-OldStyleHeaders
-If set, this message may have
-old style headers.
-If not set,
-this message is guaranteed to have new style headers
-(i.e., commas instead of spaces between addresses).
-If set, an adaptive algorithm is used that will correctly
-determine the header format in most cases.
-.TP
-.RI QueueDirectory= queuedir
-Select the directory in which to queue messages.
-.TP
-.RI StatusFile= file
-Save statistics in the named file.
-.TP
-.RI Timeout.queuereturn= time
-Set the timeout on undelivered messages in the queue to the specified time.
-After delivery has failed
-(e.g., because of a host being down)
-for this amount of time,
-failed messages will be returned to the sender.
-The default is five days.
-.TP
-.RI UserDatabaseSpec= userdatabase
-If set, a user database is consulted to get forwarding information.
-You can consider this an adjunct to the aliasing mechanism,
-except that the database is intended to be distributed;
-aliases are local to a particular host.
-This may not be available if your sendmail does not have the
-USERDB
-option compiled in.
-.TP
-ForkEachJob
-Fork each job during queue runs.
-May be convenient on memory-poor machines.
-.TP
-SevenBitInput
-Strip incoming messages to seven bits.
-.TP
-.RI EightBitMode= mode
-Set the handling of eight bit input to seven bit destinations to
-.IR mode :
-m
-(mimefy) will convert to seven-bit MIME format,
-p
-(pass) will pass it as eight bits (but violates protocols),
-and
-s
-(strict) will bounce the message.
-.TP
-.RI MinQueueAge= timeout
-Sets how long a job must ferment in the queue between attempts to send it.
-.TP
-.RI DefaultCharSet= charset
-Sets the default character set used to label 8-bit data
-that is not otherwise labelled.
-.TP
-.RI DialDelay= sleeptime
-If opening a connection fails,
-sleep for
-.I sleeptime
-seconds and try again.
-Useful on dial-on-demand sites.
-.TP
-.RI NoRecipientAction= action
-Set the behaviour when there are no recipient headers (To:, Cc: or
-Bcc:) in the message to
-.IR action :
-none
-leaves the message unchanged,
-add-to
-adds a To: header with the envelope recipients,
-add-apparently-to
-adds an Apparently-To: header with the envelope recipients,
-add-bcc
-adds an empty Bcc: header, and
-add-to-undisclosed
-adds a header reading
-`To: undisclosed-recipients:;'.
-.TP
-.RI MaxDaemonChildren= N
-Sets the maximum number of children that an incoming SMTP daemon
-will allow to spawn at any time to
-.IR N .
-.TP
-.RI ConnectionRateThrottle= N
-Sets the maximum number of connections per second to the SMTP port to
-.IR N .
-.PP
-In aliases,
-the first character of a name may be
-a vertical bar to cause interpretation of
-the rest of the name as a command
-to pipe the mail to.
-It may be necessary to quote the name
-to keep
-.B sendmail
-from suppressing the blanks from between arguments.
-For example, a common alias is:
-.IP
-msgs: "|/usr/bin/msgs -s"
-.PP
-Aliases may also have the syntax
-.RI ``:include: filename ''
-to ask
-.B sendmail
-to read the named file for a list of recipients.
-For example, an alias such as:
-.IP
-poets: ":include:/usr/local/lib/poets.list"
-.PP
-would read
-.I /usr/local/lib/poets.list
-for the list of addresses making up the group.
-.PP
-.B Sendmail
-returns an exit status
-describing what it did.
-The codes are defined in
-.RI < sysexits.h >:
-.TP
-EX_OK
-Successful completion on all addresses.
-.TP
-EX_NOUSER
-User name not recognized.
-.TP
-EX_UNAVAILABLE
-Catchall meaning necessary resources
-were not available.
-.TP
-EX_SYNTAX
-Syntax error in address.
-.TP
-EX_SOFTWARE
-Internal software error,
-including bad arguments.
-.TP
-EX_OSERR
-Temporary operating system error,
-such as
-``cannot fork''.
-.TP
-EX_NOHOST
-Host name not recognized.
-.TP
-EX_TEMPFAIL
-Message could not be sent immediately,
-but was queued.
-.PP
-If invoked as
-.BR newaliases ,
-.B sendmail
-will rebuild the alias database. If invoked as
-.BR mailq ,
-.B sendmail
-will print the contents of the mail queue.
-If invoked as
-.BR hoststat ,
-.B sendmail
-will print the persistent host status database.
-If invoked as
-.BR purgestat ,
-.B sendmail
-will purge expired entries from the persistent host status database.
-If invoked as
-.BR smtpd ,
-.B sendmail
-will act as a daemon, as if the
-.B \-bd
-option were specified.
-.SH NOTES
-.B sendmail
-often gets blamed for many problems
-that are actually the result of other problems,
-such as overly permissive modes on directories.
-For this reason,
-.B sendmail
-checks the modes on system directories and files
-to determine if they can be trusted.
-Although these checks can be turned off
-and your system security reduced by setting the
-.BR DontBlameSendmail
-option,
-the permission problems should be fixed.
-For more information, see:
-
-.I http://www.sendmail.org/tips/DontBlameSendmail.html
-.SH FILES
-Except for the file
-.I /etc/mail/sendmail.cf
-itself the following pathnames are all specified in
-.IR /etc/mail/sendmail.cf .
-Thus,
-these values are only approximations.
-.PP
-.TP
- /etc/mail/aliases
-raw data for alias names
-.TP
- /etc/mail/aliases.db
-data base of alias names
-.TP
- /etc/mail/sendmail.cf
-configuration file
-.TP
- /etc/mail/helpfile
-help file
-.TP
- /etc/mail/statistics
-collected statistics
-.TP
- /var/spool/mqueue/*
-temp files
-.SH SEE ALSO
-binmail(1),
-mail(1),
-rmail(1),
-syslog(3),
-aliases(5),
-mailaddr(7),
-rc(8)
-.PP
-DARPA
-Internet Request For Comments
-.IR RFC819 ,
-.IR RFC821 ,
-.IR RFC822 .
-.IR "Sendmail Installation and Operation Guide" ,
-No. 8, SMM.
-.PP
-http://www.sendmail.org/
-.SH HISTORY
-The
-.B sendmail
-command appeared in
-4.2BSD.
diff --git a/contrib/sendmail/src/sendmail.h b/contrib/sendmail/src/sendmail.h
deleted file mode 100644
index 82ab6a9..0000000
--- a/contrib/sendmail/src/sendmail.h
+++ /dev/null
@@ -1,2656 +0,0 @@
-/*
- * Copyright (c) 1998-2007 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.
- */
-
-/*
-** SENDMAIL.H -- MTA-specific definitions for sendmail.
-*/
-
-#ifndef _SENDMAIL_H
-# define _SENDMAIL_H 1
-
-#ifndef MILTER
-# define MILTER 1 /* turn on MILTER by default */
-#endif /* MILTER */
-
-#ifdef _DEFINE
-# define EXTERN
-#else /* _DEFINE */
-# define EXTERN extern
-#endif /* _DEFINE */
-
-
-#include <unistd.h>
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <setjmp.h>
-#include <string.h>
-#include <time.h>
-# ifdef EX_OK
-# undef EX_OK /* for SVr4.2 SMP */
-# endif /* EX_OK */
-
-#include "sendmail/sendmail.h"
-
-/* profiling? */
-#if MONCONTROL
-# define SM_PROF(x) moncontrol(x)
-#else /* MONCONTROL */
-# define SM_PROF(x)
-#endif /* MONCONTROL */
-
-#ifdef _DEFINE
-# ifndef lint
-SM_UNUSED(static char SmailId[]) = "@(#)$Id: sendmail.h,v 8.1052 2007/10/05 23:06:30 ca Exp $";
-# endif /* ! lint */
-#endif /* _DEFINE */
-
-#include "bf.h"
-#include "timers.h"
-#include <sm/exc.h>
-#include <sm/heap.h>
-#include <sm/debug.h>
-#include <sm/rpool.h>
-#include <sm/io.h>
-#include <sm/path.h>
-#include <sm/signal.h>
-#include <sm/clock.h>
-#include <sm/mbdb.h>
-#include <sm/errstring.h>
-#include <sm/sysexits.h>
-#include <sm/shm.h>
-#include <sm/misc.h>
-
-#ifdef LOG
-# include <syslog.h>
-#endif /* LOG */
-
-
-
-# if NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25
-# include <sys/socket.h>
-# endif /* NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 */
-# if NETUNIX
-# include <sys/un.h>
-# endif /* NETUNIX */
-# if NETINET || NETINET6
-# include <netinet/in.h>
-# endif /* NETINET || NETINET6 */
-# if NETINET6
-/*
-** There is no standard yet for IPv6 includes.
-** Specify OS specific implementation in conf.h
-*/
-# endif /* NETINET6 */
-# if NETISO
-# include <netiso/iso.h>
-# endif /* NETISO */
-# if NETNS
-# include <netns/ns.h>
-# endif /* NETNS */
-# if NETX25
-# include <netccitt/x25.h>
-# endif /* NETX25 */
-
-# if NAMED_BIND
-# include <arpa/nameser.h>
-# ifdef NOERROR
-# undef NOERROR /* avoid <sys/streams.h> conflict */
-# endif /* NOERROR */
-# include <resolv.h>
-# else /* NAMED_BIND */
-# undef SM_SET_H_ERRNO
-# define SM_SET_H_ERRNO(err)
-# endif /* NAMED_BIND */
-
-# if 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
-# include <openssl/ssl.h>
-# if !TLS_NO_RSA
-# define RSA_KEYLENGTH 512
-# endif /* !TLS_NO_RSA */
-#endif /* STARTTLS */
-
-#if SASL /* include the sasl include files if we have them */
-
-
-# if SASL == 2 || SASL >= 20000
-# include <sasl/sasl.h>
-# include <sasl/saslutil.h>
-# else /* SASL == 2 || SASL >= 20000 */
-# include <sasl.h>
-# include <saslutil.h>
-# endif /* SASL == 2 || SASL >= 20000 */
-# 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 || SASL == 2
-# undef SASL
-# define SASL SASL_VERSION
-# else /* SASL == 1 || SASL == 2 */
-# 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 || SASL == 2 */
-# 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
-** be pretty solid on most architectures today. They have to be
-** defined after <arpa/nameser.h> because some versions of that
-** file also define them. In all cases, we can't use sizeof because
-** some systems (e.g., Crays) always treat everything as being at
-** least 64 bits.
-*/
-
-#ifndef INADDRSZ
-# define INADDRSZ 4 /* size of an IPv4 address in bytes */
-#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 /* ! INT16SZ */
-#ifndef INT32SZ
-# define INT32SZ 4 /* size of a 32 bit integer in bytes */
-#endif /* ! INT32SZ */
-#ifndef INADDR_LOOPBACK
-# define INADDR_LOOPBACK 0x7f000001 /* loopback address */
-#endif /* ! INADDR_LOOPBACK */
-
-/*
-** Error return from inet_addr(3), in case not defined in /usr/include.
-*/
-
-#ifndef INADDR_NONE
-# define INADDR_NONE 0xffffffff
-#endif /* ! INADDR_NONE */
-
-
-/* (f)open() modes for queue files */
-# define QF_O_EXTRA 0
-
-
-/*
-** An 'argument class' describes the storage allocation status
-** of an object pointed to by an argument to a function.
-*/
-
-typedef enum
-{
- A_HEAP, /* the storage was allocated by malloc, and the
- * ownership of the storage is ceded by the caller
- * to the called function. */
- A_TEMP, /* The storage is temporary, and is only guaranteed
- * to be valid for the duration of the function call. */
- A_PERM /* The storage is 'permanent': this might mean static
- * storage, or rpool storage. */
-} ARGCLASS_T;
-
-/* forward references for prototypes */
-typedef struct envelope ENVELOPE;
-typedef struct mailer MAILER;
-typedef struct queuegrp QUEUEGRP;
-
-/*
-** Address structure.
-** Addresses are stored internally in this structure.
-*/
-
-struct address
-{
- char *q_paddr; /* the printname for the address */
- char *q_user; /* user name */
- char *q_ruser; /* real user name, or NULL if q_user */
- char *q_host; /* host name */
- struct mailer *q_mailer; /* mailer to use */
- unsigned long q_flags; /* status flags, see below */
- uid_t q_uid; /* user-id of receiver (if known) */
- gid_t q_gid; /* group-id of receiver (if known) */
- char *q_home; /* home dir (local mailer only) */
- char *q_fullname; /* full name if known */
- struct address *q_next; /* chain */
- struct address *q_alias; /* address this results from */
- char *q_owner; /* owner of q_alias */
- struct address *q_tchain; /* temporary use chain */
-#if PIPELINING
- struct address *q_pchain; /* chain for pipelining */
-#endif /* PIPELINING */
- char *q_finalrcpt; /* Final-Recipient: DSN header */
- char *q_orcpt; /* ORCPT parameter from RCPT TO: line */
- char *q_status; /* status code for DSNs */
- char *q_rstatus; /* remote status message for DSNs */
- time_t q_statdate; /* date of status messages */
- char *q_statmta; /* MTA generating q_rstatus */
- short q_state; /* address state, see below */
- char *q_signature; /* MX-based sorting value */
- int q_qgrp; /* index into queue groups */
- int q_qdir; /* queue directory inside group */
- char *q_message; /* error message */
-};
-
-typedef struct address ADDRESS;
-
-/* 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 /* propagate notify parameter */
-#define QRELAYED 0x00000400 /* DSN: relayed to non-DSN aware sys */
-#define QEXPANDED 0x00000800 /* DSN: undergone list expansion */
-#define QDELIVERED 0x00001000 /* DSN: successful final delivery */
-#define QDELAYED 0x00002000 /* DSN: message delayed */
-#define QALIAS 0x00004000 /* expanded alias */
-#define QBYTRACE 0x00008000 /* DeliverBy: trace */
-#define QBYNDELAY 0x00010000 /* DeliverBy: notify, delay */
-#define QBYNRELAY 0x00020000 /* DeliverBy: notify, relayed */
-#define QTHISPASS 0x40000000 /* temp: address set this pass */
-#define QRCPTOK 0x80000000 /* recipient() processed address */
-
-#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_RETRY 4 /* retry delivery for next MX */
-#define QS_VERIFIED 5 /* verified, but not expanded */
-
-/*
-** Notice: all of the following values are variations of QS_DONTSEND.
-** If new states are added, they must be inserted in the proper place!
-** See the macro definition of QS_IS_DEAD() down below.
-*/
-
-#define QS_DONTSEND 6 /* don't send to this address */
-#define QS_EXPANDED 7 /* expanded */
-#define QS_SENDER 8 /* message sender (MeToo) */
-#define QS_CLONED 9 /* addr cloned to split envelope */
-#define QS_DISCARDED 10 /* rcpt discarded (EF_DISCARD) */
-#define QS_REPLACED 11 /* maplocaluser()/UserDB replaced */
-#define QS_REMOVED 12 /* removed (removefromlist()) */
-#define QS_DUPLICATE 13 /* duplicate suppressed */
-#define QS_INCLUDED 14 /* :include: delivery */
-#define QS_FATALERR 15 /* fatal error, don't deliver */
-
-/* address state testing primitives */
-#define QS_IS_OK(s) ((s) == QS_OK)
-#define QS_IS_SENT(s) ((s) == QS_SENT)
-#define QS_IS_BADADDR(s) ((s) == QS_BADADDR)
-#define QS_IS_QUEUEUP(s) ((s) == QS_QUEUEUP)
-#define QS_IS_RETRY(s) ((s) == QS_RETRY)
-#define QS_IS_VERIFIED(s) ((s) == QS_VERIFIED)
-#define QS_IS_EXPANDED(s) ((s) == QS_EXPANDED)
-#define QS_IS_REMOVED(s) ((s) == QS_REMOVED)
-#define QS_IS_UNDELIVERED(s) ((s) == QS_OK || \
- (s) == QS_QUEUEUP || \
- (s) == QS_RETRY || \
- (s) == QS_VERIFIED)
-#define QS_IS_UNMARKED(s) ((s) == QS_OK || \
- (s) == QS_RETRY)
-#define QS_IS_SENDABLE(s) ((s) == QS_OK || \
- (s) == QS_QUEUEUP || \
- (s) == QS_RETRY)
-#define QS_IS_ATTEMPTED(s) ((s) == QS_QUEUEUP || \
- (s) == QS_RETRY || \
- (s) == QS_SENT || \
- (s) == QS_DISCARDED)
-#define QS_IS_DEAD(s) ((s) >= QS_DONTSEND)
-
-
-#define NULLADDR ((ADDRESS *) NULL)
-
-extern ADDRESS NullAddress; /* a null (template) address [main.c] */
-
-/* functions */
-extern void cataddr __P((char **, char **, char *, int, int, bool));
-extern char *crackaddr __P((char *, ENVELOPE *));
-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 *, bool));
-extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **,
- ENVELOPE *, bool));
-extern char **prescan __P((char *, int, char[], int, char **, unsigned char *, bool));
-extern void printaddr __P((SM_FILE_T *, ADDRESS *, bool));
-extern ADDRESS *recipient __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
-extern char *remotename __P((char *, MAILER *, int, int *, ENVELOPE *));
-extern int rewrite __P((char **, int, int, ENVELOPE *, int));
-extern bool sameaddr __P((ADDRESS *, ADDRESS *));
-extern int sendtolist __P((char *, ADDRESS *, ADDRESS **, int, ENVELOPE *));
-#if MILTER
-extern int removefromlist __P((char *, ADDRESS **, ENVELOPE *));
-#endif /* MILTER */
-extern void setsender __P((char *, ENVELOPE *, char **, int, bool));
-typedef void esmtp_args_F __P((ADDRESS *, char *, char *, ENVELOPE *));
-extern void parse_esmtp_args __P((ENVELOPE *, ADDRESS *, char *, char *,
- char *, char *args[], esmtp_args_F));
-extern esmtp_args_F mail_esmtp_args;
-extern esmtp_args_F rcpt_esmtp_args;
-extern void reset_mail_esmtp_args __P((ENVELOPE *));
-
-/* macro to simplify the common call to rewrite() */
-#define REWRITE(pvp, rs, env) rewrite(pvp, rs, 0, env, MAXATOM)
-
-/*
-** Token Tables for prescan
-*/
-
-extern unsigned char ExtTokenTab[256]; /* external strings */
-extern unsigned char IntTokenTab[256]; /* internal strings */
-
-
-/*
-** Mailer definition structure.
-** Every mailer known to the system is declared in this
-** structure. It defines the pathname of the mailer, some
-** flags associated with it, and the argument vector to
-** pass to it. The flags are defined in conf.c
-**
-** The argument vector is expanded before actual use. All
-** words except the first are passed through the macro
-** processor.
-*/
-
-struct mailer
-{
- char *m_name; /* symbolic name of this mailer */
- char *m_mailer; /* pathname of the mailer to use */
- char *m_mtatype; /* type of this MTA */
- char *m_addrtype; /* type for addresses */
- char *m_diagtype; /* type for diagnostics */
- 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 */
- short m_sh_rwset; /* rewrite set: sender header addresses */
- short m_se_rwset; /* rewrite set: sender envelope addresses */
- short m_rh_rwset; /* rewrite set: recipient header addresses */
- short m_re_rwset; /* rewrite set: recipient envelope addresses */
- 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 */
- int m_maxrcpt; /* max recipients per envelope client-side */
- short m_qgrp; /* queue group for this mailer */
-};
-
-/* bits for m_flags */
-#define M_ESMTP 'a' /* run Extended SMTP */
-#define M_ALIASABLE 'A' /* user can be LHS of an alias */
-#define M_BLANKEND 'b' /* ensure blank line at end of message */
-#define M_STRIPBACKSL 'B' /* strip all leading backslashes from user */
-#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 */
- /* '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 */
- /* '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: */
- /* '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) */
- /* 'V' UIUC: !-relativize all addresses */
-#define M_HASPWENT 'w' /* check for /etc/passwd entry */
-#define M_NOHOSTSTAT 'W' /* ignore long term host status information */
- /* 'x' CF: include Full-Name: */
-#define M_XDOT 'X' /* use hidden-dot algorithm */
-#define M_LMTP 'z' /* run Local Mail Transport Protocol */
-#define M_DIALDELAY 'Z' /* apply dial delay sleeptime */
-#define M_NOMX '0' /* turn off MX lookups */
-#define M_NONULLS '1' /* don't send null bytes */
-#define M_FSMTP '2' /* force SMTP (no ESMTP even if offered) */
-#define M_EBCDIC '3' /* extend Q-P encoding for EBCDIC */
-#define M_TRYRULESET5 '5' /* use ruleset 5 after local aliasing */
-#define M_7BITHDRS '6' /* strip headers to 7 bits even in 8 bit path */
-#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 *));
-extern void makequeue __P((char *, bool));
-extern void runqueueevent __P((int));
-#if _FFR_QUEUE_RUN_PARANOIA
-extern bool checkqueuerunner __P((void));
-#endif /* _FFR_QUEUE_RUN_PARANOIA */
-
-EXTERN MAILER *FileMailer; /* ptr to *file* mailer */
-EXTERN MAILER *InclMailer; /* ptr to *include* mailer */
-EXTERN MAILER *LocalMailer; /* ptr to local mailer */
-EXTERN MAILER *ProgMailer; /* ptr to program mailer */
-EXTERN MAILER *Mailer[MAXMAILERS + 1];
-
-/*
-** Queue group definition structure.
-** Every queue group known to the system is declared in this structure.
-** It defines the basic pathname of the queue group, some flags
-** associated with it, and the argument vector to pass to it.
-*/
-
-struct qpaths_s
-{
- char *qp_name; /* name of queue dir, relative path */
- short qp_subdirs; /* use subdirs? */
- short qp_fsysidx; /* file system index of this directory */
-# if SM_CONF_SHM
- int qp_idx; /* index into array for queue information */
-# endif /* SM_CONF_SHM */
-};
-
-typedef struct qpaths_s QPATHS;
-
-struct queuegrp
-{
- char *qg_name; /* symbolic name of this queue group */
-
- /*
- ** For now this is the same across all queue groups.
- ** Otherwise we have to play around with chdir().
- */
-
- char *qg_qdir; /* common component of queue directory */
- short qg_index; /* queue number internally, index in Queue[] */
- int qg_maxqrun; /* max # of jobs in 1 queuerun */
- int qg_numqueues; /* number of queues in this queue */
-
- /*
- ** qg_queueintvl == 0 denotes that no individual value is used.
- ** Whatever accesses this must deal with "<= 0" as
- ** "not set, use appropriate default".
- */
-
- time_t qg_queueintvl; /* interval for queue runs */
- QPATHS *qg_qpaths; /* list of queue directories */
- BITMAP256 qg_flags; /* status flags, see below */
- short qg_nice; /* niceness for queue run */
- int qg_wgrp; /* Assigned to this work group */
- int qg_maxlist; /* max items in work queue for this group */
- int qg_curnum; /* current number of queue for queue runs */
- int qg_maxrcpt; /* max recipients per envelope, 0==no limit */
-
- time_t qg_nextrun; /* time for next queue runs */
-#if _FFR_QUEUE_GROUP_SORTORDER
- short qg_sortorder; /* how do we sort this queuerun */
-#endif /* _FFR_QUEUE_GROUP_SORTORDER */
-#if 0
- long qg_wkrcptfact; /* multiplier for # recipients -> priority */
- long qg_qfactor; /* slope of queue function */
- bool qg_doqueuerun; /* XXX flag is it time to do a queuerun */
-#endif /* 0 */
-};
-
-/* bits for qg_flags (XXX: unused as of now) */
-#define QD_DEFINED ((char) 1) /* queue group has been defined */
-#define QD_FORK 'f' /* fork queue runs */
-
-extern void filesys_update __P((void));
-#if _FFR_ANY_FREE_FS
-extern bool filesys_free __P((long));
-#endif /* _FFR_ANY_FREE_FS */
-
-#if SASL
-/*
-** SASL
-*/
-
-/* lines in authinfo file or index into SASL_AI_T */
-# define SASL_WRONG (-1)
-# define SASL_USER 0 /* authorization id (user) */
-# define SASL_AUTHID 1 /* authentication id */
-# define SASL_PASSWORD 2 /* password fuer authid */
-# define SASL_DEFREALM 3 /* realm to use */
-# define SASL_MECHLIST 4 /* list of mechanisms to try */
-# define SASL_ID_REALM 5 /* authid@defrealm */
-
-/*
-** Current mechanism; this is just used to convey information between
-** invocation of SASL callback functions.
-** It must be last in the list, because it's not allocated by us
-** and hence we don't free() it.
-*/
-# define SASL_MECH 6
-# define SASL_ENTRIES 7 /* number of entries in array */
-
-# define SASL_USER_BIT (1 << SASL_USER)
-# define SASL_AUTHID_BIT (1 << SASL_AUTHID)
-# define SASL_PASSWORD_BIT (1 << SASL_PASSWORD)
-# define SASL_DEFREALM_BIT (1 << SASL_DEFREALM)
-# define SASL_MECHLIST_BIT (1 << SASL_MECHLIST)
-
-/* authenticated? */
-# define SASL_NOT_AUTH 0 /* not authenticated */
-# define SASL_PROC_AUTH 1 /* in process of authenticating */
-# define SASL_IS_AUTH 2 /* authenticated */
-
-/* SASL options */
-# define SASL_AUTH_AUTH 0x1000 /* use auth= only if authenticated */
-# if SASL >= 20101
-# define SASL_SEC_MASK SASL_SEC_MAXIMUM /* mask for SASL_SEC_* values: sasl.h */
-# else /* SASL >= 20101 */
-# define SASL_SEC_MASK 0x0fff /* mask for SASL_SEC_* values: sasl.h */
-# if (SASL_SEC_NOPLAINTEXT & SASL_SEC_MASK) == 0 || \
- (SASL_SEC_NOACTIVE & SASL_SEC_MASK) == 0 || \
- (SASL_SEC_NODICTIONARY & SASL_SEC_MASK) == 0 || \
- (SASL_SEC_FORWARD_SECRECY & SASL_SEC_MASK) == 0 || \
- (SASL_SEC_NOANONYMOUS & SASL_SEC_MASK) == 0 || \
- (SASL_SEC_PASS_CREDENTIALS & SASL_SEC_MASK) == 0
-ERROR: change SASL_SEC_MASK_ notify sendmail.org!
-# endif /* SASL_SEC_NOPLAINTEXT & SASL_SEC_MASK) == 0 ... */
-# endif /* SASL >= 20101 */
-# define MAXOUTLEN 8192 /* length of output buffer */
-
-/* functions */
-extern char *intersect __P((char *, char *, SM_RPOOL_T *));
-extern char *iteminlist __P((char *, char *, char *));
-# if SASL >= 20000
-extern int proxy_policy __P((sasl_conn_t *, void *, const char *, unsigned, const char *, unsigned, const char *, unsigned, struct propctx *));
-extern int safesaslfile __P((void *, const char *, sasl_verify_type_t));
-# else /* SASL >= 20000 */
-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 */
-# endif /* SASL >= 20000 */
-extern void stop_sasl_client __P((void));
-
-/* structure to store authinfo */
-typedef char *SASL_AI_T[SASL_ENTRIES];
-
-EXTERN char *AuthMechanisms; /* AUTH mechanisms */
-EXTERN char *AuthRealm; /* AUTH realm */
-EXTERN char *SASLInfo; /* file with AUTH info */
-EXTERN int SASLOpts; /* options for SASL */
-EXTERN int MaxSLBits; /* max. encryption bits for SASL */
-#endif /* SASL */
-
-/*
-** Structure to store macros.
-*/
-typedef struct
-{
- SM_RPOOL_T *mac_rpool; /* resource pool */
- BITMAP256 mac_allocated; /* storage has been alloc()? */
- char *mac_table[MAXMACROID + 1]; /* macros */
-} MACROS_T;
-
-EXTERN MACROS_T GlobalMacros;
-
-/*
-** Information about currently open connections to mailers, or to
-** hosts that we have looked up recently.
-*/
-
-#define MCI struct mailer_con_info
-
-MCI
-{
- unsigned long mci_flags; /* flag bits, see below */
- short mci_errno; /* error number on last connection */
- short mci_herrno; /* h_errno from last DNS lookup */
- short mci_exitstat; /* exit status from last connection */
- short mci_state; /* SMTP state */
- int mci_deliveries; /* delivery attempts for connection */
- long mci_maxsize; /* max size this server will accept */
- SM_FILE_T *mci_in; /* input side of connection */
- SM_FILE_T *mci_out; /* output side of connection */
- pid_t mci_pid; /* process id of subordinate proc */
- char *mci_phase; /* SMTP phase string */
- struct mailer *mci_mailer; /* ptr to the mailer for this conn */
- char *mci_host; /* host name */
- char *mci_status; /* DSN status to be copied to addrs */
- char *mci_rstatus; /* SMTP status to be copied to addrs */
- time_t mci_lastuse; /* last usage time */
- SM_FILE_T *mci_statfile; /* long term status file */
- char *mci_heloname; /* name to use as HELO arg */
- long mci_min_by; /* minimum DELIVERBY */
- bool mci_retryrcpt; /* tempfail for at least one rcpt */
- char *mci_tolist; /* list of valid recipients */
- SM_RPOOL_T *mci_rpool; /* resource pool */
-#if PIPELINING
- int mci_okrcpts; /* number of valid recipients */
- ADDRESS *mci_nextaddr; /* next address for pipelined status */
-#endif /* PIPELINING */
-#if SASL
- SASL_AI_T mci_sai; /* authentication info */
- bool mci_sasl_auth; /* authenticated? */
- int mci_sasl_string_len;
- char *mci_sasl_string; /* sasl reply string */
- 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 */
- MACROS_T mci_macro; /* macro definitions */
-};
-
-
-/* flag bits */
-#define MCIF_VALID 0x00000001 /* this entry is valid */
-/* 0x00000002 unused, was MCIF_TEMP */
-#define MCIF_CACHED 0x00000004 /* currently in open cache */
-#define MCIF_ESMTP 0x00000008 /* this host speaks ESMTP */
-#define MCIF_EXPN 0x00000010 /* EXPN command supported */
-#define MCIF_SIZE 0x00000020 /* SIZE option supported */
-#define MCIF_8BITMIME 0x00000040 /* BODY=8BITMIME supported */
-#define MCIF_7BIT 0x00000080 /* strip this message to 7 bits */
-/* 0x00000100 unused, was MCIF_MULTSTAT: 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 */
-#define MCIF_PIPELINED 0x00040000 /* PIPELINING supported */
-#define MCIF_VERB 0x00080000 /* VERB supported */
-#if STARTTLS
-#define MCIF_TLS 0x00100000 /* STARTTLS supported */
-#define MCIF_TLSACT 0x00200000 /* STARTTLS active */
-#define MCIF_EXTENS (MCIF_EXPN | MCIF_SIZE | MCIF_8BITMIME | MCIF_DSN | MCIF_8BITOK | MCIF_AUTH | MCIF_ENHSTAT | MCIF_TLS)
-#else /* STARTTLS */
-#define MCIF_EXTENS (MCIF_EXPN | MCIF_SIZE | MCIF_8BITMIME | MCIF_DSN | MCIF_8BITOK | MCIF_AUTH | MCIF_ENHSTAT)
-#endif /* STARTTLS */
-#define MCIF_DLVR_BY 0x00400000 /* DELIVERBY */
-#if _FFR_IGNORE_EXT_ON_HELO
-# define MCIF_HELO 0x00800000 /* we used HELO: ignore extensions */
-#endif /* _FFR_IGNORE_EXT_ON_HELO */
-#define MCIF_INLONGLINE 0x01000000 /* in the middle of a long line */
-#define MCIF_ONLY_EHLO 0x10000000 /* use only EHLO in smtpinit */
-
-/* states */
-#define MCIS_CLOSED 0 /* no traffic on this connection */
-#define MCIS_OPENING 1 /* sending initial protocol */
-#define MCIS_OPEN 2 /* open, initial protocol sent */
-#define MCIS_MAIL 3 /* MAIL command sent */
-#define MCIS_RCPT 4 /* RCPT commands being sent */
-#define MCIS_DATA 5 /* DATA command sent */
-#define MCIS_QUITING 6 /* running quit protocol */
-#define MCIS_SSD 7 /* service shutting down */
-#define MCIS_ERROR 8 /* I/O error on connection */
-
-/* functions */
-extern void mci_cache __P((MCI *));
-extern void mci_close __P((MCI *, char *where));
-extern void mci_dump __P((SM_FILE_T *, MCI *, bool));
-extern void mci_dump_all __P((SM_FILE_T *, bool));
-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 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 *, char *), char *));
-extern void mci_unlock_host __P((MCI *));
-
-EXTERN int MaxMciCache; /* maximum entries in MCI cache */
-EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */
-EXTERN time_t MciInfoTimeout; /* how long 'til we retry down hosts */
-
-/*
-** Header structure.
-** This structure is used internally to store header items.
-*/
-
-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 */
- unsigned char h_macro; /* include header if macro defined */
- unsigned long h_flags; /* status bits, see below */
- BITMAP256 h_mflags; /* m_flags bits needed */
-};
-
-typedef struct header HDR;
-
-/*
-** Header information structure.
-** Defined in conf.c, this struct declares the header fields
-** that have some magic meaning.
-*/
-
-struct hdrinfo
-{
- char *hi_field; /* the name of the field */
- unsigned long hi_flags; /* status bits, see below */
- char *hi_ruleset; /* validity check ruleset */
-};
-
-extern struct hdrinfo HdrInfo[];
-
-/* bits for h_flags and hi_flags */
-#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 */
-#define CHHDR_QUEUE 0x0008 /* header from queue file */
-
-/* functions */
-extern void addheader __P((char *, char *, int, ENVELOPE *, bool));
-extern unsigned long chompheader __P((char *, int, HDR **, ENVELOPE *));
-extern bool commaize __P((HDR *, char *, bool, MCI *, ENVELOPE *, int));
-extern HDR *copyheader __P((HDR *, SM_RPOOL_T *));
-extern void eatheader __P((ENVELOPE *, bool, bool));
-extern char *hvalue __P((char *, HDR *));
-extern void insheader __P((int, char *, char *, int, ENVELOPE *, bool));
-extern bool isheader __P((char *));
-extern bool 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
-** only one of these -- for the message that we originally read
-** and which is our primary interest -- but other envelopes can
-** be generated during processing. For example, error messages
-** will have their own envelope.
-*/
-
-struct envelope
-{
- HDR *e_header; /* head of header list */
- long e_msgpriority; /* adjusted priority of this message */
- time_t e_ctime; /* time message appeared in the queue */
- char *e_to; /* (list of) target person(s) */
- ADDRESS e_from; /* the person it is from */
- char *e_sender; /* e_from.q_paddr w comments stripped */
- char **e_fromdomain; /* the domain part of the sender */
- ADDRESS *e_sendqueue; /* list of message recipients */
- ADDRESS *e_errorqueue; /* the queue for error responses */
-
- /*
- ** Overflow detection is based on < 0, so don't change this
- ** to unsigned. We don't use unsigned and == ULONG_MAX because
- ** some libc's don't have strtoul(), see mail_esmtp_args().
- */
-
- long e_msgsize; /* size of the message in bytes */
- char *e_msgid; /* message id (for logging) */
- unsigned long e_flags; /* flags, see below */
- int e_nrcpts; /* number of recipients */
- short e_class; /* msg class (priority, junk, etc.) */
- short e_hopcount; /* number of times processed */
- short e_nsent; /* number of sends since checkpoint */
- short e_sendmode; /* message send mode */
- short e_errormode; /* error return mode */
- short e_timeoutclass; /* message timeout class */
- bool (*e_puthdr)__P((MCI *, HDR *, ENVELOPE *, int));
- /* function to put header of message */
- bool (*e_putbody)__P((MCI *, ENVELOPE *, char *));
- /* function to put body of message */
- ENVELOPE *e_parent; /* the message this one encloses */
- ENVELOPE *e_sibling; /* the next envelope of interest */
- char *e_bodytype; /* type of message body */
- SM_FILE_T *e_dfp; /* data file */
- char *e_id; /* code for this entry in queue */
-#if _FFR_SESSID
- char *e_sessid; /* session ID for this envelope */
-#endif /* _FFR_SESSID */
- int e_qgrp; /* queue group (index into queues) */
- int e_qdir; /* index into queue directories */
- int e_dfqgrp; /* data file queue group index */
- int e_dfqdir; /* data file queue directory index */
- int e_xfqgrp; /* queue group (index into queues) */
- int e_xfqdir; /* index into queue directories (xf) */
- SM_FILE_T *e_xfp; /* transcript file */
- SM_FILE_T *e_lockfp; /* the lock file for this message */
- char *e_message; /* error message; readonly; NULL,
- * or allocated from e_rpool */
- char *e_statmsg; /* stat msg (changes per delivery).
- * readonly. NULL or allocated from
- * e_rpool. */
- char *e_quarmsg; /* why envelope is quarantined */
- char e_qfletter; /* queue file letter on disk */
- char *e_msgboundary; /* MIME-style message part boundary */
- char *e_origrcpt; /* original recipient (one only) */
- char *e_envid; /* envelope id from MAIL FROM: line */
- char *e_status; /* DSN status for this message */
- time_t e_dtime; /* time of last delivery attempt */
- int e_ntries; /* number of delivery attempts */
- dev_t e_dfdev; /* data file device (crash recovery) */
- ino_t e_dfino; /* data file inode (crash recovery) */
- MACROS_T e_macro; /* macro definitions */
- MCI *e_mci; /* connection info */
- char *e_auth_param; /* readonly; NULL or static storage or
- * allocated from e_rpool */
- TIMERS e_timers; /* per job timers */
- long e_deliver_by; /* deliver by */
- int e_dlvr_flag; /* deliver by flag */
- SM_RPOOL_T *e_rpool; /* resource pool for this envelope */
- unsigned int e_features; /* server features */
-};
-
-/* values for e_flags */
-#define EF_OLDSTYLE 0x00000001L /* use spaces (not commas) in hdrs */
-#define EF_INQUEUE 0x00000002L /* this message is fully queued */
-#define EF_NO_BODY_RETN 0x00000004L /* omit message body on error */
-#define EF_CLRQUEUE 0x00000008L /* disk copy is no longer needed */
-#define EF_SENDRECEIPT 0x00000010L /* send a return receipt */
-#define EF_FATALERRS 0x00000020L /* fatal errors occurred */
-#define EF_DELETE_BCC 0x00000040L /* delete Bcc: headers entirely */
-#define EF_RESPONSE 0x00000080L /* this is an error or return receipt */
-#define EF_RESENT 0x00000100L /* this message is being forwarded */
-#define EF_VRFYONLY 0x00000200L /* verify only (don't expand aliases) */
-#define EF_WARNING 0x00000400L /* warning message has been sent */
-#define EF_QUEUERUN 0x00000800L /* this envelope is from queue */
-#define EF_GLOBALERRS 0x00001000L /* treat errors as global */
-#define EF_PM_NOTIFY 0x00002000L /* send return mail to postmaster */
-#define EF_METOO 0x00004000L /* send to me too */
-#define EF_LOGSENDER 0x00008000L /* need to log the sender */
-#define EF_NORECEIPT 0x00010000L /* suppress all return-receipts */
-#define EF_HAS8BIT 0x00020000L /* at least one 8-bit char in body */
-#define EF_NL_NOT_EOL 0x00040000L /* don't accept raw NL as EOLine */
-#define EF_CRLF_NOT_EOL 0x00080000L /* don't accept CR-LF as EOLine */
-#define EF_RET_PARAM 0x00100000L /* RCPT command had RET argument */
-#define EF_HAS_DF 0x00200000L /* set when data file is instantiated */
-#define EF_IS_MIME 0x00400000L /* really is a MIME message */
-#define EF_DONT_MIME 0x00800000L /* never MIME this message */
-#define EF_DISCARD 0x01000000L /* discard the message */
-#define EF_TOOBIG 0x02000000L /* message is too big */
-#define EF_SPLIT 0x04000000L /* envelope has been split */
-#define EF_UNSAFE 0x08000000L /* unsafe: read from untrusted source */
-#define EF_TOODEEP 0x10000000L /* message is nested too deep */
-
-#define DLVR_NOTIFY 0x01
-#define DLVR_RETURN 0x02
-#define DLVR_TRACE 0x10
-#define IS_DLVR_NOTIFY(e) (((e)->e_dlvr_flag & DLVR_NOTIFY) != 0)
-#define IS_DLVR_RETURN(e) (((e)->e_dlvr_flag & DLVR_RETURN) != 0)
-#define IS_DLVR_TRACE(e) (((e)->e_dlvr_flag & DLVR_TRACE) != 0)
-#define IS_DLVR_BY(e) ((e)->e_dlvr_flag != 0)
-
-#define BODYTYPE_NONE (0)
-#define BODYTYPE_7BIT (1)
-#define BODYTYPE_8BITMIME (2)
-#define BODYTYPE_ILLEGAL (-1)
-#define BODYTYPE_VALID(b) ((b) == BODYTYPE_7BIT || (b) == BODYTYPE_8BITMIME)
-
-extern ENVELOPE BlankEnvelope;
-
-/* functions */
-extern void clearenvelope __P((ENVELOPE *, bool, SM_RPOOL_T *));
-extern void dropenvelope __P((ENVELOPE *, bool, bool));
-extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *, SM_RPOOL_T *));
-extern void clrsessenvelope __P((ENVELOPE *));
-extern void printenvflags __P((ENVELOPE *));
-extern bool putbody __P((MCI *, ENVELOPE *, char *));
-extern bool putheader __P((MCI *, HDR *, ENVELOPE *, int));
-
-/*
-** Message priority classes.
-**
-** The message class is read directly from the Priority: header
-** field in the message.
-**
-** CurEnv->e_msgpriority is the number of bytes in the message plus
-** the creation time (so that jobs ``tend'' to be ordered correctly),
-** adjusted by the message class, the number of recipients, and the
-** amount of time the message has been sitting around. This number
-** is used to order the queue. Higher values mean LOWER priority.
-**
-** Each priority class point is worth WkClassFact priority points;
-** each recipient is worth WkRecipFact priority points. Each time
-** we reprocess a message the priority is adjusted by WkTimeFact.
-** WkTimeFact should normally decrease the priority so that jobs
-** that have historically failed will be run later; thanks go to
-** Jay Lepreau at Utah for pointing out the error in my thinking.
-**
-** The "class" is this number, unadjusted by the age or size of
-** this message. Classes with negative representations will have
-** error messages thrown away if they are not local.
-*/
-
-struct priority
-{
- char *pri_name; /* external name of priority */
- int pri_val; /* internal value for same */
-};
-
-EXTERN int NumPriorities; /* pointer into Priorities */
-EXTERN struct priority Priorities[MAXPRIORITIES];
-
-/*
-** Rewrite rules.
-*/
-
-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 */
-};
-
-/*
-** Special characters in rewriting rules.
-** These are used internally only.
-** The COND* rules are actually used in macros rather than in
-** rewriting rules, but are given here because they
-** cannot conflict.
-*/
-
-/* "out of band" indicator */
-/* sm/sendmail.h #define METAQUOTE ((unsigned char)0377) quotes the next octet */
-
-/* left hand side items */
-#define MATCHZANY ((unsigned char)0220) /* match zero or more tokens */
-#define MATCHANY ((unsigned char)0221) /* match one or more tokens */
-#define MATCHONE ((unsigned char)0222) /* match exactly one token */
-#define MATCHCLASS ((unsigned char)0223) /* match one token in a class */
-#define MATCHNCLASS ((unsigned char)0224) /* match tokens not in class */
-
-/* right hand side items */
-#define MATCHREPL ((unsigned char)0225) /* RHS replacement for above */
-#define CANONNET ((unsigned char)0226) /* canonical net, next token */
-#define CANONHOST ((unsigned char)0227) /* canonical host, next token */
-#define CANONUSER ((unsigned char)0230) /* canonical user, next N tokens */
-#define CALLSUBR ((unsigned char)0231) /* call another rewriting set */
-
-/* conditionals in macros (anywhere) */
-#define CONDIF ((unsigned char)0232) /* conditional if-then */
-#define CONDELSE ((unsigned char)0233) /* conditional else */
-#define CONDFI ((unsigned char)0234) /* conditional fi */
-
-/* bracket characters for RHS host name lookup */
-#define HOSTBEGIN ((unsigned char)0235) /* hostname lookup begin */
-#define HOSTEND ((unsigned char)0236) /* hostname lookup end */
-
-/* bracket characters for RHS generalized lookup */
-#define LOOKUPBEGIN ((unsigned char)0205) /* generalized lookup begin */
-#define LOOKUPEND ((unsigned char)0206) /* generalized lookup end */
-
-/* macro substitution characters (anywhere) */
-#define MACROEXPAND ((unsigned char)0201) /* macro expansion */
-#define MACRODEXPAND ((unsigned char)0202) /* deferred macro expansion */
-
-/* to make the code clearer */
-#define MATCHZERO CANONHOST
-
-#define MAXMATCH 9 /* max params per rewrite */
-#define MAX_MAP_ARGS 10 /* max arguments for map */
-
-/* external <==> internal mapping table */
-struct metamac
-{
- char metaname; /* external code (after $) */
- unsigned char metaval; /* internal code (as above) */
-};
-
-/* values for macros with external names only */
-#define MID_OPMODE 0202 /* operation mode */
-
-/* functions */
-#if SM_HEAP_CHECK
-extern void
-macdefine_tagged __P((
- MACROS_T *_mac,
- ARGCLASS_T _vclass,
- int _id,
- char *_value,
- char *_file,
- int _line,
- int _group));
-# define macdefine(mac,c,id,v) \
- macdefine_tagged(mac,c,id,v,__FILE__,__LINE__,sm_heap_group())
-#else /* SM_HEAP_CHECK */
-extern void
-macdefine __P((
- MACROS_T *_mac,
- ARGCLASS_T _vclass,
- int _id,
- char *_value));
-# define macdefine_tagged(mac,c,id,v,file,line,grp) macdefine(mac,c,id,v)
-#endif /* SM_HEAP_CHECK */
-extern void macset __P((MACROS_T *, int, char *));
-#define macget(mac, i) (mac)->mac_table[i]
-extern void expand __P((char *, char *, size_t, ENVELOPE *));
-extern int macid_parse __P((char *, char **));
-#define macid(name) macid_parse(name, NULL)
-extern char *macname __P((int));
-extern char *macvalue __P((int, ENVELOPE *));
-extern int rscheck __P((char *, char *, char *, ENVELOPE *, int, int, char *, char *, ADDRESS *));
-extern int rscap __P((char *, char *, char *, ENVELOPE *, char ***, char *, int));
-extern void setclass __P((int, char *));
-extern int strtorwset __P((char *, char **, int));
-extern char *translate_dollars __P((char *, char *, int *));
-extern bool wordinclass __P((char *, int));
-
-/*
-** Name canonification short circuit.
-**
-** If the name server for a host is down, the process of trying to
-** canonify the name can hang. This is similar to (but alas, not
-** identical to) looking up the name for delivery. This stab type
-** caches the result of the name server lookup so we don't hang
-** multiple times.
-*/
-
-#define NAMECANON struct _namecanon
-
-NAMECANON
-{
- short nc_errno; /* cached errno */
- short nc_herrno; /* cached h_errno */
- short nc_stat; /* cached exit status code */
- short nc_flags; /* flag bits */
- char *nc_cname; /* the canonical name */
- time_t nc_exp; /* entry expires at */
-};
-
-/* values for nc_flags */
-#define NCF_VALID 0x0001 /* entry valid */
-
-/* hostsignature structure */
-
-struct hostsig_t
-{
- char *hs_sig; /* hostsignature */
- time_t hs_exp; /* entry expires at */
-};
-
-typedef struct hostsig_t HOSTSIG_T;
-
-/* functions */
-extern bool getcanonname __P((char *, int, bool, int *));
-extern int getmxrr __P((char *, char **, unsigned short *, bool, int *, bool, int *));
-extern char *hostsignature __P((MAILER *, char *));
-extern int getfallbackmxrr __P((char *));
-
-/*
-** Mapping functions
-**
-** These allow arbitrary mappings in the config file. The idea
-** (albeit not the implementation) comes from IDA sendmail.
-*/
-
-#define MAPCLASS struct _mapclass
-#define MAP struct _map
-#define MAXMAPACTIONS 5 /* size of map_actions array */
-
-
-/*
-** An actual map.
-*/
-
-MAP
-{
- MAPCLASS *map_class; /* the class of this map */
- MAPCLASS *map_orgclass; /* the original class of this map */
- char *map_mname; /* name of this map */
- long map_mflags; /* flags, see below */
- char *map_file; /* the (nominal) filename */
- ARBPTR_T map_db1; /* the open database ptr */
- ARBPTR_T map_db2; /* an "extra" database pointer */
- char *map_keycolnm; /* key column name */
- char *map_valcolnm; /* value column name */
- unsigned char map_keycolno; /* key column number */
- unsigned char map_valcolno; /* value column number */
- char map_coldelim; /* column delimiter */
- char map_spacesub; /* spacesub */
- char *map_app; /* to append to successful matches */
- char *map_tapp; /* to append to "tempfail" matches */
- char *map_domain; /* the (nominal) NIS domain */
- char *map_rebuild; /* program to run to do auto-rebuild */
- time_t map_mtime; /* last database modification time */
- time_t map_timeout; /* timeout for map accesses */
- int map_retry; /* # of retries for map accesses */
- pid_t map_pid; /* PID of process which opened map */
- int map_lockfd; /* auxiliary lock file descriptor */
- short map_specificity; /* specificity of aliases */
- MAP *map_stack[MAXMAPSTACK]; /* list for stacked maps */
- 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 */
-/* 0x00004000 */
-#define MF_APPEND 0x00008000 /* append new entry on rebuild */
-#define MF_KEEPQUOTES 0x00010000 /* don't dequote key before lookup */
-#define MF_NODEFER 0x00020000 /* don't defer if map lookup fails */
-#define MF_REGEX_NOT 0x00040000 /* regular expression negation */
-#define MF_DEFER 0x00080000 /* don't lookup map in defer mode */
-#define MF_SINGLEMATCH 0x00100000 /* successful only if match one key */
-#define MF_SINGLEDN 0x00200000 /* only one match, but multi values */
-#define MF_FILECLASS 0x00400000 /* this is a file class map */
-#define MF_OPENBOGUS 0x00800000 /* open failed, don't call map_close */
-#define MF_CLOSING 0x01000000 /* map is being closed */
-
-#define DYNOPENMAP(map) if (!bitset(MF_OPEN, (map)->map_mflags)) \
- { \
- 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 */
-
-/* macros to handle MapTempFail */
-#define BIT_IS_MTP 0x01 /* temp.failure occurred */
-#define BIT_ASK_MTP 0x02 /* do we care about MapTempFail? */
-#define RESET_MAPTEMPFAIL MapTempFail = 0
-#define INIT_MAPTEMPFAIL MapTempFail = BIT_ASK_MTP
-#define SET_MAPTEMPFAIL MapTempFail |= BIT_IS_MTP
-#define IS_MAPTEMPFAIL bitset(BIT_IS_MTP, MapTempFail)
-#define ASK_MAPTEMPFAIL bitset(BIT_ASK_MTP, MapTempFail)
-
-/*
-** The class of a map -- essentially the functions to call
-*/
-
-MAPCLASS
-{
- char *map_cname; /* name of this map class */
- char *map_ext; /* extension for database file */
- short map_cflags; /* flag bits, see below */
- bool (*map_parse)__P((MAP *, char *));
- /* argument parsing function */
- char *(*map_lookup)__P((MAP *, char *, char **, int *));
- /* lookup function */
- void (*map_store)__P((MAP *, char *, char *));
- /* store function */
- bool (*map_open)__P((MAP *, int));
- /* open function */
- void (*map_close)__P((MAP *));
- /* close function */
-};
-
-/* bit values for map_cflags */
-#define MCF_ALIASOK 0x0001 /* can be used for aliases */
-#define MCF_ALIASONLY 0x0002 /* usable only for aliases */
-#define MCF_REBUILDABLE 0x0004 /* can rebuild alias files */
-#define MCF_OPTFILE 0x0008 /* file name is optional */
-#define MCF_NOTPERSIST 0x0010 /* don't keep map open all the time */
-
-/* functions */
-extern void closemaps __P((bool));
-extern bool impl_map_open __P((MAP *, int));
-extern void initmaps __P((void));
-extern MAP *makemapentry __P((char *));
-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 *));
-extern int udbexpand __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
-#if USERDB
-extern void _udbx_close __P((void));
-extern char *udbsender __P((char *, SM_RPOOL_T *));
-#endif /* USERDB */
-
-/*
-** LDAP related items
-*/
-#if LDAPMAP
-/* 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
-*/
-
-#if PH_MAP
-
-# include <phclient.h>
-
-struct ph_map_struct
-{
- char *ph_servers; /* list of ph servers */
- char *ph_field_list; /* list of fields to search for match */
- PH *ph; /* PH server handle */
- int ph_fastclose; /* send "quit" command on close */
- time_t ph_timeout; /* timeout interval */
-};
-typedef struct ph_map_struct PH_MAP_STRUCT;
-
-#endif /* PH_MAP */
-
-/*
-** Regular UNIX sockaddrs are too small to handle ISO addresses, so
-** we are forced to declare a supertype here.
-*/
-
-#if NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25
-union bigsockaddr
-{
- struct sockaddr sa; /* general version */
-# if NETUNIX
- struct sockaddr_un sunix; /* UNIX family */
-# endif /* NETUNIX */
-# if NETINET
- struct sockaddr_in sin; /* INET family */
-# endif /* NETINET */
-# if NETINET6
- struct sockaddr_in6 sin6; /* INET/IPv6 */
-# endif /* NETINET6 */
-# if NETISO
- struct sockaddr_iso siso; /* ISO family */
-# endif /* NETISO */
-# if NETNS
- struct sockaddr_ns sns; /* XNS family */
-# endif /* NETNS */
-# if NETX25
- struct sockaddr_x25 sx25; /* X.25 family */
-# endif /* NETX25 */
-};
-
-# define SOCKADDR union bigsockaddr
-
-/* functions */
-extern char *anynet_ntoa __P((SOCKADDR *));
-# if NETINET6
-extern char *anynet_ntop __P((struct in6_addr *, char *, size_t));
-extern int anynet_pton __P((int, const char *, void *));
-# endif /* NETINET6 */
-extern char *hostnamebyanyaddr __P((SOCKADDR *));
-extern char *validate_connection __P((SOCKADDR *, char *, ENVELOPE *));
-# if SASL >= 20000
-extern bool iptostring __P((SOCKADDR *, SOCKADDR_LEN_T, char *, unsigned));
-# endif /* SASL >= 20000 */
-
-#endif /* NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 */
-
-/*
-** Process List (proclist)
-*/
-
-#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, int, int, SOCKADDR *));
-extern void proc_list_clear __P((void));
-extern void proc_list_display __P((SM_FILE_T *, char *));
-extern void proc_list_drop __P((pid_t, int, int *));
-extern void proc_list_probe __P((void));
-extern void proc_list_set __P((pid_t, char *));
-extern void proc_list_signal __P((int, int));
-
-/*
-** Symbol table definitions
-*/
-
-struct symtab
-{
- char *s_name; /* name to be entered */
- short s_symtype; /* general type (see below) */
- struct symtab *s_next; /* pointer to next in chain */
- union
- {
- 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 */
- MAPCLASS sv_mapclass; /* mapping function class */
- MAP sv_map; /* mapping function */
- HOSTSIG_T sv_hostsig; /* host signature */
- MCI sv_mci; /* mailer connection info */
- NAMECANON sv_namecanon; /* canonical name cache */
- int sv_macro; /* macro name => id mapping */
- int sv_ruleset; /* ruleset index */
- struct hdrinfo sv_header; /* header metainfo */
- char *sv_service[MAXMAPSTACK]; /* service switch */
-#if LDAPMAP
- MAP *sv_lmap; /* Maps for LDAP connection */
-#endif /* LDAPMAP */
-#if SOCKETMAP
- MAP *sv_socketmap; /* Maps for SOCKET connection */
-#endif /* SOCKETMAP */
-#if MILTER
- struct milter *sv_milter; /* milter filter name */
-#endif /* MILTER */
- QUEUEGRP *sv_queue; /* pointer to queue */
- } 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 */
-#if LDAPMAP
-# define ST_LMAP 13 /* List head of maps for LDAP connection */
-#endif /* LDAPMAP */
-#if MILTER
-# define ST_MILTER 14 /* milter filter */
-#endif /* MILTER */
-#define ST_QUEUE 15 /* a queue entry */
-
-#if SOCKETMAP
-# define ST_SOCKETMAP 16 /* List head of maps for SOCKET connection */
-#endif /* SOCKETMAP */
-
-/* This entry must be last */
-#define ST_MCI 17 /* 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
-#if LDAPMAP
-# define s_lmap s_value.sv_lmap
-#endif /* LDAPMAP */
-#if SOCKETMAP
-# define s_socketmap s_value.sv_socketmap
-#endif /* SOCKETMAP */
-#if MILTER
-# define s_milter s_value.sv_milter
-#endif /* MILTER */
-#define s_quegrp s_value.sv_queue
-
-/* opcodes to stab */
-#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));
-
-/*
-** Operation, send, error, and MIME modes
-**
-** The operation mode describes the basic operation of sendmail.
-** This can be set from the command line, and is "send mail" by
-** default.
-**
-** The send mode tells how to send mail. It can be set in the
-** configuration file. Its setting determines how quickly the
-** mail will be delivered versus the load on your system. If the
-** -v (verbose) flag is given, it will be forced to SM_DELIVER
-** mode.
-**
-** The error mode tells how to return errors.
-*/
-
-#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) */
-#define MD_DAEMON 'd' /* run as a daemon */
-#define MD_FGDAEMON 'D' /* run daemon in foreground */
-#define MD_VERIFY 'v' /* verify: don't collect or deliver */
-#define MD_TEST 't' /* test mode: resolve addrs only */
-#define MD_INITALIAS 'i' /* initialize alias database */
-#define MD_PRINT 'p' /* print the queue */
-#define MD_PRINTNQE 'P' /* print number of entries in queue */
-#define MD_FREEZE 'z' /* freeze the configuration file */
-#define MD_HOSTSTAT 'h' /* print persistent host stat info */
-#define MD_PURGESTAT 'H' /* purge persistent host stat info */
-#define MD_QUEUERUN 'q' /* queue run */
-
-/* Note: see also include/sendmail/pathnames.h: GET_CLIENT_CF */
-
-/* values for e_sendmode -- send modes */
-#define SM_DELIVER 'i' /* interactive delivery */
-#define SM_FORK 'b' /* deliver in background */
-#if _FFR_DM_ONE
-#define SM_DM_ONE 'o' /* deliver first TA in background, then queue */
-#endif /* _FFR_DM_ONE */
-#define SM_QUEUE 'q' /* queue, don't deliver */
-#define SM_DEFER 'd' /* defer map lookups as well as queue */
-#define SM_VERIFY 'v' /* verify only (used internally) */
-#define DM_NOTSET (-1) /* DeliveryMode (per daemon) option not set */
-
-#define WILL_BE_QUEUED(m) ((m) == SM_QUEUE || (m) == SM_DEFER)
-
-/* used only as a parameter to sendall */
-#define SM_DEFAULT '\0' /* unspecified, use SendMode */
-
-/* functions */
-extern void set_delivery_mode __P((int, ENVELOPE *));
-
-/* values for e_errormode -- error handling modes */
-#define EM_PRINT 'p' /* print errors */
-#define EM_MAIL 'm' /* mail back errors */
-#define EM_WRITE 'w' /* write back errors */
-#define EM_BERKNET 'e' /* special berknet processing */
-#define EM_QUIET 'q' /* don't print messages (stat only) */
-
-
-/* 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 */
-
-
-/* how to handle messages without any recipient addresses */
-#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 */
-#define NRA_ADD_BCC 3 /* add empty Bcc: header */
-#define NRA_ADD_TO_UNDISCLOSED 4 /* add To: undisclosed:; header */
-
-
-/* flags to putxline */
-#define PXLF_NOTHINGSPECIAL 0 /* no special mapping */
-#define PXLF_MAPFROM 0x0001 /* map From_ to >From_ */
-#define PXLF_STRIP8BIT 0x0002 /* strip 8th bit */
-#define PXLF_HEADER 0x0004 /* map newlines in headers */
-#define PXLF_NOADDEOL 0x0008 /* if EOL not present, don't add one */
-#define PXLF_STRIPMQUOTE 0x0010 /* strip METAQUOTEs */
-
-/*
-** Privacy flags
-** These are bit values for the PrivacyFlags word.
-*/
-
-#define PRIV_PUBLIC 0 /* what have I got to hide? */
-#define PRIV_NEEDMAILHELO 0x00000001 /* insist on HELO for MAIL */
-#define PRIV_NEEDEXPNHELO 0x00000002 /* insist on HELO for EXPN */
-#define PRIV_NEEDVRFYHELO 0x00000004 /* insist on HELO for VRFY */
-#define PRIV_NOEXPN 0x00000008 /* disallow EXPN command */
-#define PRIV_NOVRFY 0x00000010 /* disallow VRFY command */
-#define PRIV_AUTHWARNINGS 0x00000020 /* flag possible auth probs */
-#define PRIV_NOVERB 0x00000040 /* disallow VERB command */
-#define PRIV_RESTRICTMAILQ 0x00010000 /* restrict mailq command */
-#define PRIV_RESTRICTQRUN 0x00020000 /* restrict queue run */
-#define PRIV_RESTRICTEXPAND 0x00040000 /* restrict alias/forward expansion */
-#define PRIV_NOETRN 0x00080000 /* disallow ETRN command */
-#define PRIV_NOBODYRETN 0x00100000 /* do not return bodies on bounces */
-#define PRIV_NORECEIPTS 0x00200000 /* disallow return receipts */
-#define PRIV_NOACTUALRECIPIENT 0x00400000 /* no X-Actual-Recipient in DSNs */
-
-/* don't give no info, anyway, anyhow (in the main SMTP transaction) */
-#define PRIV_GOAWAY 0x0000ffff
-
-/* struct defining such things */
-struct prival
-{
- char *pv_name; /* name of privacy flag */
- unsigned long pv_flag; /* numeric level */
-};
-
-EXTERN unsigned long PrivacyFlags; /* privacy flags */
-
-
-/*
-** Flags passed to remotename, parseaddr, allocaddr, and buildaddr.
-*/
-
-#define RF_SENDERADDR 0x001 /* this is a sender address */
-#define RF_HEADERADDR 0x002 /* this is a header address */
-#define RF_CANONICAL 0x004 /* strip comment information */
-#define RF_ADDDOMAIN 0x008 /* OK to do domain extension */
-#define RF_COPYPARSE 0x010 /* copy parsed user & host */
-#define RF_COPYPADDR 0x020 /* copy print address */
-#define RF_COPYALL (RF_COPYPARSE|RF_COPYPADDR)
-#define RF_COPYNONE 0
-#define RF_RM_ADDR 0x040 /* address to be removed */
-
-/*
-** Flags passed to rscheck
-*/
-
-#define RSF_RMCOMM 0x0001 /* strip comments */
-#define RSF_UNSTRUCTURED 0x0002 /* unstructured, ignore syntax errors */
-#define RSF_COUNT 0x0004 /* count rejections (statistics)? */
-
-/*
-** Flags passed to mime8to7 and putheader.
-*/
-
-#define M87F_OUTER 0 /* outer context */
-#define M87F_NO8BIT 0x0001 /* can't have 8-bit in this section */
-#define M87F_DIGEST 0x0002 /* processing multipart/digest */
-#define M87F_NO8TO7 0x0004 /* don't do 8->7 bit conversions */
-
-/* functions */
-extern bool mime7to8 __P((MCI *, HDR *, ENVELOPE *));
-extern int mime8to7 __P((MCI *, HDR *, ENVELOPE *, char **, int, int));
-
-/*
-** Flags passed to returntosender.
-*/
-
-#define RTSF_NO_BODY 0 /* send headers only */
-#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 *));
-
-/*
-** Mail Filters (milter)
-*/
-
-/*
-** 32-bit type used by milter
-** (needed by libmilter even if MILTER isn't defined)
-*/
-
-typedef SM_INT32 mi_int32;
-
-#if MILTER
-# define SMFTO_WRITE 0 /* Timeout for sending information */
-# define SMFTO_READ 1 /* Timeout waiting for a response */
-# define SMFTO_EOM 2 /* Timeout for ACK/NAK to EOM */
-# define SMFTO_CONNECT 3 /* Timeout for connect() */
-
-# define SMFTO_NUM_TO 4 /* Total number of timeouts */
-
-struct milter
-{
- char *mf_name; /* filter name */
- BITMAP256 mf_flags; /* MTA flags */
- mi_int32 mf_fvers; /* filter version */
- mi_int32 mf_fflags; /* filter flags */
- mi_int32 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 */
-#if _FFR_MILTER_CHECK
- /* for testing only */
- mi_int32 mf_mta_prot_version;
- mi_int32 mf_mta_prot_flags;
- mi_int32 mf_mta_actions;
-#endif /* _FFR_MILTER_CHECK */
-};
-
-struct milters
-{
- mi_int32 mis_flags; /* filter flags */
-};
-typedef struct milters milters_T;
-
-#define MIS_FL_NONE 0x00000000 /* no requirements... */
-#define MIS_FL_DEL_RCPT 0x00000001 /* can delete rcpt */
-#define MIS_FL_REJ_RCPT 0x00000002 /* can reject rcpt */
-
-
-/* MTA flags */
-# define SMF_REJECT 'R' /* Reject connection on filter fail */
-# define SMF_TEMPFAIL 'T' /* tempfail connection on failure */
-# define SMF_TEMPDROP '4' /* 421 connection on failure */
-
-EXTERN struct milter *InputFilters[MAXFILTERS];
-EXTERN char *InputFilterList;
-EXTERN int MilterLogLevel;
-
-/* functions */
-extern void setup_daemon_milters __P((void));
-#endif /* MILTER */
-
-/*
-** Vendor codes
-**
-** Vendors can customize sendmail to add special behaviour,
-** generally for back compatibility. Ideally, this should
-** be set up in the .cf file using the "V" command. However,
-** it's quite reasonable for some vendors to want the default
-** be their old version; this can be set using
-** -DVENDOR_DEFAULT=VENDOR_xxx
-** in the Makefile.
-**
-** Vendors should apply to sendmail@sendmail.org for
-** unique vendor codes.
-*/
-
-#define VENDOR_BERKELEY 1 /* Berkeley-native configuration file */
-#define VENDOR_SUN 2 /* Sun-native configuration file */
-#define VENDOR_HP 3 /* Hewlett-Packard specific config syntax */
-#define VENDOR_IBM 4 /* IBM specific config syntax */
-#define VENDOR_SENDMAIL 5 /* Sendmail, Inc. specific config syntax */
-#define VENDOR_DEC 6 /* Compaq, DEC, Digital */
-
-/* prototypes for vendor-specific hook routines */
-extern void vendor_daemon_setup __P((ENVELOPE *));
-extern void vendor_set_uid __P((UID_T));
-
-
-/*
-** Terminal escape codes.
-**
-** To make debugging output clearer.
-*/
-
-struct termescape
-{
- char *te_rv_on; /* turn reverse-video on */
- char *te_under_on; /* turn underlining on */
- char *te_normal; /* revert to normal output */
-};
-
-/*
-** Additional definitions
-*/
-
-/*
-** d_flags, see daemon.c
-** general rule: lower case: required, upper case: No
-*/
-
-#define D_AUTHREQ 'a' /* authentication required */
-#define D_BINDIF 'b' /* use if_addr for outgoing connection */
-#define D_CANONREQ 'c' /* canonification required (cf) */
-#define D_IFNHELO 'h' /* use if name for HELO */
-#define D_FQMAIL 'f' /* fq sender address required (cf) */
-#define D_FQRCPT 'r' /* fq recipient address required (cf) */
-#define D_SMTPS 's' /* SMTP over SSL (smtps) */
-#define D_UNQUALOK 'u' /* unqualified address is ok (cf) */
-#define D_NOAUTH 'A' /* no AUTH */
-#define D_NOCANON 'C' /* no canonification (cf) */
-#define D_NOETRN 'E' /* no ETRN (MSA) */
-#define D_NOTLS 'S' /* don't use STARTTLS */
-#define D_ETRNONLY ((char)0x01) /* allow only ETRN (disk low) */
-#define D_OPTIONAL 'O' /* optional socket */
-#define D_DISABLE ((char)0x02) /* optional socket disabled */
-#define D_ISSET ((char)0x03) /* this client struct is set */
-
-#if STARTTLS
-/*
-** TLS
-*/
-
-/* what to do in the TLS initialization */
-#define TLS_I_NONE 0x00000000 /* no requirements... */
-#define TLS_I_CERT_EX 0x00000001 /* cert must exist */
-#define TLS_I_CERT_UNR 0x00000002 /* cert must be g/o unreadable */
-#define TLS_I_KEY_EX 0x00000004 /* key must exist */
-#define TLS_I_KEY_UNR 0x00000008 /* key must be g/o unreadable */
-#define TLS_I_CERTP_EX 0x00000010 /* CA cert path must exist */
-#define TLS_I_CERTP_UNR 0x00000020 /* CA cert path must be g/o unreadable */
-#define TLS_I_CERTF_EX 0x00000040 /* CA cert file must exist */
-#define TLS_I_CERTF_UNR 0x00000080 /* CA cert file must be g/o unreadable */
-#define TLS_I_RSA_TMP 0x00000100 /* RSA TMP must be generated */
-#define TLS_I_USE_KEY 0x00000200 /* private key must usable */
-#define TLS_I_USE_CERT 0x00000400 /* certificate must be usable */
-#define TLS_I_VRFY_PATH 0x00000800 /* load verify path must succeed */
-#define TLS_I_VRFY_LOC 0x00001000 /* load verify default must succeed */
-#define TLS_I_CACHE 0x00002000 /* require cache */
-#define TLS_I_TRY_DH 0x00004000 /* try DH certificate */
-#define TLS_I_REQ_DH 0x00008000 /* require DH certificate */
-#define TLS_I_DHPAR_EX 0x00010000 /* require DH parameters */
-#define TLS_I_DHPAR_UNR 0x00020000 /* DH param. must be g/o unreadable */
-#define TLS_I_DH512 0x00040000 /* generate 512bit DH param */
-#define TLS_I_DH1024 0x00080000 /* generate 1024bit DH param */
-#define TLS_I_DH2048 0x00100000 /* generate 2048bit DH param */
-#define TLS_I_NO_VRFY 0x00200000 /* do not require authentication */
-#define TLS_I_KEY_OUNR 0x00400000 /* Key must be other unreadable */
-#define TLS_I_CRLF_EX 0x00800000 /* CRL file must exist */
-#define TLS_I_CRLF_UNR 0x01000000 /* CRL file must be g/o unreadable */
-
-/* require server cert */
-#define TLS_I_SRV_CERT (TLS_I_CERT_EX | TLS_I_KEY_EX | \
- TLS_I_KEY_UNR | TLS_I_KEY_OUNR | \
- TLS_I_CERTP_EX | TLS_I_CERTF_EX | \
- TLS_I_USE_KEY | TLS_I_USE_CERT | TLS_I_CACHE)
-
-/* server requirements */
-#define TLS_I_SRV (TLS_I_SRV_CERT | TLS_I_RSA_TMP | TLS_I_VRFY_PATH | \
- TLS_I_VRFY_LOC | TLS_I_TRY_DH | TLS_I_DH512 | \
- TLS_I_CACHE)
-
-/* client requirements */
-#define TLS_I_CLT (TLS_I_KEY_UNR | TLS_I_KEY_OUNR)
-
-#define TLS_AUTH_OK 0
-#define TLS_AUTH_NO 1
-#define TLS_AUTH_FAIL (-1)
-
-/* functions */
-extern bool init_tls_library __P((void));
-extern bool inittls __P((SSL_CTX **, unsigned long, bool, char *, char *, char *, char *, char *));
-extern bool initclttls __P((bool));
-extern void setclttls __P((bool));
-extern bool initsrvtls __P((bool));
-extern int tls_get_info __P((SSL *, bool, char *, MACROS_T *, bool));
-extern int endtls __P((SSL *, char *));
-extern void tlslogerr __P((const char *));
-
-
-EXTERN char *CACertPath; /* path to CA certificates (dir. with hashes) */
-EXTERN char *CACertFile; /* file with CA certificate */
-EXTERN char *CltCertFile; /* file with client certificate */
-EXTERN char *CltKeyFile; /* file with client private key */
-# if _FFR_TLS_1
-EXTERN char *CipherList; /* list of ciphers */
-EXTERN char *DHParams5; /* file with DH parameters (512) */
-# endif /* _FFR_TLS_1 */
-EXTERN char *DHParams; /* file with DH parameters */
-EXTERN char *RandFile; /* source of random data */
-EXTERN char *SrvCertFile; /* file with server certificate */
-EXTERN char *SrvKeyFile; /* file with server private key */
-EXTERN char *CRLFile; /* file CRLs */
-#if _FFR_CRLPATH
-EXTERN char *CRLPath; /* path to CRLs (dir. with hashes) */
-#endif /* _FFR_CRLPATH */
-EXTERN unsigned long TLS_Srv_Opts; /* TLS server options */
-#endif /* STARTTLS */
-
-/*
-** Queue related items
-*/
-
-/* queue file names */
-#define ANYQFL_LETTER '?'
-#define QUARQF_LETTER 'h'
-#define DATAFL_LETTER 'd'
-#define XSCRPT_LETTER 'x'
-#define NORMQF_LETTER 'q'
-#define NEWQFL_LETTER 't'
-
-# define TEMPQF_LETTER 'T'
-# define LOSEQF_LETTER 'Q'
-
-/* queue sort order */
-#define QSO_BYPRIORITY 0 /* sort by message priority */
-#define QSO_BYHOST 1 /* sort by first host name */
-#define QSO_BYTIME 2 /* sort by submission time */
-#define QSO_BYFILENAME 3 /* sort by file name only */
-#define QSO_RANDOM 4 /* sort in random order */
-#define QSO_BYMODTIME 5 /* sort by modification time */
-#define QSO_NONE 6 /* do not sort */
-#if _FFR_RHS
-# define QSO_BYSHUFFLE 7 /* sort by shuffled host name */
-#endif /* _FFR_RHS */
-
-#define NOQGRP (-1) /* no queue group (yet) */
-#define ENVQGRP (-2) /* use queue group of envelope */
-#define NOAQGRP (-3) /* no queue group in addr (yet) */
-#define ISVALIDQGRP(x) ((x) >= 0) /* valid queue group? */
-#define NOQDIR (-1) /* no queue directory (yet) */
-#define ENVQDIR (-2) /* use queue directory of envelope */
-#define NOAQDIR (-3) /* no queue directory in addr (yet) */
-#define ISVALIDQDIR(x) ((x) >= 0) /* valid queue directory? */
-#define RS_QUEUEGROUP "queuegroup" /* ruleset for queue group selection */
-
-#define NOW ((time_t) (-1)) /* queue return: now */
-
-/* SuperSafe values */
-#define SAFE_NO 0 /* no fsync(): don't use... */
-#define SAFE_INTERACTIVE 1 /* limit fsync() in -odi */
-#define SAFE_REALLY 2 /* always fsync() */
-#define SAFE_REALLY_POSTMILTER 3 /* fsync() if milter says OK */
-
-/* QueueMode bits */
-#define QM_NORMAL ' '
-#define QM_QUARANTINE 'Q'
-#define QM_LOST 'L'
-
-/* Queue Run Limitations */
-struct queue_char
-{
- char *queue_match; /* string to match */
- bool queue_negate; /* or not match, if set */
- struct queue_char *queue_next;
-};
-
-/* run_work_group() flags */
-#define RWG_NONE 0x0000
-#define RWG_FORK 0x0001
-#define RWG_VERBOSE 0x0002
-#define RWG_PERSISTENT 0x0004
-#define RWG_FORCE 0x0008
-#define RWG_RUNALL 0x0010
-
-typedef struct queue_char QUEUE_CHAR;
-
-EXTERN int volatile CurRunners; /* current number of runner children */
-EXTERN int MaxQueueRun; /* maximum number of jobs in one queue run */
-EXTERN int MaxQueueChildren; /* max # of forked queue children */
-EXTERN int MaxRunnersPerQueue; /* max # proc's active in queue group */
-EXTERN int NiceQueueRun; /* nice queue runs to this value */
-EXTERN int NumQueue; /* number of queue groups */
-EXTERN int QueueFileMode; /* mode on files in mail queue */
-EXTERN int QueueMode; /* which queue items to act upon */
-EXTERN int QueueSortOrder; /* queue sorting order algorithm */
-EXTERN time_t MinQueueAge; /* min delivery interval */
-EXTERN time_t QueueIntvl; /* intervals between running the queue */
-EXTERN char *QueueDir; /* location of queue directory */
-EXTERN QUEUE_CHAR *QueueLimitId; /* limit queue run to id */
-EXTERN QUEUE_CHAR *QueueLimitQuarantine; /* limit queue run to quarantine reason */
-EXTERN QUEUE_CHAR *QueueLimitRecipient; /* limit queue run to rcpt */
-EXTERN QUEUE_CHAR *QueueLimitSender; /* limit queue run to sender */
-EXTERN QUEUEGRP *Queue[MAXQUEUEGROUPS + 1]; /* queue groups */
-
-/* functions */
-extern void assign_queueid __P((ENVELOPE *));
-extern ADDRESS *copyqueue __P((ADDRESS *, SM_RPOOL_T *));
-extern void cleanup_queues __P((void));
-extern bool doqueuerun __P((void));
-extern void initsys __P((ENVELOPE *));
-extern void loseqfile __P((ENVELOPE *, char *));
-extern int name2qid __P((char *));
-extern char *qid_printname __P((ENVELOPE *));
-extern char *qid_printqueue __P((int, int));
-extern void quarantine_queue __P((char *, int));
-extern char *queuename __P((ENVELOPE *, int));
-extern void queueup __P((ENVELOPE *, bool, bool));
-extern bool runqueue __P((bool, bool, bool, bool));
-extern bool run_work_group __P((int, int));
-extern void set_def_queueval __P((QUEUEGRP *, bool));
-extern void setup_queues __P((bool));
-extern bool setnewqueue __P((ENVELOPE *));
-extern bool shouldqueue __P((long, time_t));
-extern void sync_queue_time __P((void));
-extern void init_qid_alg __P((void));
-extern int print_single_queue __P((int, int));
-#if REQUIRES_DIR_FSYNC
-# define SYNC_DIR(path, panic) sync_dir(path, panic)
-extern void sync_dir __P((char *, bool));
-#else /* REQUIRES_DIR_FSYNC */
-# define SYNC_DIR(path, panic) ((void) 0)
-#endif /* REQUIRES_DIR_FSYNC */
-
-/*
-** Timeouts
-**
-** Indicated values are the MINIMUM per RFC 1123 section 5.3.2.
-*/
-
-EXTERN struct
-{
- /* RFC 1123-specified timeouts [minimum value] */
- time_t to_initial; /* initial greeting timeout [5m] */
- time_t to_mail; /* MAIL command [5m] */
- time_t to_rcpt; /* RCPT command [5m] */
- time_t to_datainit; /* DATA initiation [2m] */
- time_t to_datablock; /* DATA block [3m] */
- time_t to_datafinal; /* DATA completion [10m] */
- time_t to_nextcommand; /* next command [5m] */
- /* following timeouts are not mentioned in RFC 1123 */
- time_t to_iconnect; /* initial connection timeout (first try) */
- time_t to_connect; /* initial connection timeout (later tries) */
- time_t to_aconnect; /* all connections timeout (MX and A records) */
- time_t to_rset; /* RSET command */
- time_t to_helo; /* HELO command */
- time_t to_quit; /* QUIT command */
- 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 */
- time_t to_lhlo; /* LMTP: LHLO command */
-#if SASL
- time_t to_auth; /* AUTH dialogue [10m] */
-#endif /* SASL */
-#if STARTTLS
- time_t to_starttls; /* STARTTLS dialogue [10m] */
-#endif /* STARTTLS */
- /* following are per message */
- time_t to_q_return[MAXTOCLASS]; /* queue return timeouts */
- time_t to_q_warning[MAXTOCLASS]; /* queue warning timeouts */
- 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_DSN 3 /* DSN 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));
-
-/*
-** Interface probing
-*/
-
-#define DPI_PROBENONE 0 /* Don't probe any interfaces */
-#define DPI_PROBEALL 1 /* Probe all interfaces */
-#define DPI_SKIPLOOPBACK 2 /* Don't probe loopback interfaces */
-
-/*
-** Trace information
-*/
-
-/* macros for debugging flags */
-#define tTd(flag, level) (tTdvect[flag] >= (unsigned char)level)
-#define tTdlevel(flag) (tTdvect[flag])
-
-/* variables */
-extern unsigned char tTdvect[100]; /* trace vector */
-
-/*
-** Miscellaneous information.
-*/
-
-/*
-** The "no queue id" queue id for sm_syslog
-*/
-
-#define NOQID ""
-
-#define CURHOSTNAME (CurHostName == NULL ? "local" : CurHostName)
-
-/*
-** Some in-line functions
-*/
-
-/* set exit status */
-#define setstat(s) { \
- if (ExitStat == EX_OK || ExitStat == EX_TEMPFAIL) \
- ExitStat = s; \
- }
-
-
-#define STRUCTCOPY(s, d) d = s
-
-/* free a pointer if it isn't NULL and set it to NULL */
-#define SM_FREE_CLR(p) \
- if ((p) != NULL) \
- { \
- sm_free(p); \
- (p) = NULL; \
- } \
- else
-
-/*
-** Update a permanent string variable with a new value.
-** The old value is freed, the new value is strdup'ed.
-**
-** We use sm_pstrdup_x to duplicate the string because it raises
-** an exception on error, and because it allocates "permanent storage"
-** which is not expected to be freed before process exit.
-** The latter is important for memory leak analysis.
-**
-** If an exception occurs while strdup'ing the new value,
-** then the variable remains set to the old value.
-** That's why the strdup must occur before we free the old value.
-**
-** The macro uses a do loop so that this idiom will work:
-** if (...)
-** PSTRSET(var, val1);
-** else
-** PSTRSET(var, val2);
-*/
-#define PSTRSET(var, val) \
- do \
- { \
- char *_newval = sm_pstrdup_x(val); \
- if (var != NULL) \
- sm_free(var); \
- var = _newval; \
- } while (0)
-
-#define _CHECK_RESTART \
- do \
- { \
- if (ShutdownRequest != NULL) \
- shutdown_daemon(); \
- else if (RestartRequest != NULL) \
- restart_daemon(); \
- else if (RestartWorkGroup) \
- restart_marked_work_groups(); \
- } while (0)
-
-# define CHECK_RESTART _CHECK_RESTART
-
-/* reply types (text in SmtpMsgBuffer) */
-#define XS_DEFAULT 0
-#define XS_STARTTLS 1
-#define XS_AUTH 2
-
-/*
-** Global variables.
-*/
-
-#if _FFR_ADDR_TYPE_MODES
-EXTERN bool AddrTypeModes; /* addr_type: extra "mode" information */
-#endif /* _FFR_ADDR_TYPE_MODES */
-EXTERN bool AllowBogusHELO; /* allow syntax errors on HELO command */
-EXTERN bool CheckAliases; /* parse addresses during newaliases */
-#if _FFR_QUEUE_RUN_PARANOIA
-EXTERN int CheckQueueRunners; /* check whether queue runners are OK */
-#endif /* _FFR_QUEUE_RUN_PARANOIA */
-EXTERN bool ColonOkInAddr; /* single colon legal in address */
-#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
-EXTERN bool ConfigFileRead; /* configuration file has been read */
-#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
-EXTERN bool DisConnected; /* running with OutChannel redirect to transcript file */
-EXTERN bool DontExpandCnames; /* do not $[...$] expand CNAMEs */
-EXTERN bool DontInitGroups; /* avoid initgroups() because of NIS cost */
-EXTERN bool DontLockReadFiles; /* don't read lock support files */
-EXTERN bool 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 EightBitAddrOK; /* we'll let 8-bit addresses through */
-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 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 */
-#if REQUIRES_DIR_FSYNC
-EXTERN bool RequiresDirfsync; /* requires fsync() for directory */
-#endif /* REQUIRES_DIR_FSYNC */
-EXTERN bool volatile RestartWorkGroup; /* daemon needs to restart some work groups */
-EXTERN bool RrtImpliesDsn; /* turn Return-Receipt-To: into DSN */
-EXTERN bool SaveFrom; /* save leading "From" lines */
-EXTERN bool SendMIMEErrors; /* send error messages in MIME format */
-EXTERN bool SevenBitInput; /* force 7-bit data on input */
-EXTERN bool SingleLineFromHeader; /* force From: header to be one line */
-EXTERN bool SingleThreadDelivery; /* single thread hosts on delivery */
-EXTERN bool SoftBounce; /* replace 5xy by 4xy (for testing) */
-EXTERN bool volatile StopRequest; /* stop sending output */
-EXTERN bool SuprErrs; /* set if we are suppressing errors */
-EXTERN bool TryNullMXList; /* if we are the best MX, try host directly */
-EXTERN bool UseMSP; /* mail submission: group writable queue ok? */
-EXTERN bool WorkAroundBrokenAAAA; /* some nameservers return SERVFAIL on AAAA queries */
-EXTERN bool UseErrorsTo; /* use Errors-To: header (back compat) */
-EXTERN bool UseNameServer; /* using DNS -- interpret h_errno & MX RRs */
-EXTERN char InetMode; /* default network for daemon mode */
-EXTERN char OpMode; /* operation mode, see below */
-EXTERN char SpaceSub; /* substitution for <lwsp> */
-EXTERN int BadRcptThrottle; /* Throttle rejected RCPTs per SMTP message */
-EXTERN int CheckpointInterval; /* queue file checkpoint interval */
-EXTERN int ConfigLevel; /* config file level */
-EXTERN int ConnRateThrottle; /* throttle for SMTP connection rate */
-EXTERN int volatile CurChildren; /* current number of daemonic children */
-EXTERN int CurrentLA; /* current load average */
-EXTERN int DefaultNotify; /* default DSN notification flags */
-EXTERN int DelayLA; /* load average to delay connections */
-EXTERN int DontProbeInterfaces; /* don't probe interfaces for names */
-EXTERN int Errors; /* set if errors (local to single pass) */
-EXTERN int ExitStat; /* exit status code */
-EXTERN int FastSplit; /* fast initial splitting of envelopes */
-EXTERN int FileMode; /* mode on files */
-EXTERN int LineNumber; /* line number in current input */
-EXTERN int LogLevel; /* level of logging to perform */
-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 MaxMimeFieldLength; /* maximum MIME field length */
-EXTERN int MaxMimeHeaderLength; /* maximum MIME header length */
-EXTERN int MaxNOOPCommands; /* max "noise" commands before slowdown */
-
-EXTERN int MaxRcptPerMsg; /* max recipients per SMTP message */
-EXTERN int MaxRuleRecursion; /* maximum depth of ruleset recursion */
-#if _FFR_MSG_ACCEPT
-EXTERN char *MessageAccept; /* "Message accepted for delivery" reply text */
-#endif /* _FFR_MSG_ACCEPT */
-
-EXTERN int MimeMode; /* MIME processing mode */
-EXTERN int NoRecipientAction;
-
-#if SM_CONF_SHM
-EXTERN int Numfilesys; /* number of queue file systems */
-EXTERN int *PNumFileSys;
-# define NumFileSys (*PNumFileSys)
-# else /* SM_CONF_SHM */
-EXTERN int NumFileSys; /* number of queue file systems */
-# endif /* SM_CONF_SHM */
-
-EXTERN int QueueLA; /* load average starting forced queueing */
-EXTERN int RefuseLA; /* load average refusing connections */
-EXTERN time_t RejectLogInterval; /* time btwn log msgs while refusing */
-#if _FFR_MEMSTAT
-EXTERN long QueueLowMem; /* low memory starting forced queueing */
-EXTERN long RefuseLowMem; /* low memory refusing connections */
-EXTERN char *MemoryResource;/* memory resource to look up */
-#endif /* _FFR_MEMSTAT */
-EXTERN int SuperSafe; /* be extra careful, even if expensive */
-EXTERN int VendorCode; /* vendor-specific operation enhancements */
-EXTERN int Verbose; /* set if blow-by-blow desired */
-EXTERN gid_t DefGid; /* default gid to run as */
-EXTERN gid_t RealGid; /* real gid of caller */
-EXTERN gid_t RunAsGid; /* GID to become for bulk of run */
-EXTERN gid_t EffGid; /* effective gid */
-#if SM_CONF_SHM
-EXTERN key_t ShmKey; /* shared memory key */
-EXTERN char *ShmKeyFile; /* shared memory key file */
-#endif /* SM_CONF_SHM */
-EXTERN pid_t CurrentPid; /* current process id */
-EXTERN pid_t DaemonPid; /* process id of daemon */
-EXTERN pid_t PidFilePid; /* daemon/queue runner who wrote pid file */
-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 buf for in-core data file */
-EXTERN time_t DeliverByMin; /* deliver by minimum time */
-EXTERN time_t DialDelay; /* delay between dial-on-demand tries */
-EXTERN time_t SafeAlias; /* interval to wait until @:@ in alias file */
-EXTERN time_t ServiceCacheMaxAge; /* refresh interval for cache */
-EXTERN size_t XscriptFileBufferSize; /* size of buf for in-core transcript file */
-EXTERN MODE_T OldUmask; /* umask when sendmail starts up */
-EXTERN long MaxMessageSize; /* advertised max size we will accept */
-EXTERN long MinBlocksFree; /* min # of blocks free on queue fs */
-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 */
-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 *FallbackSmartHost; /* fall back smart host */
-EXTERN char *FileName; /* name to print on error messages */
-EXTERN char *ForwardPath; /* path to search for .forward files */
-EXTERN char *HeloName; /* hostname to announce in HELO */
-EXTERN char *HelpFile; /* location of SMTP help file */
-EXTERN char *HostStatDir; /* location of host status information */
-EXTERN char *HostsFile; /* path to /etc/hosts file */
-extern char *Mbdb; /* mailbox database type */
-EXTERN char *MustQuoteChars; /* quote these characters in phrases */
-EXTERN char *MyHostName; /* name of this host for SMTP messages */
-EXTERN char *OperatorChars; /* operators (old $o macro) */
-EXTERN char *PidFile; /* location of proc id file [conf.c] */
-EXTERN char *PostMasterCopy; /* address to get errs cc's */
-EXTERN char *ProcTitlePrefix; /* process title prefix */
-EXTERN char *RealHostName; /* name of host we are talking to */
-EXTERN char *RealUserName; /* real user name of caller */
-EXTERN char *volatile RestartRequest;/* a sendmail restart has been requested */
-EXTERN char *RunAsUserName; /* user to become for bulk of run */
-EXTERN char *SafeFileEnv; /* chroot location for file delivery */
-EXTERN char *ServiceSwitchFile; /* backup service switch */
-EXTERN char *volatile ShutdownRequest;/* a sendmail shutdown has been requested */
-EXTERN char *SmtpGreeting; /* SMTP greeting message (old $e macro) */
-EXTERN char *SmtpPhase; /* current phase in SMTP processing */
-EXTERN char SmtpError[MAXLINE]; /* save failure error messages */
-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; /* saved user (input) environment */
-EXTERN char **SaveArgv; /* argument vector for re-execing */
-EXTERN BITMAP256 DontBlameSendmail; /* DontBlameSendmail bits */
-EXTERN SM_FILE_T *InChannel; /* input connection */
-EXTERN SM_FILE_T *OutChannel; /* output connection */
-EXTERN SM_FILE_T *TrafficLogFile; /* file in which to log all traffic */
-#if HESIOD
-EXTERN void *HesiodContext;
-#endif /* HESIOD */
-EXTERN ENVELOPE *CurEnv; /* envelope currently being processed */
-EXTERN char *RuleSetNames[MAXRWSETS]; /* ruleset number to name */
-EXTERN char *UserEnviron[MAXUSERENVIRON + 1];
-EXTERN struct rewrite *RewriteRules[MAXRWSETS];
-EXTERN struct termescape TermEscape; /* terminal escape codes */
-EXTERN SOCKADDR ConnectOnlyTo; /* override connection address (for testing) */
-EXTERN SOCKADDR RealHostAddr; /* address of host we are talking to */
-extern const SM_EXC_TYPE_T EtypeQuickAbort; /* type of a QuickAbort exception */
-
-
-EXTERN int ConnectionRateWindowSize;
-
-/*
-** Declarations of useful functions
-*/
-
-/* Transcript file */
-extern void closexscript __P((ENVELOPE *));
-extern void openxscript __P((ENVELOPE *));
-
-/* error related */
-extern void buffer_errors __P((void));
-extern void flush_errors __P((bool));
-extern void PRINTFLIKE(1, 2) message __P((const char *, ...));
-extern void PRINTFLIKE(1, 2) nmessage __P((const char *, ...));
-extern void PRINTFLIKE(1, 2) syserr __P((const char *, ...));
-extern void PRINTFLIKE(2, 3) usrerrenh __P((char *, const char *, ...));
-extern void PRINTFLIKE(1, 2) usrerr __P((const char *, ...));
-extern int isenhsc __P((const char *, int));
-extern int extenhsc __P((const char *, int, char *));
-
-/* 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 *, SM_FILE_T *, bool, bool));
-extern bool rebuildaliases __P((MAP *, bool));
-extern void setalias __P((char *));
-
-/* logging */
-extern void logdelivery __P((MAILER *, MCI *, char *, const char *, ADDRESS *, time_t, ENVELOPE *));
-extern void logsender __P((ENVELOPE *, char *));
-extern void PRINTFLIKE(3, 4) sm_syslog __P((int, const char *, const char *, ...));
-
-/* SMTP */
-extern void giveresponse __P((int, char *, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *, ADDRESS *));
-extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)__P((char *, bool, MAILER *, MCI *, ENVELOPE *)), char **, int));
-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 *, ADDRESS *, time_t));
-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 smtpprobe __P((MCI *));
-extern void smtpquit __P((MAILER *, MCI *, ENVELOPE *));
-extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *, ADDRESS *, time_t));
-extern void smtprset __P((MAILER *, MCI *, ENVELOPE *));
-
-#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
-#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, int, char *, bool, bool, ENVELOPE *));
-extern pid_t doworklist __P((ENVELOPE *, bool, bool));
-extern int endmailer __P((MCI *, ENVELOPE *, char **));
-extern int mailfile __P((char *volatile, MAILER *volatile, ADDRESS *, volatile long, ENVELOPE *));
-extern void sendall __P((ENVELOPE *, int));
-
-/* stats */
-#define STATS_NORMAL 'n'
-#define STATS_QUARANTINE 'q'
-#define STATS_REJECT 'r'
-#define STATS_CONNECT 'c'
-
-extern void markstats __P((ENVELOPE *, ADDRESS *, int));
-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 MILTER
-/* milter functions */
-extern void milter_config __P((char *, struct milter **, int));
-extern void milter_setup __P((char *));
-extern void milter_set_option __P((char *, char *, bool));
-extern bool milter_init __P((ENVELOPE *, char *, milters_T *));
-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_data_cmd __P((ENVELOPE *, char *));
-extern char *milter_envrcpt __P((char **, ENVELOPE *, char *, bool));
-extern char *milter_data __P((ENVELOPE *, char *));
-extern char *milter_unknown __P((char *, ENVELOPE *, char *));
-#endif /* MILTER */
-
-extern char *addquotes __P((char *, SM_RPOOL_T *));
-extern char *arpadate __P((char *));
-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 int check_bodytype __P((char *));
-extern void buildfname __P((char *, char *, char *, int));
-extern bool chkclientmodifiers __P((int));
-extern bool chkdaemonmodifiers __P((int));
-extern int checkcompat __P((ADDRESS *, ENVELOPE *));
-#ifdef XDEBUG
-extern void checkfd012 __P((char *));
-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));
-#if SM_CONF_SHM
-extern void cleanup_shm __P((bool));
-#endif /* SM_CONF_SHM */
-extern void close_sendmail_pid __P((void));
-extern void clrdaemon __P((void));
-extern void collect __P((SM_FILE_T *, bool, HDR **, ENVELOPE *, bool));
-extern bool connection_rate_check __P((SOCKADDR *, ENVELOPE *));
-extern time_t convtime __P((char *, int));
-extern char **copyplist __P((char **, bool, SM_RPOOL_T *));
-extern void copy_class __P((int, int));
-extern int count_open_connections __P((SOCKADDR *));
-extern time_t curtime __P((void));
-extern char *defcharset __P((ENVELOPE *));
-extern char *denlstring __P((char *, bool, bool));
-extern void dferror __P((SM_FILE_T *volatile, char *, ENVELOPE *));
-extern void disconnect __P((int, ENVELOPE *));
-extern void disk_status __P((SM_FILE_T *, char *));
-extern bool dns_getcanonname __P((char *, int, bool, int *, int *));
-extern pid_t dofork __P((void));
-extern int drop_privileges __P((bool));
-extern int dsntoexitstat __P((char *));
-extern void dumpfd __P((int, bool, bool));
-#if SM_HEAP_CHECK
-extern void dumpstab __P((void));
-#endif /* SM_HEAP_CHECK */
-extern void dumpstate __P((char *));
-extern bool enoughdiskspace __P((long, ENVELOPE *));
-extern char *exitstat __P((char *));
-extern void fatal_error __P((SM_EXC_T *));
-extern char *fgetfolded __P((char *, int *, SM_FILE_T *));
-extern void fill_fd __P((int, char *));
-extern char *find_character __P((char *, int));
-extern int finduser __P((char *, bool *, SM_MBDB_T *));
-extern void finis __P((bool, bool, volatile int));
-extern void fixcrlf __P((char *, bool));
-extern long freediskspace __P((const char *, long *));
-#if NETINET6 && NEEDSGETIPNODE
-extern void freehostent __P((struct hostent *));
-#endif /* NETINET6 && NEEDSGETIPNODE */
-extern char *get_column __P((char *, int, int, char *, int));
-extern char *getauthinfo __P((int, bool *));
-extern int getdtsize __P((void));
-extern int getla __P((void));
-extern char *getmodifiers __P((char *, BITMAP256));
-extern BITMAP256 *getrequests __P((ENVELOPE *));
-extern char *getvendor __P((int));
-extern void help __P((char *, ENVELOPE *));
-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 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 void logundelrcpts __P((ENVELOPE *, char *, int, bool));
-extern char lower __P((int));
-extern void makelower __P((char *));
-extern int makeconnection_ds __P((char *, MCI *));
-extern int makeconnection __P((char *, volatile unsigned int, MCI *, ENVELOPE *, time_t));
-extern void makeworkgroups __P((void));
-extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool));
-extern void mark_work_group_restart __P((int, int));
-extern MCI *mci_new __P((SM_RPOOL_T *));
-extern char *munchstring __P((char *, char **, int));
-extern struct hostent *myhostname __P((char *, int));
-extern char *newstr __P((const char *));
-#if NISPLUS
-extern char *nisplus_default_domain __P((void)); /* extern for Sun */
-#endif /* NISPLUS */
-extern bool path_is_dir __P((char *, bool));
-extern int pickqdir __P((QUEUEGRP *qg, long fsize, ENVELOPE *e));
-extern char *pintvl __P((time_t, bool));
-extern void printav __P((SM_FILE_T *, char **));
-extern void printmailer __P((SM_FILE_T *, MAILER *));
-extern void printnqe __P((SM_FILE_T *, char *));
-extern void printopenfds __P((bool));
-extern void printqueue __P((void));
-extern void printrules __P((void));
-extern pid_t prog_open __P((char **, int *, ENVELOPE *));
-extern bool putline __P((char *, MCI *));
-extern bool putxline __P((char *, size_t, MCI *, int));
-extern void queueup_macros __P((int, SM_FILE_T *, ENVELOPE *));
-extern void readcf __P((char *, bool, ENVELOPE *));
-extern SIGFUNC_DECL reapchild __P((int));
-extern int releasesignal __P((int));
-extern void resetlimits __P((void));
-extern void restart_daemon __P((void));
-extern void restart_marked_work_groups __P((void));
-extern bool rfc822_string __P((char *));
-extern void rmexpstab __P((void));
-extern bool 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 set_op_mode __P((int));
-extern void setoption __P((int, char *, bool, bool, ENVELOPE *));
-extern sigfunc_t setsignal __P((int, sigfunc_t));
-extern void sm_setuserenv __P((const char *, const char *));
-extern void settime __P((ENVELOPE *));
-extern char *sfgets __P((char *, int, SM_FILE_T *, time_t, char *));
-extern char *shortenstring __P((const char *, size_t));
-extern char *shorten_hostname __P((char []));
-extern bool shorten_rfc822_string __P((char *, size_t));
-extern void shutdown_daemon __P((void));
-extern void sm_closefrom __P((int lowest, int highest));
-extern void sm_close_on_exec __P((int lowest, int highest));
-extern struct hostent *sm_gethostbyname __P((char *, int));
-extern struct hostent *sm_gethostbyaddr __P((char *, int, int));
-extern void sm_getla __P((void));
-extern struct passwd *sm_getpwnam __P((char *));
-extern struct passwd *sm_getpwuid __P((UID_T));
-extern void sm_setproctitle __P((bool, ENVELOPE *, const char *, ...));
-extern pid_t sm_wait __P((int *));
-extern bool split_by_recipient __P((ENVELOPE *e));
-extern void stop_sendmail __P((void));
-extern void stripbackslash __P((char *));
-extern bool strreplnonprt __P((char *, int));
-extern bool strcontainedin __P((bool, char *, char *));
-extern int switch_map_find __P((char *, char *[], short []));
-#if STARTTLS
-extern void tls_set_verify __P((SSL_CTX *, SSL *, bool));
-#endif /* STARTTLS */
-extern bool transienterror __P((int));
-extern void truncate_at_delim __P((char *, size_t, int));
-extern void tTflag __P((char *));
-extern void tTsetup __P((unsigned char *, unsigned int, char *));
-extern SIGFUNC_DECL tick __P((int));
-extern char *ttypath __P((void));
-extern void unlockqueue __P((ENVELOPE *));
-#if !HASUNSETENV
-extern void unsetenv __P((char *));
-#endif /* !HASUNSETENV */
-
-/* update file system information: +/- some blocks */
-#if SM_CONF_SHM
-extern void upd_qs __P((ENVELOPE *, bool, bool, char *));
-# define updfs(e, count, space, where) upd_qs(e, count, space, where)
-#else /* SM_CONF_SHM */
-# define updfs(e, count, space, where)
-# define upd_qs(e, count, space, where)
-#endif /* SM_CONF_SHM */
-
-extern char *username __P((void));
-extern bool usershellok __P((char *, char *));
-extern void vendor_post_defaults __P((ENVELOPE *));
-extern void vendor_pre_defaults __P((ENVELOPE *));
-extern int waitfor __P((pid_t));
-extern bool writable __P((char *, ADDRESS *, long));
-#if SM_HEAP_CHECK
-# define xalloc(size) xalloc_tagged(size, __FILE__, __LINE__)
-extern char *xalloc_tagged __P((int, char*, int));
-#else /* SM_HEAP_CHECK */
-extern char *xalloc __P((int));
-#endif /* SM_HEAP_CHECK */
-extern void xputs __P((SM_FILE_T *, const char *));
-extern char *xtextify __P((char *, char *));
-extern bool xtextok __P((char *));
-extern int xunlink __P((char *));
-extern char *xuntextify __P((char *));
-
-
-#undef EXTERN
-#endif /* ! _SENDMAIL_H */
diff --git a/contrib/sendmail/src/sfsasl.c b/contrib/sendmail/src/sfsasl.c
deleted file mode 100644
index 216d87e..0000000
--- a/contrib/sendmail/src/sfsasl.c
+++ /dev/null
@@ -1,952 +0,0 @@
-/*
- * Copyright (c) 1999-2006 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sm/gen.h>
-SM_RCSID("@(#)$Id: sfsasl.c,v 8.115 2006/04/18 21:34:07 ca Exp $")
-#include <stdlib.h>
-#include <sendmail.h>
-#include <sm/time.h>
-#include <errno.h>
-
-/* allow to disable error handling code just in case... */
-#ifndef DEAL_WITH_ERROR_SSL
-# define DEAL_WITH_ERROR_SSL 1
-#endif /* ! DEAL_WITH_ERROR_SSL */
-
-#if SASL
-# include "sfsasl.h"
-
-/* Structure used by the "sasl" file type */
-struct sasl_obj
-{
- SM_FILE_T *fp;
- sasl_conn_t *conn;
-};
-
-struct sasl_info
-{
- SM_FILE_T *fp;
- sasl_conn_t *conn;
-};
-
-/*
-** SASL_GETINFO - returns requested information about a "sasl" file
-** descriptor.
-**
-** Parameters:
-** fp -- the file descriptor
-** what -- the type of information requested
-** valp -- the thang to return the information in
-**
-** Returns:
-** -1 for unknown requests
-** >=0 on success with valp filled in (if possible).
-*/
-
-static int sasl_getinfo __P((SM_FILE_T *, int, void *));
-
-static int
-sasl_getinfo(fp, what, valp)
- SM_FILE_T *fp;
- int what;
- void *valp;
-{
- struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
-
- switch (what)
- {
- case SM_IO_WHAT_FD:
- if (so->fp == NULL)
- return -1;
- return so->fp->f_file; /* for stdio fileno() compatability */
-
- case SM_IO_IS_READABLE:
- if (so->fp == NULL)
- return 0;
-
- /* get info from underlying file */
- return sm_io_getinfo(so->fp, what, valp);
-
- default:
- return -1;
- }
-}
-
-/*
-** SASL_OPEN -- creates the sasl specific information for opening a
-** file of the sasl type.
-**
-** Parameters:
-** fp -- the file pointer associated with the new open
-** info -- contains the sasl connection information pointer and
-** the original SM_FILE_T that holds the open
-** flags -- ignored
-** rpool -- ignored
-**
-** Returns:
-** 0 on success
-*/
-
-static int sasl_open __P((SM_FILE_T *, const void *, int, const void *));
-
-/* ARGSUSED2 */
-static int
-sasl_open(fp, info, flags, rpool)
- SM_FILE_T *fp;
- const void *info;
- int flags;
- const void *rpool;
-{
- struct sasl_obj *so;
- struct sasl_info *si = (struct sasl_info *) info;
-
- so = (struct sasl_obj *) sm_malloc(sizeof(struct sasl_obj));
- if (so == NULL)
- {
- errno = ENOMEM;
- return -1;
- }
- so->fp = si->fp;
- so->conn = si->conn;
-
- /*
- ** The underlying 'fp' is set to SM_IO_NOW so that the entire
- ** encoded string is written in one chunk. Otherwise there is
- ** the possibility that it may appear illegal, bogus or
- ** mangled to the other side of the connection.
- ** We will read or write through 'fp' since it is the opaque
- ** connection for the communications. We need to treat it this
- ** way in case the encoded string is to be sent down a TLS
- ** connection rather than, say, sm_io's stdio.
- */
-
- (void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
- fp->f_cookie = so;
- return 0;
-}
-
-/*
-** SASL_CLOSE -- close the sasl specific parts of the sasl file pointer
-**
-** Parameters:
-** fp -- the file pointer to close
-**
-** Returns:
-** 0 on success
-*/
-
-static int sasl_close __P((SM_FILE_T *));
-
-static int
-sasl_close(fp)
- SM_FILE_T *fp;
-{
- struct sasl_obj *so;
-
- so = (struct sasl_obj *) fp->f_cookie;
- if (so == NULL)
- return 0;
- if (so->fp != NULL)
- {
- sm_io_close(so->fp, SM_TIME_DEFAULT);
- so->fp = NULL;
- }
- sm_free(so);
- so = NULL;
- return 0;
-}
-
-/* how to deallocate a buffer allocated by SASL */
-extern void sm_sasl_free __P((void *));
-# define SASL_DEALLOC(b) sm_sasl_free(b)
-
-/*
-** SASL_READ -- read encrypted information and decrypt it for the caller
-**
-** Parameters:
-** fp -- the file pointer
-** buf -- the location to place the decrypted information
-** size -- the number of bytes to read after decryption
-**
-** Results:
-** -1 on error
-** otherwise the number of bytes read
-*/
-
-static ssize_t sasl_read __P((SM_FILE_T *, char *, size_t));
-
-static ssize_t
-sasl_read(fp, buf, size)
- SM_FILE_T *fp;
- char *buf;
- size_t size;
-{
- int result;
- ssize_t len;
-# if SASL >= 20000
- static const char *outbuf = NULL;
-# else /* SASL >= 20000 */
- static char *outbuf = NULL;
-# endif /* SASL >= 20000 */
- static unsigned int outlen = 0;
- static unsigned int offset = 0;
- struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
-
- /*
- ** sasl_decode() may require more data than a single read() returns.
- ** Hence we have to put a loop around the decoding.
- ** This also requires that we may have to split up the returned
- ** data since it might be larger than the allowed size.
- ** Therefore we use a static pointer and return portions of it
- ** if necessary.
- ** XXX Note: This function is not thread-safe nor can it be used
- ** on more than one file. A correct implementation would store
- ** this data in fp->f_cookie.
- */
-
-# if SASL >= 20000
- while (outlen == 0)
-# else /* SASL >= 20000 */
- while (outbuf == NULL && outlen == 0)
-# endif /* SASL >= 20000 */
- {
- len = sm_io_read(so->fp, SM_TIME_DEFAULT, buf, size);
- if (len <= 0)
- return len;
- result = sasl_decode(so->conn, buf,
- (unsigned int) len, &outbuf, &outlen);
- if (result != SASL_OK)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH: sasl_decode error=%d", result);
- outbuf = NULL;
- offset = 0;
- outlen = 0;
- return -1;
- }
- }
-
- if (outbuf == NULL)
- {
- /* be paranoid: outbuf == NULL but outlen != 0 */
- syserr("@sasl_read failure: outbuf == NULL but outlen != 0");
- /* NOTREACHED */
- }
- if (outlen - offset > size)
- {
- /* return another part of the buffer */
- (void) memcpy(buf, outbuf + offset, size);
- offset += size;
- len = size;
- }
- else
- {
- /* return the rest of the buffer */
- len = outlen - offset;
- (void) memcpy(buf, outbuf + offset, (size_t) len);
-# if SASL < 20000
- SASL_DEALLOC(outbuf);
-# endif /* SASL < 20000 */
- outbuf = NULL;
- offset = 0;
- outlen = 0;
- }
- return len;
-}
-
-/*
-** SASL_WRITE -- write information out after encrypting it
-**
-** Parameters:
-** fp -- the file pointer
-** buf -- holds the data to be encrypted and written
-** size -- the number of bytes to have encrypted and written
-**
-** Returns:
-** -1 on error
-** otherwise number of bytes written
-*/
-
-static ssize_t sasl_write __P((SM_FILE_T *, const char *, size_t));
-
-static ssize_t
-sasl_write(fp, buf, size)
- SM_FILE_T *fp;
- const char *buf;
- size_t size;
-{
- int result;
-# if SASL >= 20000
- const char *outbuf;
-# else /* SASL >= 20000 */
- char *outbuf;
-# endif /* SASL >= 20000 */
- unsigned int outlen, *maxencode;
- size_t ret = 0, total = 0;
- struct sasl_obj *so = (struct sasl_obj *) fp->f_cookie;
-
- /*
- ** Fetch the maximum input buffer size for sasl_encode().
- ** This can be less than the size set in attemptauth()
- ** due to a negotation with the other side, e.g.,
- ** Cyrus IMAP lmtp program sets maxbuf=4096,
- ** digestmd5 substracts 25 and hence we'll get 4071
- ** instead of 8192 (MAXOUTLEN).
- ** Hack (for now): simply reduce the size, callers are (must be)
- ** able to deal with that and invoke sasl_write() again with
- ** the rest of the data.
- ** Note: it would be better to store this value in the context
- ** after the negotiation.
- */
-
- result = sasl_getprop(so->conn, SASL_MAXOUTBUF,
- (const void **) &maxencode);
- if (result == SASL_OK && size > *maxencode && *maxencode > 0)
- size = *maxencode;
-
- result = sasl_encode(so->conn, buf,
- (unsigned int) size, &outbuf, &outlen);
-
- if (result != SASL_OK)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH: sasl_encode error=%d", result);
- return -1;
- }
-
- if (outbuf != NULL)
- {
- while (outlen > 0)
- {
- errno = 0;
- /* XXX result == 0? */
- ret = sm_io_write(so->fp, SM_TIME_DEFAULT,
- &outbuf[total], outlen);
- if (ret <= 0)
- return ret;
- outlen -= ret;
- total += ret;
- }
-# if SASL < 20000
- SASL_DEALLOC(outbuf);
-# endif /* SASL < 20000 */
- }
- return size;
-}
-
-/*
-** SFDCSASL -- create sasl file type and open in and out file pointers
-** for sendmail to read from and write to.
-**
-** Parameters:
-** fin -- the sm_io file encrypted data to be read from
-** fout -- the sm_io file encrypted data to be written to
-** conn -- the sasl connection pointer
-** tmo -- timeout
-**
-** Returns:
-** -1 on error
-** 0 on success
-**
-** Side effects:
-** The arguments "fin" and "fout" are replaced with the new
-** SM_FILE_T pointers.
-*/
-
-int
-sfdcsasl(fin, fout, conn, tmo)
- SM_FILE_T **fin;
- SM_FILE_T **fout;
- sasl_conn_t *conn;
- int tmo;
-{
- SM_FILE_T *newin, *newout;
- SM_FILE_T SM_IO_SET_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
- sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
- SM_TIME_DEFAULT);
- struct sasl_info info;
-
- if (conn == NULL)
- {
- /* no need to do anything */
- return 0;
- }
-
- SM_IO_INIT_TYPE(sasl_vector, "sasl", sasl_open, sasl_close,
- sasl_read, sasl_write, NULL, sasl_getinfo, NULL,
- SM_TIME_DEFAULT);
- info.fp = *fin;
- info.conn = conn;
- newin = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
- SM_IO_RDONLY_B, NULL);
-
- if (newin == NULL)
- return -1;
-
- info.fp = *fout;
- info.conn = conn;
- newout = sm_io_open(&sasl_vector, SM_TIME_DEFAULT, &info,
- SM_IO_WRONLY_B, NULL);
-
- if (newout == NULL)
- {
- (void) sm_io_close(newin, SM_TIME_DEFAULT);
- return -1;
- }
- sm_io_automode(newin, newout);
-
- sm_io_setinfo(*fin, SM_IO_WHAT_TIMEOUT, &tmo);
- sm_io_setinfo(*fout, SM_IO_WHAT_TIMEOUT, &tmo);
-
- *fin = newin;
- *fout = newout;
- return 0;
-}
-#endif /* SASL */
-
-#if STARTTLS
-# include "sfsasl.h"
-# include <openssl/err.h>
-
-/* Structure used by the "tls" file type */
-struct tls_obj
-{
- SM_FILE_T *fp;
- SSL *con;
-};
-
-struct tls_info
-{
- SM_FILE_T *fp;
- SSL *con;
-};
-
-/*
-** TLS_GETINFO - returns requested information about a "tls" file
-** descriptor.
-**
-** Parameters:
-** fp -- the file descriptor
-** what -- the type of information requested
-** valp -- the thang to return the information in (unused)
-**
-** Returns:
-** -1 for unknown requests
-** >=0 on success with valp filled in (if possible).
-*/
-
-static int tls_getinfo __P((SM_FILE_T *, int, void *));
-
-/* ARGSUSED2 */
-static int
-tls_getinfo(fp, what, valp)
- SM_FILE_T *fp;
- int what;
- void *valp;
-{
- struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
-
- switch (what)
- {
- case SM_IO_WHAT_FD:
- if (so->fp == NULL)
- return -1;
- return so->fp->f_file; /* for stdio fileno() compatability */
-
- case SM_IO_IS_READABLE:
- return SSL_pending(so->con) > 0;
-
- default:
- return -1;
- }
-}
-
-/*
-** TLS_OPEN -- creates the tls specific information for opening a
-** file of the tls type.
-**
-** Parameters:
-** fp -- the file pointer associated with the new open
-** info -- the sm_io file pointer holding the open and the
-** TLS encryption connection to be read from or written to
-** flags -- ignored
-** rpool -- ignored
-**
-** Returns:
-** 0 on success
-*/
-
-static int tls_open __P((SM_FILE_T *, const void *, int, const void *));
-
-/* ARGSUSED2 */
-static int
-tls_open(fp, info, flags, rpool)
- SM_FILE_T *fp;
- const void *info;
- int flags;
- const void *rpool;
-{
- struct tls_obj *so;
- struct tls_info *ti = (struct tls_info *) info;
-
- so = (struct tls_obj *) sm_malloc(sizeof(struct tls_obj));
- if (so == NULL)
- {
- errno = ENOMEM;
- return -1;
- }
- so->fp = ti->fp;
- so->con = ti->con;
-
- /*
- ** We try to get the "raw" file descriptor that TLS uses to
- ** do the actual read/write with. This is to allow us control
- ** over the file descriptor being a blocking or non-blocking type.
- ** Under the covers TLS handles the change and this allows us
- ** to do timeouts with sm_io.
- */
-
- fp->f_file = sm_io_getinfo(so->fp, SM_IO_WHAT_FD, NULL);
- (void) sm_io_setvbuf(so->fp, SM_TIME_DEFAULT, NULL, SM_IO_NOW, 0);
- fp->f_cookie = so;
- return 0;
-}
-
-/*
-** TLS_CLOSE -- close the tls specific parts of the tls file pointer
-**
-** Parameters:
-** fp -- the file pointer to close
-**
-** Returns:
-** 0 on success
-*/
-
-static int tls_close __P((SM_FILE_T *));
-
-static int
-tls_close(fp)
- SM_FILE_T *fp;
-{
- struct tls_obj *so;
-
- so = (struct tls_obj *) fp->f_cookie;
- if (so == NULL)
- return 0;
- if (so->fp != NULL)
- {
- sm_io_close(so->fp, SM_TIME_DEFAULT);
- so->fp = NULL;
- }
- sm_free(so);
- so = NULL;
- return 0;
-}
-
-/* maximum number of retries for TLS related I/O due to handshakes */
-# define MAX_TLS_IOS 4
-
-/*
-** TLS_RETRY -- check whether a failed SSL operation can be retried
-**
-** Parameters:
-** ssl -- TLS structure
-** rfd -- read fd
-** wfd -- write fd
-** tlsstart -- start time of TLS operation
-** timeout -- timeout for TLS operation
-** err -- SSL error
-** where -- description of operation
-**
-** Results:
-** >0 on success
-** 0 on timeout
-** <0 on error
-*/
-
-int
-tls_retry(ssl, rfd, wfd, tlsstart, timeout, err, where)
- SSL *ssl;
- int rfd;
- int wfd;
- time_t tlsstart;
- int timeout;
- int err;
- const char *where;
-{
- int ret;
- time_t left;
- time_t now = curtime();
- struct timeval tv;
-
- ret = -1;
-
- /*
- ** For SSL_ERROR_WANT_{READ,WRITE}:
- ** There is not a complete SSL record available yet
- ** or there is only a partial SSL record removed from
- ** the network (socket) buffer into the SSL buffer.
- ** The SSL_connect will only succeed when a full
- ** SSL record is available (assuming a "real" error
- ** doesn't happen). To handle when a "real" error
- ** does happen the select is set for exceptions too.
- ** The connection may be re-negotiated during this time
- ** so both read and write "want errors" need to be handled.
- ** A select() exception loops back so that a proper SSL
- ** error message can be gotten.
- */
-
- left = timeout - (now - tlsstart);
- if (left <= 0)
- return 0; /* timeout */
- tv.tv_sec = left;
- tv.tv_usec = 0;
-
- if (LogLevel > 14)
- {
- sm_syslog(LOG_INFO, NOQID,
- "STARTTLS=%s, info: fds=%d/%d, err=%d",
- where, rfd, wfd, err);
- }
-
- if (FD_SETSIZE > 0 &&
- ((err == SSL_ERROR_WANT_READ && rfd >= FD_SETSIZE) ||
- (err == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE)))
- {
- if (LogLevel > 5)
- {
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS=%s, error: fd %d/%d too large",
- where, rfd, wfd);
- if (LogLevel > 8)
- tlslogerr(where);
- }
- errno = EINVAL;
- }
- else if (err == SSL_ERROR_WANT_READ)
- {
- fd_set ssl_maskr, ssl_maskx;
-
- FD_ZERO(&ssl_maskr);
- FD_SET(rfd, &ssl_maskr);
- FD_ZERO(&ssl_maskx);
- FD_SET(rfd, &ssl_maskx);
- do
- {
- ret = select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx,
- &tv);
- } while (ret < 0 && errno == EINTR);
- if (ret < 0 && errno > 0)
- ret = -errno;
- }
- else if (err == SSL_ERROR_WANT_WRITE)
- {
- fd_set ssl_maskw, ssl_maskx;
-
- FD_ZERO(&ssl_maskw);
- FD_SET(wfd, &ssl_maskw);
- FD_ZERO(&ssl_maskx);
- FD_SET(rfd, &ssl_maskx);
- do
- {
- ret = select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx,
- &tv);
- } while (ret < 0 && errno == EINTR);
- if (ret < 0 && errno > 0)
- ret = -errno;
- }
- return ret;
-}
-
-/* errno to force refill() etc to stop (see IS_IO_ERROR()) */
-#ifdef ETIMEDOUT
-# define SM_ERR_TIMEOUT ETIMEDOUT
-#else /* ETIMEDOUT */
-# define SM_ERR_TIMEOUT EIO
-#endif /* ETIMEDOUT */
-
-/*
-** TLS_READ -- read secured information for the caller
-**
-** Parameters:
-** fp -- the file pointer
-** buf -- the location to place the data
-** size -- the number of bytes to read from connection
-**
-** Results:
-** -1 on error
-** otherwise the number of bytes read
-*/
-
-static ssize_t tls_read __P((SM_FILE_T *, char *, size_t));
-
-static ssize_t
-tls_read(fp, buf, size)
- SM_FILE_T *fp;
- char *buf;
- size_t size;
-{
- int r, rfd, wfd, try, ssl_err;
- struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
- time_t tlsstart;
- char *err;
-
- try = 99;
- err = NULL;
- tlsstart = curtime();
-
- retry:
- r = SSL_read(so->con, (char *) buf, size);
-
- if (r > 0)
- return r;
-
- err = NULL;
- switch (ssl_err = SSL_get_error(so->con, r))
- {
- case SSL_ERROR_NONE:
- case SSL_ERROR_ZERO_RETURN:
- break;
- case SSL_ERROR_WANT_WRITE:
- err = "read W BLOCK";
- /* FALLTHROUGH */
- case SSL_ERROR_WANT_READ:
- if (err == NULL)
- err = "read R BLOCK";
- rfd = SSL_get_rfd(so->con);
- wfd = SSL_get_wfd(so->con);
- try = tls_retry(so->con, rfd, wfd, tlsstart,
- TimeOuts.to_datablock, ssl_err, "read");
- if (try > 0)
- goto retry;
- errno = SM_ERR_TIMEOUT;
- break;
-
- case SSL_ERROR_WANT_X509_LOOKUP:
- err = "write X BLOCK";
- break;
- case SSL_ERROR_SYSCALL:
- if (r == 0 && errno == 0) /* out of protocol EOF found */
- break;
- err = "syscall error";
-/*
- get_last_socket_error());
-*/
- break;
- case SSL_ERROR_SSL:
-#if DEAL_WITH_ERROR_SSL
- if (r == 0 && errno == 0) /* out of protocol EOF found */
- break;
-#endif /* DEAL_WITH_ERROR_SSL */
- err = "generic SSL error";
- if (LogLevel > 9)
- tlslogerr("read");
-
-#if DEAL_WITH_ERROR_SSL
- /* avoid repeated calls? */
- if (r == 0)
- r = -1;
-#endif /* DEAL_WITH_ERROR_SSL */
- break;
- }
- if (err != NULL)
- {
- int save_errno;
-
- save_errno = (errno == 0) ? EIO : errno;
- if (try == 0 && save_errno == SM_ERR_TIMEOUT)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: read error=timeout");
- }
- else if (LogLevel > 8)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: read error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
- err, r, errno,
- ERR_error_string(ERR_get_error(), NULL), try,
- ssl_err);
- else if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: read error=%s (%d), retry=%d, ssl_err=%d",
- err, r, errno, try, ssl_err);
- errno = save_errno;
- }
- return r;
-}
-
-/*
-** TLS_WRITE -- write information out through secure connection
-**
-** Parameters:
-** fp -- the file pointer
-** buf -- holds the data to be securely written
-** size -- the number of bytes to write
-**
-** Returns:
-** -1 on error
-** otherwise number of bytes written
-*/
-
-static ssize_t tls_write __P((SM_FILE_T *, const char *, size_t));
-
-static ssize_t
-tls_write(fp, buf, size)
- SM_FILE_T *fp;
- const char *buf;
- size_t size;
-{
- int r, rfd, wfd, try, ssl_err;
- struct tls_obj *so = (struct tls_obj *) fp->f_cookie;
- time_t tlsstart;
- char *err;
-
- try = 99;
- err = NULL;
- tlsstart = curtime();
-
- retry:
- r = SSL_write(so->con, (char *) buf, size);
-
- if (r > 0)
- return r;
- err = NULL;
- switch (ssl_err = SSL_get_error(so->con, r))
- {
- case SSL_ERROR_NONE:
- case SSL_ERROR_ZERO_RETURN:
- break;
- case SSL_ERROR_WANT_WRITE:
- err = "read W BLOCK";
- /* FALLTHROUGH */
- case SSL_ERROR_WANT_READ:
- if (err == NULL)
- err = "read R BLOCK";
- rfd = SSL_get_rfd(so->con);
- wfd = SSL_get_wfd(so->con);
- try = tls_retry(so->con, rfd, wfd, tlsstart,
- DATA_PROGRESS_TIMEOUT, ssl_err, "write");
- if (try > 0)
- goto retry;
- errno = SM_ERR_TIMEOUT;
- break;
- case SSL_ERROR_WANT_X509_LOOKUP:
- err = "write X BLOCK";
- break;
- case SSL_ERROR_SYSCALL:
- if (r == 0 && errno == 0) /* out of protocol EOF found */
- break;
- err = "syscall error";
-/*
- get_last_socket_error());
-*/
- break;
- case SSL_ERROR_SSL:
- err = "generic SSL error";
-/*
- ERR_GET_REASON(ERR_peek_error()));
-*/
- if (LogLevel > 9)
- tlslogerr("write");
-
-#if DEAL_WITH_ERROR_SSL
- /* avoid repeated calls? */
- if (r == 0)
- r = -1;
-#endif /* DEAL_WITH_ERROR_SSL */
- break;
- }
- if (err != NULL)
- {
- int save_errno;
-
- save_errno = (errno == 0) ? EIO : errno;
- if (try == 0 && save_errno == SM_ERR_TIMEOUT)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: write error=timeout");
- }
- else if (LogLevel > 8)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: write error=%s (%d), errno=%d, get_error=%s, retry=%d, ssl_err=%d",
- err, r, errno,
- ERR_error_string(ERR_get_error(), NULL), try,
- ssl_err);
- else if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: write error=%s (%d), errno=%d, retry=%d, ssl_err=%d",
- err, r, errno, try, ssl_err);
- errno = save_errno;
- }
- return r;
-}
-
-/*
-** SFDCTLS -- create tls file type and open in and out file pointers
-** for sendmail to read from and write to.
-**
-** Parameters:
-** fin -- data input source being replaced
-** fout -- data output source being replaced
-** con -- the tls connection pointer
-**
-** Returns:
-** -1 on error
-** 0 on success
-**
-** Side effects:
-** The arguments "fin" and "fout" are replaced with the new
-** SM_FILE_T pointers.
-** The original "fin" and "fout" are preserved in the tls file
-** type but are not actually used because of the design of TLS.
-*/
-
-int
-sfdctls(fin, fout, con)
- SM_FILE_T **fin;
- SM_FILE_T **fout;
- SSL *con;
-{
- SM_FILE_T *tlsin, *tlsout;
- SM_FILE_T SM_IO_SET_TYPE(tls_vector, "tls", tls_open, tls_close,
- tls_read, tls_write, NULL, tls_getinfo, NULL,
- SM_TIME_FOREVER);
- struct tls_info info;
-
- SM_ASSERT(con != NULL);
-
- SM_IO_INIT_TYPE(tls_vector, "tls", tls_open, tls_close,
- tls_read, tls_write, NULL, tls_getinfo, NULL,
- SM_TIME_FOREVER);
- info.fp = *fin;
- info.con = con;
- tlsin = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_RDONLY_B,
- NULL);
- if (tlsin == NULL)
- return -1;
-
- info.fp = *fout;
- tlsout = sm_io_open(&tls_vector, SM_TIME_DEFAULT, &info, SM_IO_WRONLY_B,
- NULL);
- if (tlsout == NULL)
- {
- (void) sm_io_close(tlsin, SM_TIME_DEFAULT);
- return -1;
- }
- sm_io_automode(tlsin, tlsout);
-
- *fin = tlsin;
- *fout = tlsout;
- return 0;
-}
-#endif /* STARTTLS */
diff --git a/contrib/sendmail/src/sfsasl.h b/contrib/sendmail/src/sfsasl.h
deleted file mode 100644
index a92f772..0000000
--- a/contrib/sendmail/src/sfsasl.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 1999, 2000, 2006 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.20 2006/03/27 21:31:00 ca Exp $"
- */
-
-#ifndef SFSASL_H
-# define SFSASL_H
-
-# if SASL
-extern int sfdcsasl __P((SM_FILE_T **, SM_FILE_T **, sasl_conn_t *, int));
-# endif /* SASL */
-
-# if STARTTLS
-extern int tls_retry __P((SSL *, int, int, time_t, int, int,
- const char *));
-extern int sfdctls __P((SM_FILE_T **, SM_FILE_T **, SSL *));
-# endif /* STARTTLS */
-
-#endif /* ! SFSASL_H */
diff --git a/contrib/sendmail/src/shmticklib.c b/contrib/sendmail/src/shmticklib.c
deleted file mode 100644
index 6f5e301..0000000
--- a/contrib/sendmail/src/shmticklib.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.
- *
- */
-
-#include <sm/gen.h>
-SM_RCSID("@(#)$Id: shmticklib.c,v 8.14 2001/09/11 04:05:16 gshapiro Exp $")
-
-#if _FFR_SHM_STATUS
-# 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/sm_resolve.c b/contrib/sendmail/src/sm_resolve.c
deleted file mode 100644
index 035a9e5..0000000
--- a/contrib/sendmail/src/sm_resolve.c
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * Copyright (c) 2000-2004 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-/*
- * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
- * (Royal Institute of Technology, Stockholm, Sweden).
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the Institute nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sendmail.h>
-#if DNSMAP
-# if NAMED_BIND
-# include "sm_resolve.h"
-
-SM_RCSID("$Id: sm_resolve.c,v 8.35 2007/06/25 16:20:14 ca Exp $")
-
-static struct stot
-{
- const char *st_name;
- int st_type;
-} stot[] =
-{
-# if NETINET
- { "A", T_A },
-# endif /* NETINET */
-# if NETINET6
- { "AAAA", T_AAAA },
-# endif /* NETINET6 */
- { "NS", T_NS },
- { "CNAME", T_CNAME },
- { "PTR", T_PTR },
- { "MX", T_MX },
- { "TXT", T_TXT },
- { "AFSDB", T_AFSDB },
- { "SRV", T_SRV },
- { NULL, 0 }
-};
-
-static DNS_REPLY_T *parse_dns_reply __P((unsigned char *, int));
-
-/*
-** DNS_STRING_TO_TYPE -- convert resource record name into type
-**
-** Parameters:
-** name -- name of resource record type
-**
-** Returns:
-** type if succeeded.
-** -1 otherwise.
-*/
-
-int
-dns_string_to_type(name)
- const char *name;
-{
- struct stot *p = stot;
-
- for (p = stot; p->st_name != NULL; p++)
- if (sm_strcasecmp(name, p->st_name) == 0)
- return p->st_type;
- return -1;
-}
-
-/*
-** DNS_TYPE_TO_STRING -- convert resource record type into name
-**
-** Parameters:
-** type -- resource record type
-**
-** Returns:
-** name if succeeded.
-** NULL otherwise.
-*/
-
-const char *
-dns_type_to_string(type)
- int type;
-{
- struct stot *p = stot;
-
- for (p = stot; p->st_name != NULL; p++)
- if (type == p->st_type)
- return p->st_name;
- return NULL;
-}
-
-/*
-** DNS_FREE_DATA -- free all components of a DNS_REPLY_T
-**
-** Parameters:
-** r -- pointer to DNS_REPLY_T
-**
-** Returns:
-** none.
-*/
-
-void
-dns_free_data(r)
- DNS_REPLY_T *r;
-{
- RESOURCE_RECORD_T *rr;
-
- if (r->dns_r_q.dns_q_domain != NULL)
- sm_free(r->dns_r_q.dns_q_domain);
- for (rr = r->dns_r_head; rr != NULL; )
- {
- RESOURCE_RECORD_T *tmp = rr;
-
- if (rr->rr_domain != NULL)
- sm_free(rr->rr_domain);
- if (rr->rr_u.rr_data != NULL)
- sm_free(rr->rr_u.rr_data);
- rr = rr->rr_next;
- sm_free(tmp);
- }
- sm_free(r);
-}
-
-/*
-** PARSE_DNS_REPLY -- parse DNS reply data.
-**
-** Parameters:
-** data -- pointer to dns data
-** len -- len of data
-**
-** Returns:
-** pointer to DNS_REPLY_T if succeeded.
-** NULL otherwise.
-*/
-
-static DNS_REPLY_T *
-parse_dns_reply(data, len)
- unsigned char *data;
- int len;
-{
- unsigned char *p;
- ushort ans_cnt, ui;
- int status;
- size_t l;
- char host[MAXHOSTNAMELEN];
- DNS_REPLY_T *r;
- RESOURCE_RECORD_T **rr;
-
- r = (DNS_REPLY_T *) sm_malloc(sizeof(*r));
- if (r == NULL)
- return NULL;
- memset(r, 0, sizeof(*r));
-
- p = data;
-
- /* doesn't work on Crays? */
- memcpy(&r->dns_r_h, p, sizeof(r->dns_r_h));
- p += sizeof(r->dns_r_h);
- status = dn_expand(data, data + len, p, host, sizeof(host));
- if (status < 0)
- {
- dns_free_data(r);
- return NULL;
- }
- r->dns_r_q.dns_q_domain = sm_strdup(host);
- if (r->dns_r_q.dns_q_domain == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
-
- ans_cnt = ntohs((ushort) r->dns_r_h.ancount);
-
- p += status;
- GETSHORT(r->dns_r_q.dns_q_type, p);
- GETSHORT(r->dns_r_q.dns_q_class, p);
- rr = &r->dns_r_head;
- ui = 0;
- while (p < data + len && ui < ans_cnt)
- {
- int type, class, ttl, size, txtlen;
-
- status = dn_expand(data, data + len, p, host, sizeof(host));
- if (status < 0)
- {
- dns_free_data(r);
- return NULL;
- }
- ++ui;
- p += status;
- GETSHORT(type, p);
- GETSHORT(class, p);
- GETLONG(ttl, p);
- GETSHORT(size, p);
- if (p + size > data + len)
- {
- /*
- ** announced size of data exceeds length of
- ** data paket: someone is cheating.
- */
-
- if (LogLevel > 5)
- sm_syslog(LOG_WARNING, NOQID,
- "ERROR: DNS RDLENGTH=%d > data len=%d",
- size, len - (p - data));
- dns_free_data(r);
- return NULL;
- }
- *rr = (RESOURCE_RECORD_T *) sm_malloc(sizeof(**rr));
- if (*rr == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
- memset(*rr, 0, sizeof(**rr));
- (*rr)->rr_domain = sm_strdup(host);
- if ((*rr)->rr_domain == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
- (*rr)->rr_type = type;
- (*rr)->rr_class = class;
- (*rr)->rr_ttl = ttl;
- (*rr)->rr_size = size;
- switch (type)
- {
- case T_NS:
- case T_CNAME:
- case T_PTR:
- status = dn_expand(data, data + len, p, host,
- sizeof(host));
- if (status < 0)
- {
- dns_free_data(r);
- return NULL;
- }
- (*rr)->rr_u.rr_txt = sm_strdup(host);
- if ((*rr)->rr_u.rr_txt == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
- break;
-
- case T_MX:
- case T_AFSDB:
- status = dn_expand(data, data + len, p + 2, host,
- sizeof(host));
- if (status < 0)
- {
- dns_free_data(r);
- return NULL;
- }
- l = strlen(host) + 1;
- (*rr)->rr_u.rr_mx = (MX_RECORD_T *)
- sm_malloc(sizeof(*((*rr)->rr_u.rr_mx)) + l);
- if ((*rr)->rr_u.rr_mx == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
- (*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1];
- (void) sm_strlcpy((*rr)->rr_u.rr_mx->mx_r_domain,
- host, l);
- break;
-
- case T_SRV:
- status = dn_expand(data, data + len, p + 6, host,
- sizeof(host));
- if (status < 0)
- {
- dns_free_data(r);
- return NULL;
- }
- l = strlen(host) + 1;
- (*rr)->rr_u.rr_srv = (SRV_RECORDT_T*)
- sm_malloc(sizeof(*((*rr)->rr_u.rr_srv)) + l);
- if ((*rr)->rr_u.rr_srv == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
- (*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1];
- (*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3];
- (*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5];
- (void) sm_strlcpy((*rr)->rr_u.rr_srv->srv_r_target,
- host, l);
- break;
-
- case T_TXT:
-
- /*
- ** The TXT record contains the length as
- ** leading byte, hence the value is restricted
- ** to 255, which is less than the maximum value
- ** of RDLENGTH (size). Nevertheless, txtlen
- ** must be less than size because the latter
- ** specifies the length of the entire TXT
- ** record.
- */
-
- txtlen = *p;
- if (txtlen >= size)
- {
- if (LogLevel > 5)
- sm_syslog(LOG_WARNING, NOQID,
- "ERROR: DNS TXT record size=%d <= text len=%d",
- size, txtlen);
- dns_free_data(r);
- return NULL;
- }
- (*rr)->rr_u.rr_txt = (char *) sm_malloc(txtlen + 1);
- if ((*rr)->rr_u.rr_txt == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
- (void) sm_strlcpy((*rr)->rr_u.rr_txt, (char*) p + 1,
- txtlen + 1);
- break;
-
- default:
- (*rr)->rr_u.rr_data = (unsigned char*) sm_malloc(size);
- if ((*rr)->rr_u.rr_data == NULL)
- {
- dns_free_data(r);
- return NULL;
- }
- (void) memcpy((*rr)->rr_u.rr_data, p, size);
- break;
- }
- p += size;
- rr = &(*rr)->rr_next;
- }
- *rr = NULL;
- return r;
-}
-
-/*
-** DNS_LOOKUP_INT -- perform dns map lookup (internal helper routine)
-**
-** Parameters:
-** domain -- name to lookup
-** rr_class -- resource record class
-** rr_type -- resource record type
-** retrans -- retransmission timeout
-** retry -- number of retries
-**
-** Returns:
-** result of lookup if succeeded.
-** NULL otherwise.
-*/
-
-DNS_REPLY_T *
-dns_lookup_int(domain, rr_class, rr_type, retrans, retry)
- const char *domain;
- int rr_class;
- int rr_type;
- time_t retrans;
- int retry;
-{
- int len;
- unsigned long old_options = 0;
- time_t save_retrans = 0;
- int save_retry = 0;
- DNS_REPLY_T *r = NULL;
- unsigned char reply[1024];
-
- if (tTd(8, 16))
- {
- old_options = _res.options;
- _res.options |= RES_DEBUG;
- sm_dprintf("dns_lookup(%s, %d, %s)\n", domain,
- rr_class, dns_type_to_string(rr_type));
- }
- if (retrans > 0)
- {
- save_retrans = _res.retrans;
- _res.retrans = retrans;
- }
- if (retry > 0)
- {
- save_retry = _res.retry;
- _res.retry = retry;
- }
- errno = 0;
- SM_SET_H_ERRNO(0);
- len = res_search(domain, rr_class, rr_type, reply, sizeof(reply));
- if (tTd(8, 16))
- {
- _res.options = old_options;
- sm_dprintf("dns_lookup(%s, %d, %s) --> %d\n",
- domain, rr_class, dns_type_to_string(rr_type), len);
- }
- if (len >= 0)
- r = parse_dns_reply(reply, len);
- if (retrans > 0)
- _res.retrans = save_retrans;
- if (retry > 0)
- _res.retry = save_retry;
- return r;
-}
-
-# if 0
-DNS_REPLY_T *
-dns_lookup(domain, type_name, retrans, retry)
- const char *domain;
- const char *type_name;
- time_t retrans;
- int retry;
-{
- int type;
-
- type = dns_string_to_type(type_name);
- if (type == -1)
- {
- if (tTd(8, 16))
- sm_dprintf("dns_lookup: unknown resource type: `%s'\n",
- type_name);
- return NULL;
- }
- return dns_lookup_int(domain, C_IN, type, retrans, retry);
-}
-# endif /* 0 */
-# endif /* NAMED_BIND */
-#endif /* DNSMAP */
diff --git a/contrib/sendmail/src/sm_resolve.h b/contrib/sendmail/src/sm_resolve.h
deleted file mode 100644
index 7f169ba..0000000
--- a/contrib/sendmail/src/sm_resolve.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-/*
- * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
- * (Royal Institute of Technology, Stockholm, Sweden).
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the Institute nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/* $Id: sm_resolve.h,v 8.8 2001/09/01 00:06:02 gshapiro Exp $ */
-
-#if DNSMAP
-# ifndef __ROKEN_RESOLVE_H__
-# define __ROKEN_RESOLVE_H__
-
-/* We use these, but they are not always present in <arpa/nameser.h> */
-
-# ifndef T_TXT
-# define T_TXT 16
-# endif /* ! T_TXT */
-# ifndef T_AFSDB
-# define T_AFSDB 18
-# endif /* ! T_AFSDB */
-# ifndef T_SRV
-# define T_SRV 33
-# endif /* ! T_SRV */
-# ifndef T_NAPTR
-# define T_NAPTR 35
-# endif /* ! T_NAPTR */
-
-typedef struct
-{
- char *dns_q_domain;
- unsigned int dns_q_type;
- unsigned int dns_q_class;
-} DNS_QUERY_T;
-
-typedef struct
-{
- unsigned int mx_r_preference;
- char mx_r_domain[1];
-} MX_RECORD_T;
-
-typedef struct
-{
- unsigned int srv_r_priority;
- unsigned int srv_r_weight;
- unsigned int srv_r_port;
- char srv_r_target[1];
-} SRV_RECORDT_T;
-
-
-typedef struct resource_record RESOURCE_RECORD_T;
-
-struct resource_record
-{
- char *rr_domain;
- unsigned int rr_type;
- unsigned int rr_class;
- unsigned int rr_ttl;
- unsigned int rr_size;
- union
- {
- void *rr_data;
- MX_RECORD_T *rr_mx;
- MX_RECORD_T *rr_afsdb; /* mx and afsdb are identical */
- SRV_RECORDT_T *rr_srv;
-# if NETINET
- struct in_addr *rr_a;
-# endif /* NETINET */
-# if NETINET6
- struct in6_addr *rr_aaaa;
-# endif /* NETINET6 */
- char *rr_txt;
- } rr_u;
- RESOURCE_RECORD_T *rr_next;
-};
-
-# if !defined(T_A) && !defined(T_AAAA)
-/* XXX if <arpa/nameser.h> isn't included */
-typedef int HEADER; /* will never be used */
-# endif /* !defined(T_A) && !defined(T_AAAA) */
-
-typedef struct
-{
- HEADER dns_r_h;
- DNS_QUERY_T dns_r_q;
- RESOURCE_RECORD_T *dns_r_head;
-} DNS_REPLY_T;
-
-
-extern void dns_free_data __P((DNS_REPLY_T *));
-extern int dns_string_to_type __P((const char *));
-extern const char *dns_type_to_string __P((int));
-extern DNS_REPLY_T *dns_lookup_int __P((const char *,
- int,
- int,
- time_t,
- int));
-# if 0
-extern DNS_REPLY_T *dns_lookup __P((const char *domain,
- const char *type_name,
- time_t retrans,
- int retry));
-# endif /* 0 */
-
-# endif /* ! __ROKEN_RESOLVE_H__ */
-#endif /* DNSMAP */
diff --git a/contrib/sendmail/src/srvrsmtp.c b/contrib/sendmail/src/srvrsmtp.c
deleted file mode 100644
index 514a5e6..0000000
--- a/contrib/sendmail/src/srvrsmtp.c
+++ /dev/null
@@ -1,4974 +0,0 @@
-/*
- * Copyright (c) 1998-2007 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.
- *
- */
-
-#include <sendmail.h>
-#if MILTER
-# include <libmilter/mfapi.h>
-# include <libmilter/mfdef.h>
-#endif /* MILTER */
-
-SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.967 2007/10/01 16:22:14 ca Exp $")
-
-#include <sm/time.h>
-#include <sm/fdset.h>
-
-#if SASL || STARTTLS
-# include "sfsasl.h"
-#endif /* SASL || STARTTLS */
-#if SASL
-# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1)
-static int saslmechs __P((sasl_conn_t *, char **));
-#endif /* SASL */
-#if STARTTLS
-# include <sysexits.h>
-
-static SSL_CTX *srv_ctx = NULL; /* TLS server context */
-static SSL *srv_ssl = NULL; /* per connection context */
-
-static bool tls_ok_srv = false;
-
-# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
- bitset(SRV_VRFY_CLT, features))
-#endif /* STARTTLS */
-
-#if _FFR_DM_ONE
-static bool NotFirstDelivery = false;
-#endif /* _FFR_DM_ONE */
-
-/* server features */
-#define SRV_NONE 0x0000 /* none... */
-#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */
-#define SRV_VRFY_CLT 0x0002 /* request a cert */
-#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */
-#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */
-#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */
-#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */
-#define SRV_OFFER_VERB 0x0040 /* offer VERB */
-#define SRV_OFFER_DSN 0x0080 /* offer DSN */
-#if PIPELINING
-# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */
-# if _FFR_NO_PIPE
-# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
-#define SRV_REQ_AUTH 0x0400 /* require AUTH */
-#define SRV_REQ_SEC 0x0800 /* require security - equiv to AuthOptions=p */
-#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */
-
-static unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int));
-
-#define STOP_ATTACK ((time_t) -1)
-static time_t checksmtpattack __P((volatile unsigned int *, unsigned int,
- bool, char *, ENVELOPE *));
-static void printvrfyaddr __P((ADDRESS *, bool, bool));
-static char *skipword __P((char *volatile, char *));
-static void setup_smtpd_io __P((void));
-
-#if SASL
-# if SASL >= 20000
-static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
- char *_remoteip, char *_localip,
- char *_auth_id, sasl_ssf_t *_ext_ssf));
-
-# define RESET_SASLCONN \
- do \
- { \
- result = reset_saslconn(&conn, AuthRealm, remoteip, \
- localip, auth_id, &ext_ssf); \
- if (result != SASL_OK) \
- sasl_ok = false; \
- } while (0)
-
-# else /* SASL >= 20000 */
-static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
- struct sockaddr_in *_saddr_r,
- struct sockaddr_in *_saddr_l,
- sasl_external_properties_t *_ext_ssf));
-# define RESET_SASLCONN \
- do \
- { \
- result = reset_saslconn(&conn, AuthRealm, &saddr_r, \
- &saddr_l, &ext_ssf); \
- if (result != SASL_OK) \
- sasl_ok = false; \
- } while (0)
-
-# endif /* SASL >= 20000 */
-#endif /* SASL */
-
-extern ENVELOPE BlankEnvelope;
-
-#define NBADRCPTS \
- do \
- { \
- char buf[16]; \
- (void) sm_snprintf(buf, sizeof(buf), "%d", \
- BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
- ? n_badrcpts - 1 : n_badrcpts); \
- macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
- } while (0)
-
-#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \
- (s)++
-
-/*
-** PARSE_ESMTP_ARGS -- parse EMSTP arguments (for MAIL, RCPT)
-**
-** Parameters:
-** e -- the envelope
-** addr_st -- address (RCPT only)
-** p -- read buffer
-** delimptr -- current position in read buffer
-** which -- MAIL/RCPT
-** args -- arguments (output)
-** esmtp_args -- function to process a single ESMTP argument
-**
-** Returns:
-** none
-*/
-
-void
-parse_esmtp_args(e, addr_st, p, delimptr, which, args, esmtp_args)
- ENVELOPE *e;
- ADDRESS *addr_st;
- char *p;
- char *delimptr;
- char *which;
- char *args[];
- esmtp_args_F esmtp_args;
-{
- int argno;
-
- argno = 0;
- if (args != NULL)
- args[argno++] = p;
- p = delimptr;
- while (p != NULL && *p != '\0')
- {
- char *kp;
- char *vp = NULL;
- char *equal = NULL;
-
- /* locate the beginning of the keyword */
- SKIP_SPACE(p);
- if (*p == '\0')
- break;
- kp = p;
-
- /* skip to the value portion */
- while ((isascii(*p) && isalnum(*p)) || *p == '-')
- p++;
- if (*p == '=')
- {
- equal = p;
- *p++ = '\0';
- vp = p;
-
- /* skip to the end of the value */
- while (*p != '\0' && *p != ' ' &&
- !(isascii(*p) && iscntrl(*p)) &&
- *p != '=')
- p++;
- }
-
- if (*p != '\0')
- *p++ = '\0';
-
- if (tTd(19, 1))
- sm_dprintf("%s: got arg %s=\"%s\"\n", which, kp,
- vp == NULL ? "<null>" : vp);
-
- esmtp_args(addr_st, kp, vp, e);
- if (equal != NULL)
- *equal = '=';
- if (args != NULL)
- args[argno] = kp;
- argno++;
- if (argno >= MAXSMTPARGS - 1)
- usrerr("501 5.5.4 Too many parameters");
- if (Errors > 0)
- break;
- }
- if (args != NULL)
- args[argno] = NULL;
-}
-
-/*
-** SMTP -- run the SMTP protocol.
-**
-** Parameters:
-** nullserver -- if non-NULL, rejection message for
-** (almost) all SMTP commands.
-** d_flags -- daemon flags
-** e -- the envelope.
-**
-** Returns:
-** never.
-**
-** Side Effects:
-** Reads commands from the input channel and processes them.
-*/
-
-/*
-** Notice: The smtp server doesn't have a session context like the client
-** side has (mci). Therefore some data (session oriented) is allocated
-** or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
-** This should be fixed in a successor version.
-*/
-
-struct cmd
-{
- char *cmd_name; /* command name */
- int cmd_code; /* internal code, see below */
-};
-
-/* values for cmd_code */
-#define CMDERROR 0 /* bad command */
-#define CMDMAIL 1 /* mail -- designate sender */
-#define CMDRCPT 2 /* rcpt -- designate recipient */
-#define CMDDATA 3 /* data -- send message text */
-#define CMDRSET 4 /* rset -- reset state */
-#define CMDVRFY 5 /* vrfy -- verify address */
-#define CMDEXPN 6 /* expn -- expand address */
-#define CMDNOOP 7 /* noop -- do nothing */
-#define CMDQUIT 8 /* quit -- close connection and die */
-#define CMDHELO 9 /* helo -- be polite */
-#define CMDHELP 10 /* help -- give usage info */
-#define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
-#define CMDETRN 12 /* etrn -- flush queue */
-#if SASL
-# define CMDAUTH 13 /* auth -- SASL authenticate */
-#endif /* SASL */
-#if STARTTLS
-# define CMDSTLS 14 /* STARTTLS -- start TLS session */
-#endif /* STARTTLS */
-/* non-standard commands */
-#define CMDVERB 17 /* verb -- go into verbose mode */
-/* 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 */
-#define CMDDBGQSHOW 24 /* showq -- show send queue */
-#define CMDDBGDEBUG 25 /* debug -- set debug mode */
-
-/*
-** Note: If you change this list, remember to update 'helpfile'
-*/
-
-static struct cmd CmdTab[] =
-{
- { "mail", CMDMAIL },
- { "rcpt", CMDRCPT },
- { "data", CMDDATA },
- { "rset", CMDRSET },
- { "vrfy", CMDVRFY },
- { "expn", CMDEXPN },
- { "help", CMDHELP },
- { "noop", CMDNOOP },
- { "quit", CMDQUIT },
- { "helo", CMDHELO },
- { "ehlo", CMDEHLO },
- { "etrn", CMDETRN },
- { "verb", CMDVERB },
- { "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 },
- { "wiz", CMDLOGBOGUS },
-
- { NULL, CMDERROR }
-};
-
-static char *CurSmtpClient; /* who's at the other end of channel */
-
-#ifndef MAXBADCOMMANDS
-# define MAXBADCOMMANDS 25 /* maximum number of bad commands */
-#endif /* ! MAXBADCOMMANDS */
-#ifndef MAXHELOCOMMANDS
-# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
-#endif /* ! MAXHELOCOMMANDS */
-#ifndef MAXVRFYCOMMANDS
-# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
-#endif /* ! MAXVRFYCOMMANDS */
-#ifndef MAXETRNCOMMANDS
-# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
-#endif /* ! MAXETRNCOMMANDS */
-#ifndef MAXTIMEOUT
-# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */
-#endif /* ! MAXTIMEOUT */
-
-/*
-** Maximum shift value to compute timeout for bad commands.
-** This introduces an upper limit of 2^MAXSHIFT for the timeout.
-*/
-
-#ifndef MAXSHIFT
-# define MAXSHIFT 8
-#endif /* ! MAXSHIFT */
-#if MAXSHIFT > 31
- ERROR _MAXSHIFT > 31 is invalid
-#endif /* MAXSHIFT */
-
-
-#if MAXBADCOMMANDS > 0
-# define STOP_IF_ATTACK(r) do \
- { \
- if ((r) == STOP_ATTACK) \
- goto stopattack; \
- } while (0)
-
-#else /* MAXBADCOMMANDS > 0 */
-# define STOP_IF_ATTACK(r) r
-#endif /* MAXBADCOMMANDS > 0 */
-
-
-#if SM_HEAP_CHECK
-static SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
- "@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
-#endif /* SM_HEAP_CHECK */
-
-typedef struct
-{
- bool sm_gotmail; /* mail command received */
- unsigned int sm_nrcpts; /* number of successful RCPT commands */
- bool sm_discard;
-#if MILTER
- bool sm_milterize;
- bool sm_milterlist; /* any filters in the list? */
- milters_T sm_milters;
-
- /* e_nrcpts from envelope before recipient() call */
- unsigned int sm_e_nrcpts_orig;
-#endif /* MILTER */
- char *sm_quarmsg; /* carry quarantining across messages */
-} SMTP_T;
-
-static bool smtp_data __P((SMTP_T *, ENVELOPE *));
-
-#define MSG_TEMPFAIL "451 4.3.2 Please try again later"
-
-#if MILTER
-# define MILTER_ABORT(e) milter_abort((e))
-
-# define MILTER_REPLY(str) \
- { \
- int savelogusrerrs = LogUsrErrs; \
- \
- milter_cmd_fail = true; \
- switch (state) \
- { \
- case SMFIR_SHUTDOWN: \
- if (MilterLogLevel > 3) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, reject=421, errormode=4", \
- str, addr); \
- LogUsrErrs = false; \
- } \
- { \
- bool tsave = QuickAbort; \
- \
- QuickAbort = false; \
- usrerr("421 4.3.0 closing connection"); \
- QuickAbort = tsave; \
- e->e_sendqueue = NULL; \
- goto doquit; \
- } \
- break; \
- case SMFIR_REPLYCODE: \
- if (MilterLogLevel > 3) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, reject=%s", \
- str, addr, response); \
- LogUsrErrs = false; \
- } \
- if (strncmp(response, "421 ", 4) == 0 \
- || strncmp(response, "421-", 4) == 0) \
- { \
- bool tsave = QuickAbort; \
- \
- QuickAbort = false; \
- usrerr(response); \
- QuickAbort = tsave; \
- e->e_sendqueue = NULL; \
- goto doquit; \
- } \
- else \
- usrerr(response); \
- break; \
- \
- case SMFIR_REJECT: \
- if (MilterLogLevel > 3) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
- str, addr); \
- LogUsrErrs = false; \
- } \
- usrerr("550 5.7.1 Command rejected"); \
- break; \
- \
- case SMFIR_DISCARD: \
- if (MilterLogLevel > 3) \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, discard", \
- str, addr); \
- e->e_flags |= EF_DISCARD; \
- milter_cmd_fail = false; \
- break; \
- \
- case SMFIR_TEMPFAIL: \
- if (MilterLogLevel > 3) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, reject=%s", \
- str, addr, MSG_TEMPFAIL); \
- LogUsrErrs = false; \
- } \
- usrerr(MSG_TEMPFAIL); \
- break; \
- default: \
- milter_cmd_fail = false; \
- break; \
- } \
- LogUsrErrs = savelogusrerrs; \
- if (response != NULL) \
- sm_free(response); /* XXX */ \
- }
-
-#else /* MILTER */
-# define MILTER_ABORT(e)
-#endif /* MILTER */
-
-/* clear all SMTP state (for HELO/EHLO/RSET) */
-#define CLEAR_STATE(cmd) \
-do \
-{ \
- /* abort milter filters */ \
- MILTER_ABORT(e); \
- \
- if (smtp.sm_nrcpts > 0) \
- { \
- logundelrcpts(e, cmd, 10, false); \
- smtp.sm_nrcpts = 0; \
- macdefine(&e->e_macro, A_PERM, \
- macid("{nrcpts}"), "0"); \
- } \
- \
- e->e_sendqueue = NULL; \
- e->e_flags |= EF_CLRQUEUE; \
- \
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) \
- logsender(e, NULL); \
- e->e_flags &= ~EF_LOGSENDER; \
- \
- /* clean up a bit */ \
- smtp.sm_gotmail = false; \
- SuprErrs = true; \
- dropenvelope(e, true, false); \
- sm_rpool_free(e->e_rpool); \
- e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \
- CurEnv = e; \
- e->e_features = features; \
- \
- /* put back discard bit */ \
- if (smtp.sm_discard) \
- e->e_flags |= EF_DISCARD; \
- \
- /* restore connection quarantining */ \
- if (smtp.sm_quarmsg == NULL) \
- { \
- e->e_quarmsg = NULL; \
- macdefine(&e->e_macro, A_PERM, \
- macid("{quarantine}"), ""); \
- } \
- else \
- { \
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
- smtp.sm_quarmsg); \
- macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
- e->e_quarmsg); \
- } \
-} while (0)
-
-/* sleep to flatten out connection load */
-#define MIN_DELAY_LOG 15 /* wait before logging this again */
-
-/* is it worth setting the process title for 1s? */
-#define DELAY_CONN(cmd) \
- if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA) \
- { \
- time_t dnow; \
- \
- sm_setproctitle(true, e, \
- "%s: %s: delaying %s: load average: %d", \
- qid_printname(e), CurSmtpClient, \
- cmd, DelayLA); \
- if (LogLevel > 8 && (dnow = curtime()) > log_delay) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "delaying=%s, load average=%d >= %d", \
- cmd, CurrentLA, DelayLA); \
- log_delay = dnow + MIN_DELAY_LOG; \
- } \
- (void) sleep(1); \
- sm_setproctitle(true, e, "%s %s: %.80s", \
- qid_printname(e), CurSmtpClient, inp); \
- }
-
-static bool SevenBitInput_Saved; /* saved version of SevenBitInput */
-
-void
-smtp(nullserver, d_flags, e)
- char *volatile nullserver;
- BITMAP256 d_flags;
- register ENVELOPE *volatile e;
-{
- register char *volatile p;
- register struct cmd *volatile c = NULL;
- char *cmd;
- auto ADDRESS *vrfyqueue;
- ADDRESS *a;
- volatile bool gothello; /* helo command received */
- bool vrfy; /* set if this is a vrfy command */
- char *volatile protocol; /* sending protocol */
- char *volatile sendinghost; /* sending hostname */
- char *volatile peerhostname; /* name of SMTP peer or "localhost" */
- auto char *delimptr;
- char *id;
- volatile unsigned int n_badcmds = 0; /* count of bad commands */
- volatile unsigned int n_badrcpts = 0; /* number of rejected RCPT */
- volatile unsigned int n_verifies = 0; /* count of VRFY/EXPN */
- volatile unsigned int n_etrn = 0; /* count of ETRN */
- volatile unsigned int n_noop = 0; /* count of NOOP/VERB/etc */
- volatile unsigned int n_helo = 0; /* count of HELO/EHLO */
- bool ok;
- volatile bool first;
- volatile bool tempfail = false;
- volatile time_t wt; /* timeout after too many commands */
- volatile time_t previous; /* time after checksmtpattack() */
- volatile bool lognullconnection = true;
- register char *q;
- SMTP_T smtp;
- char *addr;
- char *greetcode = "220";
- char *hostname; /* my hostname ($j) */
- QUEUE_CHAR *new;
- char *args[MAXSMTPARGS];
- char inp[MAXINPLINE];
-#if MAXINPLINE < MAXLINE
- ERROR _MAXINPLINE must NOT be less than _MAXLINE: MAXINPLINE < MAXLINE
-#endif /* MAXINPLINE < MAXLINE */
- char cmdbuf[MAXLINE];
-#if SASL
- sasl_conn_t *conn;
- volatile bool sasl_ok;
- volatile unsigned int n_auth = 0; /* count of AUTH commands */
- bool ismore;
- int result;
- volatile int authenticating;
- char *user;
- char *in, *out2;
-# if SASL >= 20000
- char *auth_id = NULL;
- const char *out;
- sasl_ssf_t ext_ssf;
- char localip[60], remoteip[60];
-# else /* SASL >= 20000 */
- char *out;
- const char *errstr;
- sasl_external_properties_t ext_ssf;
- struct sockaddr_in saddr_l;
- struct sockaddr_in saddr_r;
-# endif /* SASL >= 20000 */
- sasl_security_properties_t ssp;
- sasl_ssf_t *ssf;
- unsigned int inlen, out2len;
- unsigned int outlen;
- char *volatile auth_type;
- char *mechlist;
- volatile unsigned int n_mechs;
- unsigned int len;
-#else /* SASL */
-#endif /* SASL */
- int r;
-#if STARTTLS
- int rfd, wfd;
- volatile bool tls_active = false;
- volatile bool smtps = bitnset(D_SMTPS, d_flags);
- bool saveQuickAbort;
- bool saveSuprErrs;
- time_t tlsstart;
-#endif /* STARTTLS */
- volatile unsigned int features;
-#if PIPELINING
-# if _FFR_NO_PIPE
- int np_log = 0;
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
- volatile time_t log_delay = (time_t) 0;
-#if MILTER
- volatile bool milter_cmd_done, milter_cmd_safe;
- volatile bool milter_rcpt_added, milter_cmd_fail;
- ADDRESS addr_st;
-# define p_addr_st &addr_st
-#else /* MILTER */
-# define p_addr_st NULL
-#endif /* MILTER */
- size_t inplen;
-
- SevenBitInput_Saved = SevenBitInput;
- smtp.sm_nrcpts = 0;
-#if MILTER
- smtp.sm_milterize = (nullserver == NULL);
- smtp.sm_milterlist = false;
- addr = NULL;
-#endif /* MILTER */
-
- /* setup I/O fd correctly for the SMTP server */
- setup_smtpd_io();
-
-#if SM_HEAP_CHECK
- if (sm_debug_active(&DebugLeakSmtp, 1))
- {
- sm_heap_newgroup();
- sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
- }
-#endif /* SM_HEAP_CHECK */
-
- /* XXX the rpool should be set when e is initialized in main() */
- e->e_rpool = sm_rpool_new_x(NULL);
- e->e_macro.mac_rpool = e->e_rpool;
-
- settime(e);
- sm_getla();
- peerhostname = RealHostName;
- if (peerhostname == NULL)
- peerhostname = "localhost";
- CurHostName = peerhostname;
- CurSmtpClient = macvalue('_', e);
- if (CurSmtpClient == NULL)
- CurSmtpClient = CurHostName;
-
- /* check_relay may have set discard bit, save for later */
- smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
-
-#if PIPELINING
- /* auto-flush output when reading input */
- (void) sm_io_autoflush(InChannel, OutChannel);
-#endif /* PIPELINING */
-
- sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
-
- /* Set default features for server. */
- features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
- bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
- | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
- | (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
- : (SRV_OFFER_EXPN
- | (bitset(PRIV_NOVERB, PrivacyFlags)
- ? SRV_NONE : SRV_OFFER_VERB)))
- | ((bitset(PRIV_NORECEIPTS, PrivacyFlags) || !SendMIMEErrors)
- ? SRV_NONE : SRV_OFFER_DSN)
-#if SASL
- | (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
- | (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
- : SRV_NONE)
-#endif /* SASL */
-#if PIPELINING
- | SRV_OFFER_PIPE
-#endif /* PIPELINING */
-#if STARTTLS
- | (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
- | (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
- : SRV_VRFY_CLT)
-#endif /* STARTTLS */
- ;
- if (nullserver == NULL)
- {
- features = srvfeatures(e, CurSmtpClient, features);
- if (bitset(SRV_TMP_FAIL, features))
- {
- if (LogLevel > 4)
- sm_syslog(LOG_ERR, NOQID,
- "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
- CurSmtpClient);
- nullserver = "450 4.3.0 Please try again later.";
- }
- else
- {
-#if PIPELINING
-# if _FFR_NO_PIPE
- if (bitset(SRV_NO_PIPE, features))
- {
- /* for consistency */
- features &= ~SRV_OFFER_PIPE;
- }
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
-#if SASL
- if (bitset(SRV_REQ_SEC, features))
- SASLOpts |= SASL_SEC_NOPLAINTEXT;
- else
- SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
-#endif /* SASL */
- }
- }
- else if (strncmp(nullserver, "421 ", 4) == 0)
- {
- message(nullserver);
- goto doquit;
- }
-
- e->e_features = features;
- hostname = macvalue('j', e);
-#if SASL
- if (AuthRealm == NULL)
- AuthRealm = hostname;
- sasl_ok = bitset(SRV_OFFER_AUTH, features);
- n_mechs = 0;
- authenticating = SASL_NOT_AUTH;
-
- /* SASL server new connection */
- if (sasl_ok)
- {
-# if SASL >= 20000
- result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
- NULL, 0, &conn);
-# elif SASL > 10505
- /* use empty realm: only works in SASL > 1.5.5 */
- result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
-# else /* SASL >= 20000 */
- /* use no realm -> realm is set to hostname by SASL lib */
- result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
- &conn);
-# endif /* SASL >= 20000 */
- sasl_ok = result == SASL_OK;
- if (!sasl_ok)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH error: sasl_server_new failed=%d",
- result);
- }
- }
- if (sasl_ok)
- {
- /*
- ** SASL set properties for sasl
- ** set local/remote IP
- ** XXX Cyrus SASL v1 only supports IPv4
- **
- ** XXX where exactly are these used/required?
- ** Kerberos_v4
- */
-
-# if SASL >= 20000
- localip[0] = remoteip[0] = '\0';
-# if NETINET || NETINET6
- in = macvalue(macid("{daemon_family}"), e);
- if (in != NULL && (
-# if NETINET6
- strcmp(in, "inet6") == 0 ||
-# endif /* NETINET6 */
- strcmp(in, "inet") == 0))
- {
- SOCKADDR_LEN_T addrsize;
- SOCKADDR saddr_l;
- SOCKADDR saddr_r;
-
- addrsize = sizeof(saddr_r);
- if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *) &saddr_r,
- &addrsize) == 0)
- {
- if (iptostring(&saddr_r, addrsize,
- remoteip, sizeof(remoteip)))
- {
- sasl_setprop(conn, SASL_IPREMOTEPORT,
- remoteip);
- }
- addrsize = sizeof(saddr_l);
- if (getsockname(sm_io_getinfo(InChannel,
- SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *) &saddr_l,
- &addrsize) == 0)
- {
- if (iptostring(&saddr_l, addrsize,
- localip,
- sizeof(localip)))
- {
- sasl_setprop(conn,
- SASL_IPLOCALPORT,
- localip);
- }
- }
- }
- }
-# endif /* NETINET || NETINET6 */
-# else /* SASL >= 20000 */
-# if NETINET
- in = macvalue(macid("{daemon_family}"), e);
- if (in != NULL && strcmp(in, "inet") == 0)
- {
- SOCKADDR_LEN_T addrsize;
-
- addrsize = sizeof(struct sockaddr_in);
- if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *)&saddr_r,
- &addrsize) == 0)
- {
- sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
- addrsize = sizeof(struct sockaddr_in);
- if (getsockname(sm_io_getinfo(InChannel,
- SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *)&saddr_l,
- &addrsize) == 0)
- sasl_setprop(conn, SASL_IP_LOCAL,
- &saddr_l);
- }
- }
-# endif /* NETINET */
-# endif /* SASL >= 20000 */
-
- auth_type = NULL;
- mechlist = NULL;
- user = NULL;
-# if 0
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{auth_author}"), NULL);
-# endif /* 0 */
-
- /* set properties */
- (void) memset(&ssp, '\0', sizeof(ssp));
-
- /* XXX should these be options settable via .cf ? */
- /* ssp.min_ssf = 0; is default due to memset() */
- {
- ssp.max_ssf = MaxSLBits;
- ssp.maxbufsize = MAXOUTLEN;
- }
- ssp.security_flags = SASLOpts & SASL_SEC_MASK;
- sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
-
- if (sasl_ok)
- {
- /*
- ** external security strength factor;
- ** currently we have none so zero
- */
-
-# if SASL >= 20000
- ext_ssf = 0;
- auth_id = NULL;
- sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK) &&
- (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
- auth_id) == SASL_OK));
-# else /* SASL >= 20000 */
- ext_ssf.ssf = 0;
- ext_ssf.auth_id = NULL;
- sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK;
-# endif /* SASL >= 20000 */
- }
- if (sasl_ok)
- n_mechs = saslmechs(conn, &mechlist);
- }
-#endif /* SASL */
-
-#if STARTTLS
-#endif /* STARTTLS */
-
-#if MILTER
- if (smtp.sm_milterize)
- {
- char state;
-
- /* initialize mail filter connection */
- smtp.sm_milterlist = milter_init(e, &state, &smtp.sm_milters);
- switch (state)
- {
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: initialization failed, rejecting commands");
- greetcode = "554";
- nullserver = "Command rejected";
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: initialization failed, temp failing commands");
- tempfail = true;
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_SHUTDOWN:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: initialization failed, closing connection");
- tempfail = true;
- smtp.sm_milterize = false;
- message("421 4.7.0 %s closing connection",
- MyHostName);
-
- /* arrange to ignore send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
- }
-
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- q = macvalue(macid("{client_name}"), e);
- SM_ASSERT(q != NULL || OpMode == MD_SMTP);
- if (q == NULL)
- q = "localhost";
- response = milter_connect(q, RealHostAddr, e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: connect: host=%s, addr=%s, rejecting commands",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- greetcode = "554";
- nullserver = "Command rejected";
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: connect: host=%s, addr=%s, temp failing commands",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- tempfail = true;
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_SHUTDOWN:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: connect: host=%s, addr=%s, shutdown",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- tempfail = true;
- smtp.sm_milterize = false;
- message("421 4.7.0 %s closing connection",
- MyHostName);
-
- /* arrange to ignore send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
- if (response != NULL)
- sm_free(response); /* XXX */
- }
-#endif /* MILTER */
-
- /*
- ** Broken proxies and SMTP slammers
- ** push data without waiting, catch them
- */
-
- if (
-#if STARTTLS
- !smtps &&
-#endif /* STARTTLS */
- *greetcode == '2' && nullserver == NULL)
- {
- time_t msecs = 0;
- char **pvp;
- char pvpbuf[PSBUFSIZE];
-
- /* Ask the rulesets how long to pause */
- pvp = NULL;
- r = rscap("greet_pause", peerhostname,
- anynet_ntoa(&RealHostAddr), e,
- &pvp, pvpbuf, sizeof(pvpbuf));
- if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
- (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
- {
- msecs = strtol(pvp[1], NULL, 10);
- }
-
- if (msecs > 0)
- {
- int fd;
- fd_set readfds;
- struct timeval timeout;
- struct timeval bp, ep, tp; /* {begin,end,total}pause */
- int eoftest;
-
- /* pause for a moment */
- timeout.tv_sec = msecs / 1000;
- timeout.tv_usec = (msecs % 1000) * 1000;
-
- /* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
- if (timeout.tv_sec >= 300)
- {
- timeout.tv_sec = 300;
- timeout.tv_usec = 0;
- }
-
- /* check if data is on the socket during the pause */
- fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
- FD_ZERO(&readfds);
- SM_FD_SET(fd, &readfds);
- gettimeofday(&bp, NULL);
- if (select(fd + 1, FDSET_CAST &readfds,
- NULL, NULL, &timeout) > 0 &&
- FD_ISSET(fd, &readfds) &&
- (eoftest = sm_io_getc(InChannel, SM_TIME_DEFAULT))
- != SM_IO_EOF)
- {
- sm_io_ungetc(InChannel, SM_TIME_DEFAULT,
- eoftest);
- gettimeofday(&ep, NULL);
- timersub(&ep, &bp, &tp);
- greetcode = "554";
- nullserver = "Command rejected";
- sm_syslog(LOG_INFO, e->e_id,
- "rejecting commands from %s [%s] due to pre-greeting traffic after %d seconds",
- peerhostname,
- anynet_ntoa(&RealHostAddr),
- (int) tp.tv_sec +
- (tp.tv_usec >= 500000 ? 1 : 0)
- );
- }
- }
- }
-
-#if STARTTLS
- /* If this an smtps connection, start TLS now */
- if (smtps)
- {
- Errors = 0;
- goto starttls;
- }
-
- greeting:
-
-#endif /* STARTTLS */
-
- /* output the first line, inserting "ESMTP" as second word */
- if (*greetcode == '5')
- (void) sm_snprintf(inp, sizeof(inp),
- "%s not accepting messages", hostname);
- else
- expand(SmtpGreeting, inp, sizeof(inp), e);
-
- p = strchr(inp, '\n');
- if (p != NULL)
- *p++ = '\0';
- id = strchr(inp, ' ');
- if (id == NULL)
- id = &inp[strlen(inp)];
- if (p == NULL)
- (void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
- "%s %%.*s ESMTP%%s", greetcode);
- else
- (void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
- "%s-%%.*s ESMTP%%s", greetcode);
- message(cmdbuf, (int) (id - inp), inp, id);
-
- /* output remaining lines */
- while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
- {
- *p++ = '\0';
- if (isascii(*id) && isspace(*id))
- id++;
- (void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, "-%s");
- message(cmdbuf, id);
- }
- if (id != NULL)
- {
- if (isascii(*id) && isspace(*id))
- id++;
- (void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, " %s");
- message(cmdbuf, id);
- }
-
- protocol = NULL;
- sendinghost = macvalue('s', e);
-
- /* If quarantining by a connect/ehlo action, save between messages */
- if (e->e_quarmsg == NULL)
- smtp.sm_quarmsg = NULL;
- else
- smtp.sm_quarmsg = newstr(e->e_quarmsg);
-
- /* sendinghost's storage must outlive the current envelope */
- if (sendinghost != NULL)
- sendinghost = sm_strdup_x(sendinghost);
- first = true;
- gothello = false;
- smtp.sm_gotmail = false;
- for (;;)
- {
- SM_TRY
- {
- QuickAbort = false;
- HoldErrs = false;
- SuprErrs = false;
- LogUsrErrs = false;
- OnlyOneError = true;
- e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
-#if MILTER
- milter_cmd_fail = false;
-#endif /* MILTER */
-
- /* setup for the read */
- e->e_to = NULL;
- Errors = 0;
- FileName = NULL;
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
-
- /* read the input line */
- SmtpPhase = "server cmd read";
- sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
-
- /* handle errors */
- if (sm_io_error(OutChannel) ||
- (p = sfgets(inp, sizeof(inp), InChannel,
- TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
- {
- char *d;
-
- d = macvalue(macid("{daemon_name}"), e);
- if (d == NULL)
- d = "stdin";
- /* end of file, just die */
- disconnect(1, e);
-
-#if MILTER
- /* close out milter filters */
- milter_quit(e);
-#endif /* MILTER */
-
- message("421 4.4.1 %s Lost input channel from %s",
- MyHostName, CurSmtpClient);
- if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
- sm_syslog(LOG_NOTICE, e->e_id,
- "lost input channel from %s 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;
- goto doquit;
- }
-
- /* also used by "proxy" check below */
- inplen = strlen(inp);
-#if SASL
- /*
- ** SMTP AUTH requires accepting any length,
- ** at least for challenge/response. However, not imposing
- ** a limit is a bad idea (denial of service).
- */
-
- if (authenticating != SASL_PROC_AUTH
- && sm_strncasecmp(inp, "AUTH ", 5) != 0
- && inplen > MAXLINE)
- {
- message("421 4.7.0 %s Command too long, possible attack %s",
- MyHostName, CurSmtpClient);
- sm_syslog(LOG_INFO, e->e_id,
- "%s: SMTP violation, input too long: %lu",
- CurSmtpClient, (unsigned long) inplen);
- goto doquit;
- }
-#endif /* SASL */
-
- if (first)
- {
- size_t cmdlen;
- int idx;
- char *http_cmd;
- static char *http_cmds[] = { "GET", "POST",
- "CONNECT", "USER", NULL };
-
- for (idx = 0; (http_cmd = http_cmds[idx]) != NULL;
- idx++)
- {
- cmdlen = strlen(http_cmd);
- if (cmdlen < inplen &&
- sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
- isascii(inp[cmdlen]) && isspace(inp[cmdlen]))
- {
- /* Open proxy, drop it */
- message("421 4.7.0 %s Rejecting open proxy %s",
- MyHostName, CurSmtpClient);
- sm_syslog(LOG_INFO, e->e_id,
- "%s: probable open proxy: command=%.40s",
- CurSmtpClient, inp);
- goto doquit;
- }
- }
- first = false;
- }
-
- /* clean up end of line */
- fixcrlf(inp, true);
-
-#if PIPELINING
-# if _FFR_NO_PIPE
- /*
- ** if there is more input and pipelining is disabled:
- ** delay ... (and maybe discard the input?)
- ** XXX this doesn't really work, at least in tests using
- ** telnet SM_IO_IS_READABLE only returns 1 if there were
- ** more than 2 input lines available.
- */
-
- if (bitset(SRV_NO_PIPE, features) &&
- sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
- {
- if (++np_log < 3)
- sm_syslog(LOG_INFO, NOQID,
- "unauthorized PIPELINING, sleeping");
- sleep(1);
- }
-
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
-
-#if SASL
- if (authenticating == SASL_PROC_AUTH)
- {
-# if 0
- if (*inp == '\0')
- {
- authenticating = SASL_NOT_AUTH;
- message("501 5.5.2 missing input");
- RESET_SASLCONN;
- continue;
- }
-# endif /* 0 */
- if (*inp == '*' && *(inp + 1) == '\0')
- {
- authenticating = SASL_NOT_AUTH;
-
- /* RFC 2554 4. */
- message("501 5.0.0 AUTH aborted");
- RESET_SASLCONN;
- continue;
- }
-
- /* could this be shorter? XXX */
-# if SASL >= 20000
- in = xalloc(strlen(inp) + 1);
- result = sasl_decode64(inp, strlen(inp), in,
- strlen(inp), &inlen);
-# else /* SASL >= 20000 */
- out = xalloc(strlen(inp));
- result = sasl_decode64(inp, strlen(inp), out, &outlen);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- authenticating = SASL_NOT_AUTH;
-
- /* RFC 2554 4. */
- message("501 5.5.4 cannot decode AUTH parameter %s",
- inp);
-# if SASL >= 20000
- sm_free(in);
-# endif /* SASL >= 20000 */
- RESET_SASLCONN;
- continue;
- }
-
-# if SASL >= 20000
- result = sasl_server_step(conn, in, inlen,
- &out, &outlen);
- sm_free(in);
-# else /* SASL >= 20000 */
- result = sasl_server_step(conn, out, outlen,
- &out, &outlen, &errstr);
-# endif /* SASL >= 20000 */
-
- /* get an OK if we're done */
- if (result == SASL_OK)
- {
- authenticated:
- message("235 2.0.0 OK Authenticated");
- authenticating = SASL_IS_AUTH;
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{auth_type}"), auth_type);
-
-# if SASL >= 20000
- user = macvalue(macid("{auth_authen}"), e);
-
- /* get security strength (features) */
- result = sasl_getprop(conn, SASL_SSF,
- (const void **) &ssf);
-# else /* SASL >= 20000 */
- result = sasl_getprop(conn, SASL_USERNAME,
- (void **)&user);
- if (result != SASL_OK)
- {
- user = "";
- macdefine(&BlankEnvelope.e_macro,
- A_PERM,
- macid("{auth_authen}"), NULL);
- }
- else
- {
- macdefine(&BlankEnvelope.e_macro,
- A_TEMP,
- macid("{auth_authen}"),
- xtextify(user, "<>\")"));
- }
-
-# if 0
- /* get realm? */
- sasl_getprop(conn, SASL_REALM, (void **) &data);
-# endif /* 0 */
-
- /* get security strength (features) */
- result = sasl_getprop(conn, SASL_SSF,
- (void **) &ssf);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- macdefine(&BlankEnvelope.e_macro,
- A_PERM,
- macid("{auth_ssf}"), "0");
- ssf = NULL;
- }
- else
- {
- char pbuf[8];
-
- (void) sm_snprintf(pbuf, sizeof(pbuf),
- "%u", *ssf);
- macdefine(&BlankEnvelope.e_macro,
- A_TEMP,
- macid("{auth_ssf}"), pbuf);
- if (tTd(95, 8))
- sm_dprintf("AUTH auth_ssf: %u\n",
- *ssf);
- }
-
- /*
- ** Only switch to encrypted connection
- ** if a security layer has been negotiated
- */
-
- if (ssf != NULL && *ssf > 0)
- {
- int tmo;
-
- /*
- ** Convert I/O layer to use SASL.
- ** If the call fails, the connection
- ** is aborted.
- */
-
- tmo = TimeOuts.to_datablock * 1000;
- if (sfdcsasl(&InChannel, &OutChannel,
- conn, tmo) == 0)
- {
- /* restart dialogue */
- n_helo = 0;
-# if PIPELINING
- (void) sm_io_autoflush(InChannel,
- OutChannel);
-# endif /* PIPELINING */
- }
- else
- syserr("503 5.3.3 SASL TLS failed");
- }
-
- /* NULL pointer ok since it's our function */
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d",
- CurSmtpClient,
- shortenstring(user, 128),
- auth_type, *ssf);
- }
- else if (result == SASL_CONTINUE)
- {
- len = ENC64LEN(outlen);
- out2 = xalloc(len);
- result = sasl_encode64(out, outlen, out2, len,
- &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,
- "AUTH encode64 error [%d for \"%s\"]",
- result, out);
- /* start over? */
- authenticating = SASL_NOT_AUTH;
- }
- else
- {
- message("334 %s", out2);
- if (tTd(95, 2))
- sm_dprintf("AUTH continue: msg='%s' len=%u\n",
- out2, out2len);
- }
-# if SASL >= 20000
- sm_free(out2);
-# endif /* SASL >= 20000 */
- }
- else
- {
- /* not SASL_OK or SASL_CONT */
- message("535 5.7.0 authentication failed");
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH failure (%s): %s (%d) %s",
- auth_type,
- sasl_errstring(result, NULL,
- NULL),
- result,
-# if SASL >= 20000
- sasl_errdetail(conn));
-# else /* SASL >= 20000 */
- errstr == NULL ? "" : errstr);
-# endif /* SASL >= 20000 */
- RESET_SASLCONN;
- 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)
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "<<< %s\n", inp);
-
- if (LogLevel > 14)
- sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
-
- /* break off command */
- for (p = inp; isascii(*p) && isspace(*p); p++)
- continue;
- cmd = cmdbuf;
- while (*p != '\0' &&
- !(isascii(*p) && isspace(*p)) &&
- cmd < &cmdbuf[sizeof(cmdbuf) - 2])
- *cmd++ = *p++;
- *cmd = '\0';
-
- /* throw away leading whitespace */
- SKIP_SPACE(p);
-
- /* decode command */
- for (c = CmdTab; c->cmd_name != NULL; c++)
- {
- if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
- break;
- }
-
- /* reset errors */
- errno = 0;
-
- /* check whether a "non-null" command has been used */
- switch (c->cmd_code)
- {
-#if SASL
- case CMDAUTH:
- /* avoid information leak; take first two words? */
- q = "AUTH";
- break;
-#endif /* SASL */
-
- case CMDMAIL:
- case CMDEXPN:
- case CMDVRFY:
- case CMDETRN:
- lognullconnection = false;
- /* FALLTHROUGH */
- default:
- q = inp;
- break;
- }
-
- if (e->e_id == NULL)
- sm_setproctitle(true, e, "%s: %.80s",
- CurSmtpClient, q);
- else
- sm_setproctitle(true, e, "%s %s: %.80s",
- qid_printname(e),
- CurSmtpClient, q);
-
- /*
- ** Process command.
- **
- ** If we are running as a null server, return 550
- ** to almost everything.
- */
-
- if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
- {
- switch (c->cmd_code)
- {
- case CMDQUIT:
- case CMDHELO:
- case CMDEHLO:
- case CMDNOOP:
- case CMDRSET:
- case CMDERROR:
- /* process normally */
- break;
-
- case CMDETRN:
- if (bitnset(D_ETRNONLY, d_flags) &&
- nullserver == NULL)
- break;
- DELAY_CONN("ETRN");
- /* FALLTHROUGH */
-
- default:
-#if MAXBADCOMMANDS > 0
- /* theoretically this could overflow */
- if (nullserver != NULL &&
- ++n_badcmds > MAXBADCOMMANDS)
- {
- message("421 4.7.0 %s Too many bad commands; closing connection",
- MyHostName);
-
- /* arrange to ignore send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
-#endif /* MAXBADCOMMANDS > 0 */
- if (nullserver != NULL)
- {
- if (ISSMTPREPLY(nullserver))
- usrerr(nullserver);
- else
- usrerr("550 5.0.0 %s",
- nullserver);
- }
- else
- usrerr("452 4.4.5 Insufficient disk space; try again later");
- continue;
- }
- }
-
- switch (c->cmd_code)
- {
-#if SASL
- case CMDAUTH: /* sasl */
- DELAY_CONN("AUTH");
- if (!sasl_ok || n_mechs <= 0)
- {
- message("503 5.3.3 AUTH not available");
- break;
- }
- if (authenticating == SASL_IS_AUTH)
- {
- message("503 5.5.0 Already Authenticated");
- break;
- }
- if (smtp.sm_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 %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr("454 4.3.0 Please try again later");
- break;
- }
-
- ismore = false;
-
- /* crude way to avoid crack attempts */
- STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
- true, "AUTH", e));
-
- /* make sure mechanism (p) is 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;
- }
- }
-
- if (*p == '\0')
- {
- message("501 5.5.2 AUTH mechanism must be specified");
- break;
- }
-
- /* check whether mechanism is available */
- if (iteminlist(p, mechlist, " ") == NULL)
- {
- message("504 5.3.3 AUTH mechanism %.32s not available",
- p);
- break;
- }
-
- /*
- ** RFC 2554 4.
- ** Unlike a zero-length client answer to a
- ** 334 reply, a zero- length initial response
- ** is sent as a single equals sign ("=").
- */
-
- if (ismore && *q == '=' && *(q + 1) == '\0')
- {
- /* will be free()d, don't use in=""; */
- in = xalloc(1);
- *in = '\0';
- inlen = 0;
- }
- else if (ismore)
- {
- /* could this be shorter? XXX */
-# if SASL >= 20000
- in = xalloc(strlen(q) + 1);
- result = sasl_decode64(q, strlen(q), in,
- strlen(q), &inlen);
-# else /* SASL >= 20000 */
- in = sm_rpool_malloc(e->e_rpool, strlen(q));
- result = sasl_decode64(q, strlen(q), in,
- &inlen);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- message("501 5.5.4 cannot BASE64 decode '%s'",
- q);
- if (LogLevel > 5)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH decode64 error [%d for \"%s\"]",
- result, q);
- /* start over? */
- authenticating = SASL_NOT_AUTH;
-# if SASL >= 20000
- sm_free(in);
-# endif /* SASL >= 20000 */
- in = NULL;
- inlen = 0;
- break;
- }
- }
- else
- {
- in = NULL;
- inlen = 0;
- }
-
- /* see if that auth type exists */
-# if SASL >= 20000
- result = sasl_server_start(conn, p, in, inlen,
- &out, &outlen);
- if (in != NULL)
- sm_free(in);
-# else /* SASL >= 20000 */
- result = sasl_server_start(conn, p, in, inlen,
- &out, &outlen, &errstr);
-# endif /* SASL >= 20000 */
-
- if (result != SASL_OK && result != SASL_CONTINUE)
- {
- message("535 5.7.0 authentication failed");
- if (LogLevel > 9)
- sm_syslog(LOG_ERR, e->e_id,
- "AUTH failure (%s): %s (%d) %s",
- p,
- sasl_errstring(result, NULL,
- NULL),
- result,
-# if SASL >= 20000
- sasl_errdetail(conn));
-# else /* SASL >= 20000 */
- errstr);
-# endif /* SASL >= 20000 */
- RESET_SASLCONN;
- 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,
- &out2len);
-
- if (result != SASL_OK)
- {
- message("454 4.5.4 Temporary authentication failure");
- if (LogLevel > 5)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH encode64 error [%d for \"%s\"]",
- result, out);
-
- /* start over? */
- authenticating = SASL_NOT_AUTH;
- RESET_SASLCONN;
- }
- else
- {
- message("334 %s", out2);
- authenticating = SASL_PROC_AUTH;
- }
-# if SASL >= 20000
- sm_free(out2);
-# endif /* SASL >= 20000 */
- break;
-#endif /* SASL */
-
-#if STARTTLS
- case CMDSTLS: /* starttls */
- DELAY_CONN("STARTTLS");
- if (*p != '\0')
- {
- message("501 5.5.2 Syntax error (no parameters allowed)");
- break;
- }
- if (!bitset(SRV_OFFER_TLS, features))
- {
- message("503 5.5.0 TLS not available");
- break;
- }
- if (!tls_ok_srv)
- {
- message("454 4.3.3 TLS not available after start");
- break;
- }
- if (smtp.sm_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 %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr("454 4.7.0 Please try again later");
- break;
- }
- starttls:
-# if TLS_NO_RSA
- /*
- ** XXX do we need a temp key ?
- */
-# else /* TLS_NO_RSA */
-# endif /* TLS_NO_RSA */
-
-# if TLS_VRFY_PER_CTX
- /*
- ** Note: this sets the verification globally
- ** (per SSL_CTX)
- ** it's ok since it applies only to one transaction
- */
-
- TLS_VERIFY_CLIENT();
-# endif /* TLS_VRFY_PER_CTX */
-
- if (srv_ssl != NULL)
- SSL_clear(srv_ssl);
- else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
- {
- message("454 4.3.3 TLS not available: error generating SSL handle");
- if (LogLevel > 8)
- tlslogerr("server");
- goto tls_done;
- }
-
-# if !TLS_VRFY_PER_CTX
- /*
- ** this could be used if it were possible to set
- ** verification per SSL (connection)
- ** not just per SSL_CTX (global)
- */
-
- TLS_VERIFY_CLIENT();
-# endif /* !TLS_VRFY_PER_CTX */
-
- rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
- wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
-
- if (rfd < 0 || wfd < 0 ||
- SSL_set_rfd(srv_ssl, rfd) <= 0 ||
- SSL_set_wfd(srv_ssl, wfd) <= 0)
- {
- message("454 4.3.3 TLS not available: error set fd");
- SSL_free(srv_ssl);
- srv_ssl = NULL;
- goto tls_done;
- }
- if (!smtps)
- message("220 2.0.0 Ready to start TLS");
-# if PIPELINING
- (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-# endif /* PIPELINING */
-
- SSL_set_accept_state(srv_ssl);
-
-# define SSL_ACC(s) SSL_accept(s)
-
- tlsstart = curtime();
- ssl_retry:
- if ((r = SSL_ACC(srv_ssl)) <= 0)
- {
- int i, ssl_err;
-
- ssl_err = SSL_get_error(srv_ssl, r);
- i = tls_retry(srv_ssl, rfd, wfd, tlsstart,
- TimeOuts.to_starttls, ssl_err,
- "server");
- if (i > 0)
- goto ssl_retry;
-
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=server, error: accept failed=%d, SSL_error=%d, errno=%d, retry=%d",
- r, ssl_err, errno, i);
- if (LogLevel > 8)
- tlslogerr("server");
- }
- tls_ok_srv = false;
- SSL_free(srv_ssl);
- srv_ssl = NULL;
-
- /*
- ** according to the next draft of
- ** RFC 2487 the connection should be dropped
- */
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
-
- /* ignore return code for now, it's in {verify} */
- (void) tls_get_info(srv_ssl, true,
- CurSmtpClient,
- &BlankEnvelope.e_macro,
- bitset(SRV_VRFY_CLT, features));
-
- /*
- ** 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}"), e),
- "STARTTLS", e,
- RSF_RMCOMM|RSF_COUNT,
- 5, NULL, NOQID, NULL) != EX_OK ||
- Errors > 0)
- {
- extern char MsgBuf[];
-
- if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
- nullserver = newstr(MsgBuf);
- else
- nullserver = "503 5.7.0 Authentication required.";
- }
- QuickAbort = saveQuickAbort;
- SuprErrs = saveSuprErrs;
-
- tls_ok_srv = false; /* don't offer STARTTLS again */
- n_helo = 0;
-# if SASL
- if (sasl_ok)
- {
- int cipher_bits;
- bool verified;
- char *s, *v, *c;
-
- s = macvalue(macid("{cipher_bits}"), e);
- v = macvalue(macid("{verify}"), e);
- c = macvalue(macid("{cert_subject}"), e);
- verified = (v != NULL && strcmp(v, "OK") == 0);
- if (s != NULL && (cipher_bits = atoi(s)) > 0)
- {
-# if SASL >= 20000
- ext_ssf = cipher_bits;
- auth_id = verified ? c : NULL;
- sasl_ok = ((sasl_setprop(conn,
- SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK) &&
- (sasl_setprop(conn,
- SASL_AUTH_EXTERNAL,
- auth_id) == SASL_OK));
-# else /* SASL >= 20000 */
- ext_ssf.ssf = cipher_bits;
- ext_ssf.auth_id = verified ? c : NULL;
- sasl_ok = sasl_setprop(conn,
- SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK;
-# endif /* SASL >= 20000 */
- mechlist = NULL;
- if (sasl_ok)
- n_mechs = saslmechs(conn,
- &mechlist);
- }
- }
-# endif /* SASL */
-
- /* switch to secure connection */
- if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
- {
- tls_active = true;
-# if PIPELINING
- (void) sm_io_autoflush(InChannel, OutChannel);
-# endif /* PIPELINING */
- }
- 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("STARTTLS: can't switch to encrypted layer");
- }
- tls_done:
- if (smtps)
- {
- if (tls_active)
- goto greeting;
- else
- goto doquit;
- }
- break;
-#endif /* STARTTLS */
-
- case CMDHELO: /* hello -- introduce yourself */
- case CMDEHLO: /* extended hello */
- DELAY_CONN("EHLO");
- if (c->cmd_code == CMDEHLO)
- {
- protocol = "ESMTP";
- SmtpPhase = "server EHLO";
- }
- else
- {
- protocol = "SMTP";
- SmtpPhase = "server HELO";
- }
-
- /* avoid denial-of-service */
- STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
- true, "HELO/EHLO", e));
-
-#if 0
- /* RFC2821 4.1.4 allows duplicate HELO/EHLO */
- /* check for duplicate HELO/EHLO per RFC 1651 4.2 */
- if (gothello)
- {
- usrerr("503 %s Duplicate HELO/EHLO",
- MyHostName);
- break;
- }
-#endif /* 0 */
-
- /* check for valid domain name (re 1123 5.2.5) */
- if (*p == '\0' && !AllowBogusHELO)
- {
- usrerr("501 %s requires domain address",
- cmdbuf);
- break;
- }
-
- /* check for long domain name (hides Received: info) */
- 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 %s",
- CurSmtpClient);
- break;
- }
-
- ok = true;
- for (q = p; *q != '\0'; q++)
- {
- if (!isascii(*q))
- break;
- if (isalnum(*q))
- continue;
- if (isspace(*q))
- {
- *q = '\0';
-
- /* only complain if strict check */
- ok = AllowBogusHELO;
-
- /* allow trailing whitespace */
- while (!ok && *++q != '\0' &&
- isspace(*q))
- ;
- if (*q == '\0')
- ok = true;
- break;
- }
- if (strchr("[].-_#:", *q) == NULL)
- break;
- }
-
- if (*q == '\0' && ok)
- {
- q = "pleased to meet you";
- sendinghost = sm_strdup_x(p);
- }
- else if (!AllowBogusHELO)
- {
- usrerr("501 Invalid domain name");
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, CurEnv->e_id,
- "invalid domain name (%s) from %.100s",
- p, CurSmtpClient);
- break;
- }
- else
- {
- q = "accepting invalid domain name";
- }
-
- if (gothello || smtp.sm_gotmail)
- CLEAR_STATE(cmdbuf);
-
-#if MILTER
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_helo(p, e, &state);
- switch (state)
- {
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: helo=%s, reject=Command rejected",
- p);
- nullserver = "Command rejected";
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: helo=%s, reject=%s",
- p, MSG_TEMPFAIL);
- tempfail = true;
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_REPLYCODE:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: helo=%s, reject=%s",
- p, response);
- if (strncmp(response, "421 ", 4) != 0
- && strncmp(response, "421-", 4) != 0)
- {
- nullserver = newstr(response);
- smtp.sm_milterize = false;
- break;
- }
- /* FALLTHROUGH */
-
- case SMFIR_SHUTDOWN:
- if (MilterLogLevel > 3 &&
- response == NULL)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: helo=%s, reject=421 4.7.0 %s closing connection",
- p, MyHostName);
- tempfail = true;
- smtp.sm_milterize = false;
- if (response != NULL)
- usrerr(response);
- else
- message("421 4.7.0 %s closing connection",
- MyHostName);
- /* arrange to ignore send list */
- e->e_sendqueue = NULL;
- lognullconnection = false;
- goto doquit;
- }
- if (response != NULL)
- sm_free(response);
-
- /*
- ** If quarantining by a connect/ehlo action,
- ** save between messages
- */
-
- if (smtp.sm_quarmsg == NULL &&
- e->e_quarmsg != NULL)
- smtp.sm_quarmsg = newstr(e->e_quarmsg);
- }
-#endif /* MILTER */
- gothello = true;
-
- /* print HELO response message */
- if (c->cmd_code != CMDEHLO)
- {
- message("250 %s Hello %s, %s",
- MyHostName, CurSmtpClient, q);
- break;
- }
-
- 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
- **
- ** Note: If you change this list,
- ** remember to update 'helpfile'
- */
-
- message("250-ENHANCEDSTATUSCODES");
-#if PIPELINING
- if (bitset(SRV_OFFER_PIPE, features))
- message("250-PIPELINING");
-#endif /* PIPELINING */
- if (bitset(SRV_OFFER_EXPN, features))
- {
- message("250-EXPN");
- if (bitset(SRV_OFFER_VERB, features))
- message("250-VERB");
- }
-#if MIME8TO7
- message("250-8BITMIME");
-#endif /* MIME8TO7 */
- if (MaxMessageSize > 0)
- message("250-SIZE %ld", MaxMessageSize);
- else
- message("250-SIZE");
-#if DSN
- if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
- message("250-DSN");
-#endif /* DSN */
- if (bitset(SRV_OFFER_ETRN, features))
- message("250-ETRN");
-#if SASL
- if (sasl_ok && mechlist != NULL && *mechlist != '\0')
- message("250-AUTH %s", mechlist);
-#endif /* SASL */
-#if STARTTLS
- if (tls_ok_srv &&
- bitset(SRV_OFFER_TLS, features))
- message("250-STARTTLS");
-#endif /* STARTTLS */
- if (DeliverByMin > 0)
- message("250-DELIVERBY %ld",
- (long) DeliverByMin);
- else if (DeliverByMin == 0)
- message("250-DELIVERBY");
-
- /* < 0: no deliver-by */
-
- message("250 HELP");
- break;
-
- case CMDMAIL: /* mail -- designate sender */
- SmtpPhase = "server MAIL";
- DELAY_CONN("MAIL");
-
- /* check for validity of this command */
- if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
- {
- usrerr("503 5.0.0 Polite people say HELO first");
- break;
- }
- if (smtp.sm_gotmail)
- {
- usrerr("503 5.5.0 Sender already specified");
- break;
- }
-#if SASL
- if (bitset(SRV_REQ_AUTH, features) &&
- 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 %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr(MSG_TEMPFAIL);
- break;
- }
-
- /* make sure we know who the sending host is */
- if (sendinghost == NULL)
- sendinghost = peerhostname;
-
-
-#if SM_HEAP_CHECK
- if (sm_debug_active(&DebugLeakSmtp, 1))
- {
- sm_heap_newgroup();
- sm_dprintf("smtp() heap group #%d\n",
- sm_heap_group());
- }
-#endif /* SM_HEAP_CHECK */
-
- if (Errors > 0)
- goto undo_no_pm;
- if (!gothello)
- {
- auth_warning(e, "%s didn't use HELO protocol",
- CurSmtpClient);
- }
-#ifdef PICKY_HELO_CHECK
- if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
- (sm_strcasecmp(peerhostname, "localhost") != 0 ||
- sm_strcasecmp(sendinghost, MyHostName) != 0))
- {
- auth_warning(e, "Host %s claimed to be %s",
- CurSmtpClient, sendinghost);
- }
-#endif /* PICKY_HELO_CHECK */
-
- if (protocol == NULL)
- protocol = "SMTP";
- macdefine(&e->e_macro, A_PERM, 'r', protocol);
- macdefine(&e->e_macro, A_PERM, 's', sendinghost);
-
- if (Errors > 0)
- goto undo_no_pm;
- smtp.sm_nrcpts = 0;
- n_badrcpts = 0;
- macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
- macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
- macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
- "0");
- e->e_flags |= EF_CLRQUEUE;
- sm_setproctitle(true, e, "%s %s: %.80s",
- qid_printname(e),
- CurSmtpClient, inp);
-
- /* do the processing */
- SM_TRY
- {
- extern char *FullName;
-
- QuickAbort = true;
- SM_FREE_CLR(FullName);
-
- /* must parse sender first */
- delimptr = NULL;
- setsender(p, e, &delimptr, ' ', false);
- if (delimptr != NULL && *delimptr != '\0')
- *delimptr++ = '\0';
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- /* Successfully set e_from, allow logging */
- e->e_flags |= EF_LOGSENDER;
-
- /* put resulting triple from parseaddr() into macros */
- if (e->e_from.q_mailer != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_mailer}"),
- e->e_from.q_mailer->m_name);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_mailer}"), NULL);
- if (e->e_from.q_host != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_host}"),
- e->e_from.q_host);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_host}"), "localhost");
- if (e->e_from.q_user != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_addr}"),
- e->e_from.q_user);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_addr}"), NULL);
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- /* 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))
- {
- auth_warning(e, "%s owned process doing -bs",
- RealUserName);
- }
-
- /* reset to default value */
- SevenBitInput = SevenBitInput_Saved;
-
- /* now parse ESMTP arguments */
- e->e_msgsize = 0;
- addr = p;
- parse_esmtp_args(e, NULL, p, delimptr, "MAIL", args,
- mail_esmtp_args);
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
-#if SASL
-# if _FFR_AUTH_PASSING
- /* set the default AUTH= if the sender didn't */
- if (e->e_auth_param == NULL)
- {
- /* XXX only do this for an MSA? */
- e->e_auth_param = macvalue(macid("{auth_authen}"),
- e);
- if (e->e_auth_param == NULL)
- e->e_auth_param = "<>";
-
- /*
- ** XXX should we invoke Strust_auth now?
- ** authorizing as the client that just
- ** authenticated, so we'll trust implicitly
- */
- }
-# endif /* _FFR_AUTH_PASSING */
-#endif /* SASL */
-
- /* do config file checking of the sender */
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "e s");
-#if _FFR_MAIL_MACRO
- /* make the "real" sender address available */
- macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
- e->e_from.q_paddr);
-#endif /* _FFR_MAIL_MACRO */
- if (rscheck("check_mail", addr,
- NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
- NULL, e->e_id, NULL) != EX_OK ||
- Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), NULL);
-
- if (MaxMessageSize > 0 &&
- (e->e_msgsize > MaxMessageSize ||
- e->e_msgsize < 0))
- {
- usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
- MaxMessageSize);
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- }
-
- /*
- ** XXX always check whether there is at least one fs
- ** with enough space?
- ** However, this may not help much: the queue group
- ** selection may later on select a FS that hasn't
- ** enough space.
- */
-
- if ((NumFileSys == 1 || NumQueue == 1) &&
- !enoughdiskspace(e->e_msgsize, e)
-#if _FFR_ANY_FREE_FS
- && !filesys_free(e->e_msgsize)
-#endif /* _FFR_ANY_FREE_FS */
- )
- {
- /*
- ** We perform this test again when the
- ** queue directory is selected, in collect.
- */
-
- usrerr("452 4.4.5 Insufficient disk space; try again later");
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- }
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- LogUsrErrs = true;
-#if MILTER
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_envfrom(args, e, &state);
- MILTER_REPLY("from");
- }
-#endif /* MILTER */
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- message("250 2.1.0 Sender ok");
- smtp.sm_gotmail = true;
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /*
- ** An error occurred while processing a MAIL command.
- ** Jump to the common error handling code.
- */
-
- sm_exc_free(exc);
- goto undo_no_pm;
- }
- SM_END_TRY
- break;
-
- undo_no_pm:
- e->e_flags &= ~EF_PM_NOTIFY;
- undo:
- break;
-
- case CMDRCPT: /* rcpt -- designate recipient */
- DELAY_CONN("RCPT");
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), NULL);
-#if MILTER
- (void) memset(&addr_st, '\0', sizeof(addr_st));
- a = NULL;
- milter_rcpt_added = false;
- smtp.sm_e_nrcpts_orig = e->e_nrcpts;
-#endif
- if (BadRcptThrottle > 0 &&
- n_badrcpts >= BadRcptThrottle)
- {
- if (LogLevel > 5 &&
- n_badrcpts == BadRcptThrottle)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "%s: Possible SMTP RCPT flood, throttling.",
- CurSmtpClient);
-
- /* To avoid duplicated message */
- n_badrcpts++;
- }
- NBADRCPTS;
-
- /*
- ** Don't use exponential backoff for now.
- ** Some servers will open more connections
- ** and actually overload the receiver even
- ** more.
- */
-
- (void) sleep(1);
- }
- if (!smtp.sm_gotmail)
- {
- usrerr("503 5.0.0 Need MAIL before RCPT");
- break;
- }
- SmtpPhase = "server RCPT";
- SM_TRY
- {
- QuickAbort = true;
- LogUsrErrs = true;
-
- /* limit flooding of our machine */
- if (MaxRcptPerMsg > 0 &&
- smtp.sm_nrcpts >= MaxRcptPerMsg)
- {
- /* sleep(1); / * slow down? */
- usrerr("452 4.5.3 Too many recipients");
- goto rcpt_done;
- }
-
- if (e->e_sendmode != SM_DELIVER
-#if _FFR_DM_ONE
- && (NotFirstDelivery || SM_DM_ONE != e->e_sendmode)
-#endif /* _FFR_DM_ONE */
- )
- e->e_flags |= EF_VRFYONLY;
-
-#if MILTER
- /*
- ** Do not expand recipients at RCPT time (in the call
- ** to recipient()) if a milter can delete or reject
- ** a RCPT. 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 (!(smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags)) &&
- (smtp.sm_milters.mis_flags &
- (MIS_FL_DEL_RCPT|MIS_FL_REJ_RCPT)) != 0)
- e->e_flags |= EF_VRFYONLY;
- milter_cmd_done = false;
- milter_cmd_safe = false;
-#endif /* MILTER */
-
- p = skipword(p, "to");
- if (p == NULL)
- goto rcpt_done;
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "e r");
- a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
- e, true);
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), NULL);
- if (Errors > 0)
- goto rcpt_done;
- if (a == NULL)
- {
- usrerr("501 5.0.0 Missing recipient");
- goto rcpt_done;
- }
-
- if (delimptr != NULL && *delimptr != '\0')
- *delimptr++ = '\0';
-
- /* put resulting triple from parseaddr() into macros */
- if (a->q_mailer != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"),
- a->q_mailer->m_name);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"), NULL);
- if (a->q_host != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), a->q_host);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), "localhost");
- if (a->q_user != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), a->q_user);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), NULL);
- if (Errors > 0)
- goto rcpt_done;
-
- /* now parse ESMTP arguments */
- addr = p;
- parse_esmtp_args(e, a, p, delimptr, "RCPT", args,
- rcpt_esmtp_args);
- if (Errors > 0)
- goto rcpt_done;
-
-#if MILTER
- /*
- ** rscheck() can trigger an "exception"
- ** in which case the execution continues at
- ** SM_EXCEPT(exc, "[!F]*")
- ** This means milter_cmd_safe is not set
- ** and hence milter is not invoked.
- ** Would it be "safe" to change that, i.e., use
- ** milter_cmd_safe = true;
- ** here so a milter is informed (if requested)
- ** about RCPTs that are rejected by check_rcpt?
- */
-# if _FFR_MILTER_CHECK_REJECTIONS_TOO
- milter_cmd_safe = true;
-# endif
-#endif
-
- /* do config file checking of the recipient */
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "e r");
- if (rscheck("check_rcpt", addr,
- NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
- NULL, e->e_id, p_addr_st) != EX_OK ||
- Errors > 0)
- goto rcpt_done;
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), NULL);
-
- /* If discarding, don't bother to verify user */
- if (bitset(EF_DISCARD, e->e_flags))
- a->q_state = QS_VERIFIED;
-#if MILTER
- milter_cmd_safe = true;
-#endif
-
- /* save in recipient list after ESMTP mods */
- a = recipient(a, &e->e_sendqueue, 0, e);
- /* may trigger exception... */
-
-#if MILTER
- milter_rcpt_added = true;
-#endif
-
- if(!(Errors > 0) && QS_IS_BADADDR(a->q_state))
- {
- /* punt -- should keep message in ADDRESS.... */
- usrerr("550 5.1.1 Addressee unknown");
- }
-
-#if MILTER
- rcpt_done:
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- /* how to get the error codes? */
- if (Errors > 0)
- {
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"),
- "error");
- if (a != NULL &&
- a->q_status != NULL &&
- a->q_rstatus != NULL)
- {
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"),
- a->q_status);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"),
- a->q_rstatus);
- }
- else
- {
- if (addr_st.q_host != NULL)
- macdefine(&e->e_macro,
- A_PERM,
- macid("{rcpt_host}"),
- addr_st.q_host);
- if (addr_st.q_user != NULL)
- macdefine(&e->e_macro,
- A_PERM,
- macid("{rcpt_addr}"),
- addr_st.q_user);
- }
- }
-
- response = milter_envrcpt(args, e, &state,
- Errors > 0);
- milter_cmd_done = true;
- MILTER_REPLY("to");
- }
-#endif /* MILTER */
-
- /* no errors during parsing, but might be a duplicate */
- e->e_to = a->q_paddr;
- if (!(Errors > 0) && !QS_IS_BADADDR(a->q_state))
- {
- if (smtp.sm_nrcpts == 0)
- initsys(e);
- message("250 2.1.5 Recipient ok%s",
- QS_IS_QUEUEUP(a->q_state) ?
- " (will queue)" : "");
- smtp.sm_nrcpts++;
- }
-
- /* Is this needed? */
-#if !MILTER
- rcpt_done:
-#endif /* !MILTER */
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_notify}"), NULL);
-
- if (Errors > 0)
- {
- ++n_badrcpts;
- NBADRCPTS;
- }
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /* An exception occurred while processing RCPT */
- e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
- ++n_badrcpts;
- NBADRCPTS;
-#if MILTER
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags) &&
- !milter_cmd_done && milter_cmd_safe)
- {
- char state;
- char *response;
-
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"), "error");
-
- /* how to get the error codes? */
- if (addr_st.q_host != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"),
- addr_st.q_host);
- else if (a != NULL && a->q_status != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"),
- a->q_status);
-
- if (addr_st.q_user != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"),
- addr_st.q_user);
- else if (a != NULL && a->q_rstatus != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"),
- a->q_rstatus);
-
- response = milter_envrcpt(args, e, &state,
- true);
- milter_cmd_done = true;
- MILTER_REPLY("to");
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), NULL);
- }
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- milter_rcpt_added && milter_cmd_done &&
- milter_cmd_fail)
- {
- (void) removefromlist(addr, &e->e_sendqueue, e);
- milter_cmd_fail = false;
- if (smtp.sm_e_nrcpts_orig < e->e_nrcpts)
- e->e_nrcpts = smtp.sm_e_nrcpts_orig;
- }
-#endif /* MILTER */
- }
- SM_END_TRY
- break;
-
- case CMDDATA: /* data -- text of mail */
- DELAY_CONN("DATA");
- if (!smtp_data(&smtp, e))
- goto doquit;
- break;
-
- case CMDRSET: /* rset -- reset state */
- if (tTd(94, 100))
- message("451 4.0.0 Test failure");
- else
- message("250 2.0.0 Reset state");
- CLEAR_STATE(cmdbuf);
- break;
-
- case CMDVRFY: /* vrfy -- verify address */
- case CMDEXPN: /* expn -- expand address */
- vrfy = c->cmd_code == CMDVRFY;
- DELAY_CONN(vrfy ? "VRFY" : "EXPN");
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)",
- vrfy ? "VRFY" : "EXPN",
- p, CurSmtpClient);
-
- /* RFC 821 doesn't allow 4xy reply code */
- usrerr("550 5.7.1 Please try again later");
- break;
- }
- wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
- false, vrfy ? "VRFY" : "EXPN", e);
- STOP_IF_ATTACK(wt);
- previous = curtime();
- if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
- (!vrfy && !bitset(SRV_OFFER_EXPN, features)))
- {
- if (vrfy)
- message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
- else
- message("502 5.7.0 Sorry, we do not allow this operation");
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: %s [rejected]",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- break;
- }
- else if (!gothello &&
- bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
- PrivacyFlags))
- {
- usrerr("503 5.0.0 I demand that you introduce yourself first");
- break;
- }
- if (Errors > 0)
- break;
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id, "%s: %s",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- SM_TRY
- {
- QuickAbort = true;
- vrfyqueue = NULL;
- if (vrfy)
- e->e_flags |= EF_VRFYONLY;
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- {
- 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, RSF_RMCOMM,
- 3, NULL, NOQID, NULL) != EX_OK ||
- Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
- }
- if (wt > 0)
- {
- time_t t;
-
- t = wt - (curtime() - previous);
- if (t > 0)
- (void) sleep(t);
- }
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- if (vrfyqueue == NULL)
- {
- usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
- }
- while (vrfyqueue != NULL)
- {
- 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 &&
- (!QS_IS_UNDELIVERED(a->q_state)))
- continue;
- printvrfyaddr(vrfyqueue, a == NULL, vrfy);
- vrfyqueue = a;
- }
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /*
- ** An exception occurred while processing VRFY/EXPN
- */
-
- sm_exc_free(exc);
- goto undo;
- }
- SM_END_TRY
- break;
-
- case CMDETRN: /* etrn -- force queue flush */
- DELAY_CONN("ETRN");
-
- /* Don't leak queue information via debug flags */
- if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
- (RealUid != 0 && RealUid != TrustedUid &&
- OpMode == MD_SMTP))
- {
- /* different message for MSA ? */
- message("502 5.7.0 Sorry, we do not allow this operation");
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: %s [rejected]",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- break;
- }
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr(MSG_TEMPFAIL);
- break;
- }
-
- if (strlen(p) <= 0)
- {
- usrerr("500 5.5.2 Parameter required");
- break;
- }
-
- /* crude way to avoid denial-of-service attacks */
- STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
- true, "ETRN", e));
-
- /*
- ** Do config file checking of the parameter.
- ** Even though we have srv_features now, we still
- ** need this ruleset because the former is called
- ** when the connection has been established, while
- ** this ruleset is called when the command is
- ** actually issued and therefore has all information
- ** available to make a decision.
- */
-
- if (rscheck("check_etrn", p, NULL, e,
- RSF_RMCOMM, 3, NULL, NOQID, NULL)
- != EX_OK ||
- Errors > 0)
- break;
-
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: ETRN %s", CurSmtpClient,
- shortenstring(p, MAXSHORTSTR));
-
- id = p;
- if (*id == '#')
- {
- int i, qgrp;
-
- id++;
- qgrp = name2qid(id);
- if (!ISVALIDQGRP(qgrp))
- {
- usrerr("459 4.5.4 Queue %s unknown",
- id);
- break;
- }
- for (i = 0; i < NumQueue && Queue[i] != NULL;
- i++)
- Queue[i]->qg_nextrun = (time_t) -1;
- Queue[qgrp]->qg_nextrun = 0;
- ok = run_work_group(Queue[qgrp]->qg_wgrp,
- RWG_FORK|RWG_FORCE);
- if (ok && Errors == 0)
- message("250 2.0.0 Queuing for queue group %s started", id);
- break;
- }
-
- if (*id == '@')
- id++;
- else
- *--id = '@';
-
- new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
- if (new == NULL)
- {
- syserr("500 5.5.0 ETRN out of memory");
- break;
- }
- new->queue_match = id;
- new->queue_negate = false;
- new->queue_next = NULL;
- QueueLimitRecipient = new;
- ok = runqueue(true, false, false, true);
- sm_free(QueueLimitRecipient); /* XXX */
- QueueLimitRecipient = NULL;
- if (ok && Errors == 0)
- message("250 2.0.0 Queuing for node %s started", p);
- break;
-
- case CMDHELP: /* help -- give user info */
- DELAY_CONN("HELP");
- help(p, e);
- break;
-
- case CMDNOOP: /* noop -- do nothing */
- DELAY_CONN("NOOP");
- STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
- true, "NOOP", e));
- message("250 2.0.0 OK");
- break;
-
- case CMDQUIT: /* quit -- leave mail */
- message("221 2.0.0 %s closing connection", MyHostName);
-#if PIPELINING
- (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-#endif /* PIPELINING */
-
- if (smtp.sm_nrcpts > 0)
- logundelrcpts(e, "aborted by sender", 9, false);
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
-
-#if STARTTLS
- /* 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;
- /* XXX sasl_done(); this is a child */
- }
-#endif /* SASL */
-
-doquit:
- /* avoid future 050 messages */
- disconnect(1, e);
-
-#if MILTER
- /* close out milter filters */
- milter_quit(e);
-#endif /* MILTER */
-
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- if (lognullconnection && LogLevel > 5 &&
- nullserver == NULL)
- {
- char *d;
-
- d = macvalue(macid("{daemon_name}"), e);
- if (d == NULL)
- d = "stdin";
-
- /*
- ** even though this id is "bogus", it makes
- ** it simpler to "grep" related events, e.g.,
- ** timeouts for the same connection.
- */
-
- sm_syslog(LOG_INFO, e->e_id,
- "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
- CurSmtpClient, d);
- }
- if (tTd(93, 100))
- {
- /* return to handle next connection */
- return;
- }
- finis(true, true, ExitStat);
- /* NOTREACHED */
-
- /* just to avoid bogus warning from some compilers */
- exit(EX_OSERR);
-
- case CMDVERB: /* set verbose mode */
- DELAY_CONN("VERB");
- if (!bitset(SRV_OFFER_EXPN, features) ||
- !bitset(SRV_OFFER_VERB, features))
- {
- /* this would give out the same info */
- message("502 5.7.0 Verbose unavailable");
- break;
- }
- STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
- true, "VERB", e));
- Verbose = 1;
- set_delivery_mode(SM_DELIVER, e);
- message("250 2.0.0 Verbose mode");
- break;
-
-#if SMTPDEBUG
- case CMDDBGQSHOW: /* show queues */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Send Queue=");
- printaddr(smioout, e->e_sendqueue, true);
- break;
-
- case CMDDBGDEBUG: /* set debug mode */
- tTsetup(tTdvect, sizeof(tTdvect), "0-99.1");
- tTflag(p);
- message("200 2.0.0 Debug set");
- break;
-
-#else /* SMTPDEBUG */
- case CMDDBGQSHOW: /* show queues */
- case CMDDBGDEBUG: /* set debug mode */
-#endif /* SMTPDEBUG */
- case CMDLOGBOGUS: /* bogus command */
- DELAY_CONN("Bogus");
- if (LogLevel > 0)
- sm_syslog(LOG_CRIT, e->e_id,
- "\"%s\" command from %s (%.100s)",
- c->cmd_name, CurSmtpClient,
- anynet_ntoa(&RealHostAddr));
- /* FALLTHROUGH */
-
- case CMDERROR: /* unknown command */
-#if MAXBADCOMMANDS > 0
- if (++n_badcmds > MAXBADCOMMANDS)
- {
- stopattack:
- 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;
- }
-#endif /* MAXBADCOMMANDS > 0 */
-
-#if MILTER && SMFI_VERSION > 2
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- if (MilterLogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "Sending \"%s\" to Milter", inp);
- response = milter_unknown(inp, e, &state);
- MILTER_REPLY("unknown");
- if (state == SMFIR_REPLYCODE ||
- state == SMFIR_REJECT ||
- state == SMFIR_TEMPFAIL ||
- state == SMFIR_SHUTDOWN)
- {
- /* MILTER_REPLY already gave an error */
- break;
- }
- }
-#endif /* MILTER && SMFI_VERSION > 2 */
-
- usrerr("500 5.5.1 Command unrecognized: \"%s\"",
- shortenstring(inp, MAXSHORTSTR));
- break;
-
- case CMDUNIMPL:
- DELAY_CONN("Unimpl");
- usrerr("502 5.5.1 Command not implemented: \"%s\"",
- shortenstring(inp, MAXSHORTSTR));
- break;
-
- default:
- DELAY_CONN("default");
- errno = 0;
- syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
- break;
- }
-#if SASL
- }
-#endif /* SASL */
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /*
- ** The only possible exception is "E:mta.quickabort".
- ** There is nothing to do except fall through and loop.
- */
- }
- SM_END_TRY
- }
-}
-/*
-** SMTP_DATA -- implement the SMTP DATA command.
-**
-** Parameters:
-** smtp -- status of SMTP connection.
-** e -- envelope.
-**
-** Returns:
-** true iff SMTP session can continue.
-**
-** Side Effects:
-** possibly sends message.
-*/
-
-static bool
-smtp_data(smtp, e)
- SMTP_T *smtp;
- ENVELOPE *e;
-{
-#if MILTER
- bool milteraccept;
-#endif /* MILTER */
- bool aborting;
- bool doublequeue;
- bool rv = true;
- ADDRESS *a;
- ENVELOPE *ee;
- char *id;
- char *oldid;
- unsigned int features;
- char buf[32];
-
- SmtpPhase = "server DATA";
- if (!smtp->sm_gotmail)
- {
- usrerr("503 5.0.0 Need MAIL command");
- return true;
- }
- else if (smtp->sm_nrcpts <= 0)
- {
- usrerr("503 5.0.0 Need RCPT (recipient)");
- return true;
- }
- (void) sm_snprintf(buf, sizeof(buf), "%u", smtp->sm_nrcpts);
- if (rscheck("check_data", buf, NULL, e,
- RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
- e->e_id, NULL) != EX_OK)
- return true;
-
-#if MILTER && SMFI_VERSION > 3
- if (smtp->sm_milterlist && smtp->sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
- int savelogusrerrs = LogUsrErrs;
-
- response = milter_data_cmd(e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- if (MilterLogLevel > 3)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, reject=%s",
- response);
- LogUsrErrs = false;
- }
- usrerr(response);
- if (strncmp(response, "421 ", 4) == 0
- || strncmp(response, "421-", 4) == 0)
- {
- e->e_sendqueue = NULL;
- return false;
- }
- return true;
-
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, reject=550 5.7.1 Command rejected");
- LogUsrErrs = false;
- }
- usrerr("550 5.7.1 Command rejected");
- return true;
-
- case SMFIR_DISCARD:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, discard");
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, reject=%s",
- MSG_TEMPFAIL);
- LogUsrErrs = false;
- }
- usrerr(MSG_TEMPFAIL);
- return true;
-
- case SMFIR_SHUTDOWN:
- if (MilterLogLevel > 3)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, reject=421 4.7.0 %s closing connection",
- MyHostName);
- LogUsrErrs = false;
- }
- usrerr("421 4.7.0 %s closing connection", MyHostName);
- e->e_sendqueue = NULL;
- return false;
- }
- LogUsrErrs = savelogusrerrs;
- if (response != NULL)
- sm_free(response); /* XXX */
- }
-#endif /* MILTER && SMFI_VERSION > 3 */
-
- /* put back discard bit */
- if (smtp->sm_discard)
- e->e_flags |= EF_DISCARD;
-
- /* check to see if we need to re-expand aliases */
- /* also reset QS_BADADDR on already-diagnosted addrs */
- doublequeue = false;
- for (a = e->e_sendqueue; a != NULL; a = a->q_next)
- {
- if (QS_IS_VERIFIED(a->q_state) &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- /* need to re-expand aliases */
- doublequeue = true;
- }
- if (QS_IS_BADADDR(a->q_state))
- {
- /* make this "go away" */
- a->q_state = QS_DONTSEND;
- }
- }
-
- /* collect the text of the message */
- SmtpPhase = "collect";
- buffer_errors();
-
- collect(InChannel, true, NULL, e, true);
-
- /* redefine message size */
- (void) sm_snprintf(buf, sizeof(buf), "%ld", e->e_msgsize);
- macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
-
- /* rscheck() will set Errors or EF_DISCARD if it trips */
- (void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT,
- 3, NULL, e->e_id, NULL);
-
-#if MILTER
- milteraccept = true;
- if (smtp->sm_milterlist && smtp->sm_milterize &&
- Errors <= 0 &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_data(e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, reject=%s",
- response);
- milteraccept = false;
- usrerr(response);
- break;
-
- case SMFIR_REJECT:
- milteraccept = false;
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, reject=554 5.7.1 Command rejected");
- usrerr("554 5.7.1 Command rejected");
- break;
-
- case SMFIR_DISCARD:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, discard");
- milteraccept = false;
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, reject=%s",
- MSG_TEMPFAIL);
- milteraccept = false;
- usrerr(MSG_TEMPFAIL);
- break;
-
- case SMFIR_SHUTDOWN:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, reject=421 4.7.0 %s closing connection",
- MyHostName);
- milteraccept = false;
- usrerr("421 4.7.0 %s closing connection", MyHostName);
- rv = false;
- break;
- }
- if (response != NULL)
- sm_free(response);
- }
-
- /* Milter may have changed message size */
- (void) sm_snprintf(buf, sizeof(buf), "%ld", e->e_msgsize);
- macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
-
- /* abort message filters that didn't get the body & log msg is OK */
- if (smtp->sm_milterlist && smtp->sm_milterize)
- {
- milter_abort(e);
- if (milteraccept && MilterLogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
- }
-
- /*
- ** If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
- ** milter accepted message, sync it now
- **
- ** XXX This is almost a copy of the code in collect(): put it into
- ** a function that is called from both places?
- */
-
- if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
- {
- int afd;
- SM_FILE_T *volatile df;
- char *dfname;
-
- df = e->e_dfp;
- dfname = queuename(e, DATAFL_LETTER);
- if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
- && errno != EINVAL)
- {
- int save_errno;
-
- save_errno = errno;
- if (save_errno == EEXIST)
- {
- struct stat st;
- int dfd;
-
- if (stat(dfname, &st) < 0)
- st.st_size = -1;
- errno = EEXIST;
- syserr("@collect: bfcommit(%s): already on disk, size=%ld",
- dfname, (long) st.st_size);
- dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
- if (dfd >= 0)
- dumpfd(dfd, true, true);
- }
- errno = save_errno;
- dferror(df, "bfcommit", e);
- flush_errors(true);
- finis(save_errno != EEXIST, true, ExitStat);
- }
- else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
- {
- dferror(df, "sm_io_getinfo", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- else if (fsync(afd) < 0)
- {
- dferror(df, "fsync", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
- {
- dferror(df, "sm_io_close", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
-
- /* Now reopen the df file */
- e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
- SM_IO_RDONLY, NULL);
- if (e->e_dfp == NULL)
- {
- /* we haven't acked receipt yet, so just chuck this */
- syserr("@Cannot reopen %s", dfname);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- }
-#endif /* MILTER */
-
- /* Check if quarantining stats should be updated */
- if (e->e_quarmsg != NULL)
- markstats(e, NULL, STATS_QUARANTINE);
-
- /*
- ** If a header/body check (header checks or milter)
- ** set EF_DISCARD, don't queueup the message --
- ** that would lose the EF_DISCARD bit and deliver
- ** the message.
- */
-
- if (bitset(EF_DISCARD, e->e_flags))
- doublequeue = false;
-
- aborting = Errors > 0;
- if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
- (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
- !split_by_recipient(e))
- aborting = bitset(EF_FATALERRS, e->e_flags);
-
- if (aborting)
- {
- ADDRESS *q;
-
- /* Log who the mail would have gone to */
- logundelrcpts(e, e->e_message, 8, false);
-
- /*
- ** If something above refused the message, we still haven't
- ** accepted responsibility for it. Don't send DSNs.
- */
-
- for (q = e->e_sendqueue; q != NULL; q = q->q_next)
- q->q_flags &= ~Q_PINGFLAGS;
-
- flush_errors(true);
- buffer_errors();
- goto abortmessage;
- }
-
- /* from now on, we have to operate silently */
- buffer_errors();
-
-#if 0
- /*
- ** Clear message, it may contain an error from the SMTP dialogue.
- ** This error must not show up in the queue.
- ** Some error message should show up, e.g., alias database
- ** not available, but others shouldn't, e.g., from check_rcpt.
- */
-
- e->e_message = NULL;
-#endif /* 0 */
-
- /*
- ** Arrange to send to everyone.
- ** If sending to multiple people, mail back
- ** errors rather than reporting directly.
- ** In any case, don't mail back errors for
- ** anything that has happened up to
- ** now (the other end will do this).
- ** Truncate our transcript -- the mail has gotten
- ** to us successfully, and if we have
- ** to mail this back, it will be easier
- ** on the reader.
- ** Then send to everyone.
- ** Finally give a reply code. If an error has
- ** already been given, don't mail a
- ** message back.
- ** We goose error returns by clearing error bit.
- */
-
- SmtpPhase = "delivery";
- (void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
- id = e->e_id;
-
-#if NAMED_BIND
- _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
- _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
-#endif /* NAMED_BIND */
-
- for (ee = e; ee != NULL; ee = ee->e_sibling)
- {
- /* make sure we actually do delivery */
- ee->e_flags &= ~EF_CLRQUEUE;
-
- /* from now on, operate silently */
- ee->e_errormode = EM_MAIL;
-
- if (doublequeue)
- {
- /* make sure it is in the queue */
- queueup(ee, false, true);
- }
- else
- {
- int mode;
-
- /* send to all recipients */
- mode = SM_DEFAULT;
-#if _FFR_DM_ONE
- if (SM_DM_ONE == e->e_sendmode)
- {
- if (NotFirstDelivery)
- {
- mode = SM_QUEUE;
- e->e_sendmode = SM_QUEUE;
- }
- else
- {
- mode = SM_FORK;
- NotFirstDelivery = true;
- }
- }
-#endif /* _FFR_DM_ONE */
- sendall(ee, mode);
- }
- ee->e_to = NULL;
- }
-
- /* put back id for SMTP logging in putoutmsg() */
- oldid = CurEnv->e_id;
- CurEnv->e_id = id;
-
- /* issue success message */
-#if _FFR_MSG_ACCEPT
- if (MessageAccept != NULL && *MessageAccept != '\0')
- {
- char msg[MAXLINE];
-
- expand(MessageAccept, msg, sizeof(msg), e);
- message("250 2.0.0 %s", msg);
- }
- else
-#endif /* _FFR_MSG_ACCEPT */
- message("250 2.0.0 %s Message accepted for delivery", id);
- CurEnv->e_id = oldid;
-
- /* if we just queued, poke it */
- if (doublequeue)
- {
- bool anything_to_send = false;
-
- sm_getla();
- for (ee = e; ee != NULL; ee = ee->e_sibling)
- {
- if (WILL_BE_QUEUED(ee->e_sendmode))
- continue;
- if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
- {
- ee->e_sendmode = SM_QUEUE;
- continue;
- }
- else if (QueueMode != QM_QUARANTINE &&
- ee->e_quarmsg != NULL)
- {
- ee->e_sendmode = SM_QUEUE;
- continue;
- }
- anything_to_send = true;
-
- /* close all the queue files */
- closexscript(ee);
- if (ee->e_dfp != NULL)
- {
- (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
- ee->e_dfp = NULL;
- }
- unlockqueue(ee);
- }
- if (anything_to_send)
- {
-#if PIPELINING
- /*
- ** XXX if we don't do this, we get 250 twice
- ** because it is also flushed in the child.
- */
-
- (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-#endif /* PIPELINING */
- (void) doworklist(e, true, true);
- }
- }
-
- abortmessage:
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- /* clean up a bit */
- smtp->sm_gotmail = false;
-
- /*
- ** Call dropenvelope if and only if the envelope is *not*
- ** being processed by the child process forked by doworklist().
- */
-
- if (aborting || bitset(EF_DISCARD, e->e_flags))
- dropenvelope(e, true, false);
- else
- {
- for (ee = e; ee != NULL; ee = ee->e_sibling)
- {
- if (!doublequeue &&
- QueueMode != QM_QUARANTINE &&
- ee->e_quarmsg != NULL)
- {
- dropenvelope(ee, true, false);
- continue;
- }
- if (WILL_BE_QUEUED(ee->e_sendmode))
- dropenvelope(ee, true, false);
- }
- }
- sm_rpool_free(e->e_rpool);
-
- /*
- ** At this point, e == &MainEnvelope, but if we did splitting,
- ** then CurEnv may point to an envelope structure that was just
- ** freed with the rpool. So reset CurEnv *before* calling
- ** newenvelope.
- */
-
- CurEnv = e;
- features = e->e_features;
- newenvelope(e, e, sm_rpool_new_x(NULL));
- e->e_flags = BlankEnvelope.e_flags;
- e->e_features = features;
-
- /* restore connection quarantining */
- if (smtp->sm_quarmsg == NULL)
- {
- e->e_quarmsg = NULL;
- macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
- }
- else
- {
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), e->e_quarmsg);
- }
- return rv;
-}
-/*
-** LOGUNDELRCPTS -- log undelivered (or all) recipients.
-**
-** Parameters:
-** e -- envelope.
-** msg -- message for Stat=
-** level -- log level.
-** all -- log all recipients.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** logs undelivered (or all) recipients
-*/
-
-void
-logundelrcpts(e, msg, level, all)
- ENVELOPE *e;
- char *msg;
- int level;
- bool all;
-{
- ADDRESS *a;
-
- if (LogLevel <= level || msg == NULL || *msg == '\0')
- return;
-
- /* Clear $h so relay= doesn't get mislogged by logdelivery() */
- macdefine(&e->e_macro, A_PERM, 'h', NULL);
-
- /* Log who the mail would have gone to */
- for (a = e->e_sendqueue; a != NULL; a = a->q_next)
- {
- if (!QS_IS_UNDELIVERED(a->q_state) && !all)
- continue;
- e->e_to = a->q_paddr;
- logdelivery(NULL, NULL, a->q_status, msg, NULL,
- (time_t) 0, e);
- }
- e->e_to = NULL;
-}
-/*
-** CHECKSMTPATTACK -- check for denial-of-service attack by repetition
-**
-** Parameters:
-** 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.
-**
-** Returns:
-** time to wait,
-** STOP_ATTACK if twice as many commands as allowed and
-** MaxChildren > 0.
-**
-** Side Effects:
-** Slows down if we seem to be under attack.
-*/
-
-static time_t
-checksmtpattack(pcounter, maxcount, waitnow, cname, e)
- volatile unsigned int *pcounter;
- unsigned int maxcount;
- bool waitnow;
- char *cname;
- ENVELOPE *e;
-{
- if (maxcount <= 0) /* no limit */
- return (time_t) 0;
-
- if (++(*pcounter) >= maxcount)
- {
- unsigned int shift;
- time_t s;
-
- if (*pcounter == maxcount && LogLevel > 5)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "%s: possible SMTP attack: command=%.40s, count=%u",
- CurSmtpClient, cname, *pcounter);
- }
- shift = *pcounter - maxcount;
- s = 1 << shift;
- if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
- s = MAXTIMEOUT;
-
-#define IS_ATTACK(s) ((MaxChildren > 0 && *pcounter >= maxcount * 2) \
- ? STOP_ATTACK : (time_t) s)
-
- /* sleep at least 1 second before returning */
- (void) sleep(*pcounter / maxcount);
- s -= *pcounter / maxcount;
- if (s >= MAXTIMEOUT || s < 0)
- s = MAXTIMEOUT;
- if (waitnow && s > 0)
- {
- (void) sleep(s);
- return IS_ATTACK(0);
- }
- return IS_ATTACK(s);
- }
- return (time_t) 0;
-}
-/*
-** SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
-**
-** Parameters:
-** none.
-**
-** Returns:
-** nothing.
-**
-** Side Effects:
-** may change I/O fd.
-*/
-
-static void
-setup_smtpd_io()
-{
- int inchfd, outchfd, outfd;
-
- inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
- outchfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
- outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
- if (outchfd != outfd)
- {
- /* arrange for debugging output to go to remote host */
- (void) dup2(outchfd, outfd);
- }
-
- /*
- ** if InChannel and OutChannel are stdin/stdout
- ** and connected to ttys
- ** and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
- ** then "chain" them together.
- */
-
- if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
- isatty(inchfd) && isatty(outchfd))
- {
- int inmode, outmode;
-
- inmode = fcntl(inchfd, F_GETFL, 0);
- if (inmode == -1)
- {
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, NOQID,
- "fcntl(inchfd, F_GETFL) failed: %s",
- sm_errstring(errno));
- return;
- }
- outmode = fcntl(outchfd, F_GETFL, 0);
- if (outmode == -1)
- {
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, NOQID,
- "fcntl(outchfd, F_GETFL) failed: %s",
- sm_errstring(errno));
- return;
- }
- if (bitset(O_NONBLOCK, inmode) ||
- bitset(O_NONBLOCK, outmode) ||
- fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
- return;
- outmode = fcntl(outchfd, F_GETFL, 0);
- if (outmode != -1 && bitset(O_NONBLOCK, outmode))
- {
- /* changing InChannel also changes OutChannel */
- sm_io_automode(OutChannel, InChannel);
- if (tTd(97, 4) && LogLevel > 9)
- sm_syslog(LOG_INFO, NOQID,
- "set automode for I (%d)/O (%d) in SMTP server",
- inchfd, outchfd);
- }
-
- /* undo change of inchfd */
- (void) fcntl(inchfd, F_SETFL, inmode);
- }
-}
-/*
-** SKIPWORD -- skip a fixed word.
-**
-** Parameters:
-** p -- place to start looking.
-** w -- word to skip.
-**
-** Returns:
-** p following w.
-** NULL on error.
-**
-** Side Effects:
-** clobbers the p data area.
-*/
-
-static char *
-skipword(p, w)
- register char *volatile p;
- char *w;
-{
- register char *q;
- char *firstp = p;
-
- /* find beginning of word */
- SKIP_SPACE(p);
- q = p;
-
- /* find end of word */
- while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
- p++;
- while (isascii(*p) && isspace(*p))
- *p++ = '\0';
- if (*p != ':')
- {
- syntax:
- usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
- shortenstring(firstp, MAXSHORTSTR));
- return NULL;
- }
- *p++ = '\0';
- SKIP_SPACE(p);
-
- if (*p == '\0')
- goto syntax;
-
- /* see if the input word matches desired word */
- if (sm_strcasecmp(q, w))
- goto syntax;
-
- return p;
-}
-
-/*
-** RESET_MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
-**
-** Parameters:
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-void
-reset_mail_esmtp_args(e)
- ENVELOPE *e;
-{
- /* "size": no reset */
-
- /* "body" */
- SevenBitInput = SevenBitInput_Saved;
- e->e_bodytype = NULL;
-
- /* "envid" */
- e->e_envid = NULL;
- macdefine(&e->e_macro, A_PERM, macid("{dsn_envid}"), NULL);
-
- /* "ret" */
- e->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
- macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), NULL);
-
-#if SASL
- /* "auth" */
- macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), NULL);
- e->e_auth_param = "";
-# if _FFR_AUTH_PASSING
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{auth_author}"), NULL);
-# endif /* _FFR_AUTH_PASSING */
-#endif /* SASL */
-
- /* "by" */
- e->e_deliver_by = 0;
- e->e_dlvr_flag = 0;
-}
-
-/*
-** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
-**
-** Parameters:
-** a -- address (unused, for compatibility with rcpt_esmtp_args)
-** kp -- the parameter key.
-** vp -- the value of that parameter.
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-void
-mail_esmtp_args(a, kp, vp, e)
- ADDRESS *a;
- char *kp;
- char *vp;
- ENVELOPE *e;
-{
- if (sm_strcasecmp(kp, "size") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 5.5.2 SIZE requires a value");
- /* NOTREACHED */
- }
- macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
- errno = 0;
- e->e_msgsize = strtol(vp, (char **) NULL, 10);
- if (e->e_msgsize == LONG_MAX && errno == ERANGE)
- {
- usrerr("552 5.2.3 Message size exceeds maximum value");
- /* NOTREACHED */
- }
- if (e->e_msgsize < 0)
- {
- usrerr("552 5.2.3 Message size invalid");
- /* NOTREACHED */
- }
- }
- else if (sm_strcasecmp(kp, "body") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 5.5.2 BODY requires a value");
- /* NOTREACHED */
- }
- else if (sm_strcasecmp(vp, "8bitmime") == 0)
- {
- SevenBitInput = false;
- }
- else if (sm_strcasecmp(vp, "7bit") == 0)
- {
- SevenBitInput = true;
- }
- else
- {
- usrerr("501 5.5.4 Unknown BODY type %s", vp);
- /* NOTREACHED */
- }
- e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
- }
- else if (sm_strcasecmp(kp, "envid") == 0)
- {
- if (!bitset(SRV_OFFER_DSN, e->e_features))
- {
- usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 ENVID requires a value");
- /* NOTREACHED */
- }
- if (!xtextok(vp))
- {
- usrerr("501 5.5.4 Syntax error in ENVID parameter value");
- /* NOTREACHED */
- }
- if (e->e_envid != NULL)
- {
- usrerr("501 5.5.0 Duplicate ENVID parameter");
- /* NOTREACHED */
- }
- e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_envid}"), e->e_envid);
- }
- else if (sm_strcasecmp(kp, "ret") == 0)
- {
- if (!bitset(SRV_OFFER_DSN, e->e_features))
- {
- usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 RET requires a value");
- /* NOTREACHED */
- }
- if (bitset(EF_RET_PARAM, e->e_flags))
- {
- usrerr("501 5.5.0 Duplicate RET parameter");
- /* NOTREACHED */
- }
- e->e_flags |= EF_RET_PARAM;
- if (sm_strcasecmp(vp, "hdrs") == 0)
- e->e_flags |= EF_NO_BODY_RETN;
- else if (sm_strcasecmp(vp, "full") != 0)
- {
- usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
- /* NOTREACHED */
- }
- macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
- }
-#if SASL
- else if (sm_strcasecmp(kp, "auth") == 0)
- {
- int len;
- char *q;
- char *auth_param; /* the value of the AUTH=x */
- bool saveQuickAbort = QuickAbort;
- bool saveSuprErrs = SuprErrs;
- bool saveExitStat = ExitStat;
-
- 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) sm_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 define this always or only if trusted? */
- macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
- auth_param);
-
- /*
- ** 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", auth_param, NULL, e, RSF_RMCOMM,
- 9, NULL, NOQID, NULL) != EX_OK || Errors > 0))
- {
- if (tTd(95, 8))
- {
- q = e->e_auth_param;
- sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
- auth_param, (q == NULL) ? "" : q);
- }
-
- /* not trusted */
- e->e_auth_param = "<>";
-# if _FFR_AUTH_PASSING
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{auth_author}"), NULL);
-# endif /* _FFR_AUTH_PASSING */
- }
- else
- {
- if (tTd(95, 8))
- sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
- e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
- auth_param);
- }
- sm_free(auth_param); /* XXX */
-
- /* reset values */
- Errors = 0;
- QuickAbort = saveQuickAbort;
- SuprErrs = saveSuprErrs;
- ExitStat = saveExitStat;
- }
-#endif /* SASL */
-#define PRTCHAR(c) ((isascii(c) && isprint(c)) ? (c) : '?')
-
- /*
- ** "by" is only accepted if DeliverByMin >= 0.
- ** We maybe could add this to the list of server_features.
- */
-
- else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
- {
- char *s;
-
- if (vp == NULL)
- {
- usrerr("501 5.5.2 BY= requires a value");
- /* NOTREACHED */
- }
- errno = 0;
- e->e_deliver_by = strtol(vp, &s, 10);
- if (e->e_deliver_by == LONG_MIN ||
- e->e_deliver_by == LONG_MAX ||
- e->e_deliver_by > 999999999l ||
- e->e_deliver_by < -999999999l)
- {
- usrerr("501 5.5.2 BY=%s out of range", vp);
- /* NOTREACHED */
- }
- if (s == NULL || *s != ';')
- {
- usrerr("501 5.5.2 BY= missing ';'");
- /* NOTREACHED */
- }
- e->e_dlvr_flag = 0;
- ++s; /* XXX: spaces allowed? */
- SKIP_SPACE(s);
- switch (tolower(*s))
- {
- case 'n':
- e->e_dlvr_flag = DLVR_NOTIFY;
- break;
- case 'r':
- e->e_dlvr_flag = DLVR_RETURN;
- if (e->e_deliver_by <= 0)
- {
- usrerr("501 5.5.4 mode R requires BY time > 0");
- /* NOTREACHED */
- }
- if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
- e->e_deliver_by < DeliverByMin)
- {
- usrerr("555 5.5.2 time %ld less than %ld",
- e->e_deliver_by, (long) DeliverByMin);
- /* NOTREACHED */
- }
- break;
- default:
- usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
- /* NOTREACHED */
- }
- ++s; /* XXX: spaces allowed? */
- SKIP_SPACE(s);
- switch (tolower(*s))
- {
- case 't':
- e->e_dlvr_flag |= DLVR_TRACE;
- break;
- case '\0':
- break;
- default:
- usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
- /* NOTREACHED */
- }
-
- /* XXX: check whether more characters follow? */
- }
- else
- {
- usrerr("555 5.5.4 %s parameter unrecognized", kp);
- /* NOTREACHED */
- }
-}
-
-/*
-** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
-**
-** Parameters:
-** a -- the address corresponding to the To: parameter.
-** kp -- the parameter key.
-** vp -- the value of that parameter.
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-void
-rcpt_esmtp_args(a, kp, vp, e)
- ADDRESS *a;
- char *kp;
- char *vp;
- ENVELOPE *e;
-{
- if (sm_strcasecmp(kp, "notify") == 0)
- {
- char *p;
-
- if (!bitset(SRV_OFFER_DSN, e->e_features))
- {
- usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 NOTIFY requires a value");
- /* NOTREACHED */
- }
- a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
- a->q_flags |= QHASNOTIFY;
- macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
-
- if (sm_strcasecmp(vp, "never") == 0)
- return;
- for (p = vp; p != NULL; vp = p)
- {
- char *s;
-
- s = p = strchr(p, ',');
- if (p != NULL)
- *p++ = '\0';
- if (sm_strcasecmp(vp, "success") == 0)
- a->q_flags |= QPINGONSUCCESS;
- else if (sm_strcasecmp(vp, "failure") == 0)
- a->q_flags |= QPINGONFAILURE;
- else if (sm_strcasecmp(vp, "delay") == 0)
- a->q_flags |= QPINGONDELAY;
- else
- {
- usrerr("501 5.5.4 Bad argument \"%s\" to NOTIFY",
- vp);
- /* NOTREACHED */
- }
- if (s != NULL)
- *s = ',';
- }
- }
- else if (sm_strcasecmp(kp, "orcpt") == 0)
- {
- if (!bitset(SRV_OFFER_DSN, e->e_features))
- {
- usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 ORCPT requires a value");
- /* NOTREACHED */
- }
- if (strchr(vp, ';') == NULL || !xtextok(vp))
- {
- usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
- /* NOTREACHED */
- }
- if (a->q_orcpt != NULL)
- {
- usrerr("501 5.5.0 Duplicate ORCPT parameter");
- /* NOTREACHED */
- }
- a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
- }
- else
- {
- usrerr("555 5.5.4 %s parameter unrecognized", kp);
- /* NOTREACHED */
- }
-}
-/*
-** PRINTVRFYADDR -- print an entry in the verify queue
-**
-** Parameters:
-** a -- the address to print.
-** last -- set if this is the last one.
-** vrfy -- set if this is a VRFY command.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Prints the appropriate 250 codes.
-*/
-#define OFFF (3 + 1 + 5 + 1) /* offset in fmt: SMTP reply + enh. code */
-
-static void
-printvrfyaddr(a, last, vrfy)
- register ADDRESS *a;
- bool last;
- bool vrfy;
-{
- char fmtbuf[30];
-
- if (vrfy && a->q_mailer != NULL &&
- !bitnset(M_VRFY250, a->q_mailer->m_flags))
- (void) sm_strlcpy(fmtbuf, "252", sizeof(fmtbuf));
- else
- (void) sm_strlcpy(fmtbuf, "250", sizeof(fmtbuf));
- fmtbuf[3] = last ? ' ' : '-';
- (void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof(fmtbuf) - 4);
- if (a->q_fullname == NULL)
- {
- if ((a->q_mailer == NULL ||
- a->q_mailer->m_addrtype == NULL ||
- sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
- strchr(a->q_user, '@') == NULL)
- (void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
- sizeof(fmtbuf) - OFFF);
- else
- (void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
- sizeof(fmtbuf) - OFFF);
- message(fmtbuf, a->q_user, MyHostName);
- }
- else
- {
- if ((a->q_mailer == NULL ||
- a->q_mailer->m_addrtype == NULL ||
- sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
- strchr(a->q_user, '@') == NULL)
- (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
- sizeof(fmtbuf) - OFFF);
- else
- (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
- sizeof(fmtbuf) - OFFF);
- message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
- }
-}
-
-#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 */
-# if SASL >= 20000
- result = sasl_listmech(conn, NULL,
- "", " ", "", (const char **) mechlist,
- (unsigned int *)&len, &num);
-# else /* SASL >= 20000 */
- result = sasl_listmech(conn, "user", /* XXX */
- "", " ", "", mechlist,
- (unsigned int *)&len, (unsigned int *)&num);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH error: listmech=%d, num=%d",
- result, num);
- num = 0;
- }
- if (num > 0)
- {
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, NOQID,
- "AUTH: available mech=%s, allowed mech=%s",
- *mechlist, AuthMechanisms);
- *mechlist = intersect(AuthMechanisms, *mechlist, NULL);
- }
- else
- {
- *mechlist = NULL; /* be paranoid... */
- if (result == SASL_OK && LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH warning: no mechanisms");
- }
- return num;
-}
-
-# if SASL >= 20000
-/*
-** PROXY_POLICY -- define proxy policy for AUTH
-**
-** Parameters:
-** conn -- unused.
-** context -- unused.
-** requested_user -- authorization identity.
-** rlen -- authorization identity length.
-** auth_identity -- authentication identity.
-** alen -- authentication identity length.
-** def_realm -- default user realm.
-** urlen -- user realm length.
-** propctx -- unused.
-**
-** Returns:
-** ok?
-**
-** Side Effects:
-** sets {auth_authen} macro.
-*/
-
-int
-proxy_policy(conn, context, requested_user, rlen, auth_identity, alen,
- def_realm, urlen, propctx)
- sasl_conn_t *conn;
- void *context;
- const char *requested_user;
- unsigned rlen;
- const char *auth_identity;
- unsigned alen;
- const char *def_realm;
- unsigned urlen;
- struct propctx *propctx;
-{
- if (auth_identity == NULL)
- return SASL_FAIL;
-
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{auth_authen}"), (char *) auth_identity);
-
- return SASL_OK;
-}
-# else /* SASL >= 20000 */
-
-/*
-** PROXY_POLICY -- define proxy policy for AUTH
-**
-** Parameters:
-** context -- 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 >= 20000 */
-#endif /* SASL */
-
-#if STARTTLS
-/*
-** INITSRVTLS -- initialize server side TLS
-**
-** Parameters:
-** tls_ok -- should tls initialization be done?
-**
-** Returns:
-** succeeded?
-**
-** Side Effects:
-** sets tls_ok_srv which is a static variable in this module.
-** Do NOT remove assignments to it!
-*/
-
-bool
-initsrvtls(tls_ok)
- bool tls_ok;
-{
- if (!tls_ok)
- return false;
-
- /* do NOT remove assignment */
- tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCertFile,
- SrvKeyFile, CACertPath, CACertFile, DHParams);
- return tls_ok_srv;
-}
-#endif /* STARTTLS */
-/*
-** SRVFEATURES -- get features for SMTP server
-**
-** Parameters:
-** e -- envelope (should be session context).
-** clientname -- name of client.
-** features -- default features for this invocation.
-**
-** Returns:
-** server features.
-*/
-
-/* table with options: it uses just one character, how about strings? */
-static struct
-{
- char srvf_opt;
- unsigned int srvf_flag;
-} srv_feat_table[] =
-{
- { 'A', SRV_OFFER_AUTH },
- { 'B', SRV_OFFER_VERB },
- { 'C', SRV_REQ_SEC },
- { 'D', SRV_OFFER_DSN },
- { 'E', SRV_OFFER_ETRN },
- { 'L', SRV_REQ_AUTH },
-#if PIPELINING
-# if _FFR_NO_PIPE
- { 'N', SRV_NO_PIPE },
-# endif /* _FFR_NO_PIPE */
- { 'P', SRV_OFFER_PIPE },
-#endif /* PIPELINING */
- { 'R', SRV_VRFY_CLT }, /* same as V; not documented */
- { 'S', SRV_OFFER_TLS },
-/* { 'T', SRV_TMP_FAIL }, */
- { 'V', SRV_VRFY_CLT },
- { 'X', SRV_OFFER_EXPN },
-/* { 'Y', SRV_OFFER_VRFY }, */
- { '\0', SRV_NONE }
-};
-
-static unsigned int
-srvfeatures(e, clientname, features)
- ENVELOPE *e;
- char *clientname;
- unsigned int features;
-{
- int r, i, j;
- char **pvp, c, opt;
- char pvpbuf[PSBUFSIZE];
-
- pvp = NULL;
- r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
- sizeof(pvpbuf));
- if (r != EX_OK)
- return features;
- if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
- return features;
- if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
- return SRV_TMP_FAIL;
-
- /*
- ** General rule (see sendmail.h, d_flags):
- ** lower case: required/offered, upper case: Not required/available
- **
- ** Since we can change some features per daemon, we have both
- ** cases here: turn on/off a feature.
- */
-
- for (i = 1; pvp[i] != NULL; i++)
- {
- c = pvp[i][0];
- j = 0;
- for (;;)
- {
- if ((opt = srv_feat_table[j].srvf_opt) == '\0')
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "srvfeatures: unknown feature %s",
- pvp[i]);
- break;
- }
- if (c == opt)
- {
- features &= ~(srv_feat_table[j].srvf_flag);
- break;
- }
- if (c == tolower(opt))
- {
- features |= srv_feat_table[j].srvf_flag;
- break;
- }
- ++j;
- }
- }
- return features;
-}
-
-/*
-** HELP -- implement the HELP command.
-**
-** Parameters:
-** topic -- the topic we want help for.
-** e -- envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** outputs the help file to message output.
-*/
-#define HELPVSTR "#vers "
-#define HELPVERSION 2
-
-void
-help(topic, e)
- char *topic;
- ENVELOPE *e;
-{
- register SM_FILE_T *hf;
- register char *p;
- int len;
- bool noinfo;
- 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 (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
- sff |= SFF_SAFEDIRPATH;
-
- if (HelpFile == NULL ||
- (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
- {
- /* no help */
- errno = 0;
- message("502 5.3.0 Sendmail %s -- HELP not implemented",
- Version);
- return;
- }
-
- if (topic == NULL || *topic == '\0')
- {
- topic = "smtp";
- noinfo = false;
- }
- else
- {
- makelower(topic);
- noinfo = true;
- }
-
- len = strlen(topic);
-
- while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
- {
- if (buf[0] == '#')
- {
- if (foundvers < 0 &&
- strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
- {
- int h;
-
- if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
- &h) == 1)
- foundvers = h;
- }
- continue;
- }
- if (strncmp(buf, topic, len) == 0)
- {
- if (first)
- {
- first = false;
-
- /* 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 + strlen(buf) - 1;
- else
- p++;
- fixcrlf(p, true);
- if (foundvers >= 2)
- {
- char *lbp;
- int lbs = sizeof(buf) - (p - buf);
-
- lbp = translate_dollars(p, p, &lbs);
- expand(lbp, inp, sizeof(inp), e);
- if (p != lbp)
- sm_free(lbp);
- p = inp;
- }
- message("214-2.0.0 %s", p);
- noinfo = false;
- }
- }
-
- if (noinfo)
- message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
- else
- 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) sm_io_close(hf, SM_TIME_DEFAULT);
-}
-
-#if SASL
-/*
-** RESET_SASLCONN -- reset SASL connection data
-**
-** Parameters:
-** conn -- SASL connection context
-** hostname -- host name
-** various connection data
-**
-** Returns:
-** SASL result
-*/
-
-static int
-reset_saslconn(sasl_conn_t **conn, char *hostname,
-# if SASL >= 20000
- char *remoteip, char *localip,
- char *auth_id, sasl_ssf_t * ext_ssf)
-# else /* SASL >= 20000 */
- struct sockaddr_in *saddr_r, struct sockaddr_in *saddr_l,
- sasl_external_properties_t * ext_ssf)
-# endif /* SASL >= 20000 */
-{
- int result;
-
- sasl_dispose(conn);
-# if SASL >= 20000
- result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
- NULL, 0, conn);
-# elif SASL > 10505
- /* use empty realm: only works in SASL > 1.5.5 */
- result = sasl_server_new("smtp", hostname, "", NULL, 0, conn);
-# else /* SASL >= 20000 */
- /* use no realm -> realm is set to hostname by SASL lib */
- result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
- conn);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- return result;
-
-# if SASL >= 20000
-# if NETINET || NETINET6
- if (remoteip != NULL && *remoteip != '\0')
- result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip);
- if (result != SASL_OK)
- return result;
-
- if (localip != NULL && *localip != '\0')
- result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip);
- if (result != SASL_OK)
- return result;
-# endif /* NETINET || NETINET6 */
-
- result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
- if (result != SASL_OK)
- return result;
-
- result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id);
- if (result != SASL_OK)
- return result;
-# else /* SASL >= 20000 */
-# if NETINET
- if (saddr_r != NULL)
- result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r);
- if (result != SASL_OK)
- return result;
-
- if (saddr_l != NULL)
- result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l);
- if (result != SASL_OK)
- return result;
-# endif /* NETINET */
-
- result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
- if (result != SASL_OK)
- return result;
-# endif /* SASL >= 20000 */
- return SASL_OK;
-}
-#endif /* SASL */
diff --git a/contrib/sendmail/src/stab.c b/contrib/sendmail/src/stab.c
deleted file mode 100644
index 6dacdfa..0000000
--- a/contrib/sendmail/src/stab.c
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Copyright (c) 1998-2001, 2003 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: stab.c,v 8.89 2006/08/15 23:24:58 ca Exp $")
-
-/*
-** STAB -- manage the symbol table
-**
-** Parameters:
-** name -- the name to be looked up or inserted.
-** type -- the type of symbol.
-** op -- what to do:
-** ST_ENTER -- enter the name if not already present.
-** ST_FIND -- find it only.
-**
-** Returns:
-** pointer to a STAB entry for this name.
-** NULL if not found and not entered.
-**
-** Side Effects:
-** can update the symbol table.
-*/
-
-#define STABSIZE 2003
-#define SM_LOWER(c) ((isascii(c) && isupper(c)) ? tolower(c) : (c))
-
-static STAB *SymTab[STABSIZE];
-
-STAB *
-stab(name, type, op)
- char *name;
- int type;
- int op;
-{
- register STAB *s;
- register STAB **ps;
- register int hfunc;
- register char *p;
- int len;
-
- if (tTd(36, 5))
- sm_dprintf("STAB: %s %d ", name, type);
-
- /*
- ** Compute the hashing function
- */
-
- hfunc = type;
- for (p = name; *p != '\0'; p++)
- hfunc = ((hfunc << 1) ^ (SM_LOWER(*p) & 0377)) % STABSIZE;
-
- if (tTd(36, 9))
- sm_dprintf("(hfunc=%d) ", hfunc);
-
- ps = &SymTab[hfunc];
- if (type == ST_MACRO || type == ST_RULESET)
- {
- while ((s = *ps) != NULL &&
- (s->s_symtype != type || strcmp(name, s->s_name)))
- ps = &s->s_next;
- }
- else
- {
- while ((s = *ps) != NULL &&
- (s->s_symtype != type || sm_strcasecmp(name, s->s_name)))
- ps = &s->s_next;
- }
-
- /*
- ** Dispose of the entry.
- */
-
- if (s != NULL || op == ST_FIND)
- {
- if (tTd(36, 5))
- {
- if (s == NULL)
- sm_dprintf("not found\n");
- else
- {
- long *lp = (long *) s->s_class;
-
- sm_dprintf("type %d val %lx %lx %lx %lx\n",
- s->s_symtype, lp[0], lp[1], lp[2], lp[3]);
- }
- }
- return s;
- }
-
- /*
- ** Make a new entry and link it in.
- */
-
- if (tTd(36, 5))
- sm_dprintf("entered\n");
-
- /* determine size of new entry */
- switch (type)
- {
- case ST_CLASS:
- len = sizeof(s->s_class);
- break;
-
- case ST_ADDRESS:
- len = sizeof(s->s_address);
- break;
-
- case ST_MAILER:
- len = sizeof(s->s_mailer);
- break;
-
- case ST_ALIAS:
- len = sizeof(s->s_alias);
- break;
-
- case ST_MAPCLASS:
- len = sizeof(s->s_mapclass);
- break;
-
- case ST_MAP:
- len = sizeof(s->s_map);
- break;
-
- case ST_HOSTSIG:
- len = sizeof(s->s_hostsig);
- break;
-
- case ST_NAMECANON:
- len = sizeof(s->s_namecanon);
- break;
-
- case ST_MACRO:
- len = sizeof(s->s_macro);
- break;
-
- case ST_RULESET:
- len = sizeof(s->s_ruleset);
- break;
-
- case ST_HEADER:
- len = sizeof(s->s_header);
- break;
-
- case ST_SERVICE:
- len = sizeof(s->s_service);
- break;
-
-#if LDAPMAP
- case ST_LMAP:
- len = sizeof(s->s_lmap);
- break;
-#endif /* LDAPMAP */
-
-#if MILTER
- case ST_MILTER:
- len = sizeof(s->s_milter);
- break;
-#endif /* MILTER */
-
- case ST_QUEUE:
- len = sizeof(s->s_quegrp);
- break;
-
-#if SOCKETMAP
- case ST_SOCKETMAP:
- len = sizeof(s->s_socketmap);
- break;
-#endif /* SOCKETMAP */
-
- default:
- /*
- ** Each mailer has its 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
- {
- syserr("stab: unknown symbol type %d", type);
- len = sizeof(s->s_value);
- }
- break;
- }
- len += sizeof(*s) - sizeof(s->s_value);
-
- if (tTd(36, 15))
- sm_dprintf("size of stab entry: %d\n", len);
-
- /* make new entry */
- s = (STAB *) sm_pmalloc_x(len);
- memset((char *) s, '\0', len);
- s->s_name = sm_pstrdup_x(name);
- s->s_symtype = type;
-
- /* link it in */
- *ps = s;
-
- /* set a default value for rulesets */
- if (type == ST_RULESET)
- s->s_ruleset = -1;
-
- return s;
-}
-/*
-** STABAPPLY -- apply function to all stab entries
-**
-** Parameters:
-** func -- the function to apply. It will be given two
-** parameters (the stab entry and the arg).
-** arg -- an arbitrary argument, passed to func.
-**
-** Returns:
-** none.
-*/
-
-void
-stabapply(func, arg)
- void (*func)__P((STAB *, int));
- int arg;
-{
- register STAB **shead;
- register STAB *s;
-
- for (shead = SymTab; shead < &SymTab[STABSIZE]; shead++)
- {
- for (s = *shead; s != NULL; s = s->s_next)
- {
- if (tTd(36, 90))
- sm_dprintf("stabapply: trying %d/%s\n",
- s->s_symtype, s->s_name);
- func(s, arg);
- }
- }
-}
-/*
-** QUEUEUP_MACROS -- queueup the macros in a class
-**
-** Write the macros listed in the specified class into the
-** file referenced by qfp.
-**
-** Parameters:
-** class -- class ID.
-** qfp -- file pointer to the queue file.
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-void
-queueup_macros(class, qfp, e)
- int class;
- SM_FILE_T *qfp;
- ENVELOPE *e;
-{
- register STAB **shead;
- register STAB *s;
-
- if (e == NULL)
- return;
-
- class = bitidx(class);
- for (shead = SymTab; shead < &SymTab[STABSIZE]; shead++)
- {
- for (s = *shead; s != NULL; s = s->s_next)
- {
- int m;
- char *p;
-
- if (s->s_symtype == ST_CLASS &&
- bitnset(bitidx(class), s->s_class) &&
- (m = macid(s->s_name)) != 0 &&
- (p = macvalue(m, e)) != NULL)
- {
- (void) sm_io_fprintf(qfp, SM_TIME_DEFAULT,
- "$%s%s\n",
- s->s_name,
- denlstring(p, true,
- false));
- }
- }
- }
-}
-/*
-** COPY_CLASS -- copy class members from one class to another
-**
-** Parameters:
-** src -- source class.
-** dst -- destination class.
-**
-** Returns:
-** none.
-*/
-
-void
-copy_class(src, dst)
- int src;
- int dst;
-{
- register STAB **shead;
- register STAB *s;
-
- src = bitidx(src);
- dst = bitidx(dst);
- for (shead = SymTab; shead < &SymTab[STABSIZE]; shead++)
- {
- for (s = *shead; s != NULL; s = s->s_next)
- {
- if (s->s_symtype == ST_CLASS &&
- bitnset(src, s->s_class))
- setbitn(dst, s->s_class);
- }
- }
-}
-
-/*
-** RMEXPSTAB -- remove expired entries from SymTab.
-**
-** These entries need to be removed in long-running processes,
-** e.g., persistent queue runners, to avoid consuming memory.
-**
-** XXX It might be useful to restrict the maximum TTL to avoid
-** caching data very long.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** can remove entries from the symbol table.
-*/
-
-#define SM_STAB_FREE(x) \
- do \
- { \
- char *o = (x); \
- (x) = NULL; \
- if (o != NULL) \
- sm_free(o); \
- } while (0)
-
-void
-rmexpstab()
-{
- int i;
- STAB *s, *p, *f;
- time_t now;
-
- now = curtime();
- for (i = 0; i < STABSIZE; i++)
- {
- p = NULL;
- s = SymTab[i];
- while (s != NULL)
- {
- switch (s->s_symtype)
- {
- case ST_HOSTSIG:
- if (s->s_hostsig.hs_exp >= now)
- goto next; /* not expired */
- SM_STAB_FREE(s->s_hostsig.hs_sig); /* XXX */
- break;
-
- case ST_NAMECANON:
- if (s->s_namecanon.nc_exp >= now)
- goto next; /* not expired */
- SM_STAB_FREE(s->s_namecanon.nc_cname); /* XXX */
- break;
-
- default:
- if (s->s_symtype >= ST_MCI)
- {
- /* call mci_uncache? */
- SM_STAB_FREE(s->s_mci.mci_status);
- SM_STAB_FREE(s->s_mci.mci_rstatus);
- SM_STAB_FREE(s->s_mci.mci_heloname);
-#if 0
- /* not dynamically allocated */
- SM_STAB_FREE(s->s_mci.mci_host);
- SM_STAB_FREE(s->s_mci.mci_tolist);
-#endif /* 0 */
-#if SASL
- /* should always by NULL */
- SM_STAB_FREE(s->s_mci.mci_sasl_string);
-#endif /* SASL */
- if (s->s_mci.mci_rpool != NULL)
- {
- sm_rpool_free(s->s_mci.mci_rpool);
- s->s_mci.mci_macro.mac_rpool = NULL;
- s->s_mci.mci_rpool = NULL;
- }
- break;
- }
- next:
- p = s;
- s = s->s_next;
- continue;
- }
-
- /* remove entry */
- SM_STAB_FREE(s->s_name); /* XXX */
- f = s;
- s = s->s_next;
- sm_free(f); /* XXX */
- if (p == NULL)
- SymTab[i] = s;
- else
- p->s_next = s;
- }
- }
-}
-
-#if SM_HEAP_CHECK
-/*
-** DUMPSTAB -- dump symbol table.
-**
-** For debugging.
-*/
-
-#define MAXSTTYPES (ST_MCI + 1)
-
-void
-dumpstab()
-{
- int i, t, total, types[MAXSTTYPES];
- STAB *s;
- static int prevt[MAXSTTYPES], prev = 0;
-
- total = 0;
- for (i = 0; i < MAXSTTYPES; i++)
- types[i] = 0;
- for (i = 0; i < STABSIZE; i++)
- {
- s = SymTab[i];
- while (s != NULL)
- {
- ++total;
- t = s->s_symtype;
- if (t > MAXSTTYPES - 1)
- t = MAXSTTYPES - 1;
- types[t]++;
- s = s->s_next;
- }
- }
- sm_syslog(LOG_INFO, NOQID, "stab: total=%d (%d)", total, total - prev);
- prev = total;
- for (i = 0; i < MAXSTTYPES; i++)
- {
- if (types[i] != 0)
- {
- sm_syslog(LOG_INFO, NOQID, "stab: type[%2d]=%2d (%d)",
- i, types[i], types[i] - prevt[i]);
- }
- prevt[i] = types[i];
- }
-}
-#endif /* SM_HEAP_CHECK */
diff --git a/contrib/sendmail/src/stats.c b/contrib/sendmail/src/stats.c
deleted file mode 100644
index 16a9c6d..0000000
--- a/contrib/sendmail/src/stats.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: stats.c,v 8.57 2006/08/15 23:24:58 ca Exp $")
-
-#include <sendmail/mailstats.h>
-
-static struct statistics Stat;
-
-static bool GotStats = false; /* set when we have stats to merge */
-
-/* See http://physics.nist.gov/cuu/Units/binary.html */
-#define ONE_K 1000 /* one thousand (twenty-four?) */
-#define KBYTES(x) (((x) + (ONE_K - 1)) / ONE_K)
-/*
-** MARKSTATS -- mark statistics
-**
-** Parameters:
-** e -- the envelope.
-** to -- to address.
-** type -- type of stats this represents.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** changes static Stat structure
-*/
-
-void
-markstats(e, to, type)
- register ENVELOPE *e;
- register ADDRESS *to;
- int type;
-{
- switch (type)
- {
- case STATS_QUARANTINE:
- if (e->e_from.q_mailer != NULL)
- Stat.stat_nq[e->e_from.q_mailer->m_mno]++;
- break;
-
- case STATS_REJECT:
- if (e->e_from.q_mailer != NULL)
- {
- if (bitset(EF_DISCARD, e->e_flags))
- Stat.stat_nd[e->e_from.q_mailer->m_mno]++;
- else
- Stat.stat_nr[e->e_from.q_mailer->m_mno]++;
- }
- Stat.stat_cr++;
- break;
-
- case STATS_CONNECT:
- if (to == NULL)
- Stat.stat_cf++;
- else
- Stat.stat_ct++;
- break;
-
- case STATS_NORMAL:
- if (to == NULL)
- {
- if (e->e_from.q_mailer != NULL)
- {
- Stat.stat_nf[e->e_from.q_mailer->m_mno]++;
- Stat.stat_bf[e->e_from.q_mailer->m_mno] +=
- KBYTES(e->e_msgsize);
- }
- }
- else
- {
- Stat.stat_nt[to->q_mailer->m_mno]++;
- Stat.stat_bt[to->q_mailer->m_mno] += KBYTES(e->e_msgsize);
- }
- break;
-
- default:
- /* Silently ignore bogus call */
- return;
- }
-
-
- GotStats = true;
-}
-/*
-** 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:
-** sfile -- the name of the statistics file.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** merges the Stat structure with the sfile file.
-*/
-
-void
-poststats(sfile)
- char *sfile;
-{
- int fd;
- static bool entered = false;
- long sff = SFF_REGONLY|SFF_OPENASROOT;
- struct statistics stats;
- extern off_t lseek();
-
- if (sfile == NULL || *sfile == '\0' || !GotStats || entered)
- return;
- entered = true;
-
- (void) time(&Stat.stat_itime);
- Stat.stat_size = sizeof(Stat);
- Stat.stat_magic = STAT_MAGIC;
- Stat.stat_version = STAT_VERSION;
-
- if (!bitnset(DBS_WRITESTATSTOSYMLINK, DontBlameSendmail))
- sff |= SFF_NOSLINK;
- if (!bitnset(DBS_WRITESTATSTOHARDLINK, DontBlameSendmail))
- sff |= SFF_NOHLINK;
-
- fd = safeopen(sfile, O_RDWR, 0600, sff);
- if (fd < 0)
- {
- if (LogLevel > 12)
- sm_syslog(LOG_INFO, NOQID, "poststats: %s: %s",
- sfile, sm_errstring(errno));
- errno = 0;
- entered = false;
- return;
- }
- 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++)
- {
- 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_nq[i] += Stat.stat_nq[i];
- }
- stats.stat_cr += Stat.stat_cr;
- stats.stat_ct += Stat.stat_ct;
- stats.stat_cf += Stat.stat_cf;
- }
- else
- memmove((char *) &stats, (char *) &Stat, sizeof(stats));
-
- /* write out results */
- (void) lseek(fd, (off_t) 0, 0);
- (void) write(fd, (char *) &stats, sizeof(stats));
- (void) close(fd);
-
- /* clear the structure to avoid future disappointment */
- clearstats();
- entered = false;
-}
diff --git a/contrib/sendmail/src/statusd_shm.h b/contrib/sendmail/src/statusd_shm.h
deleted file mode 100644
index 7d88964..0000000
--- a/contrib/sendmail/src/statusd_shm.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- * $Id: statusd_shm.h,v 8.7 2000/09/17 17:30:06 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
deleted file mode 100644
index 990ffe3..0000000
--- a/contrib/sendmail/src/sysexits.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: sysexits.c,v 8.34 2002/09/09 02:43:00 gshapiro Exp $")
-
-/*
-** DSNTOEXITSTAT -- convert DSN-style error code to EX_ style.
-**
-** Parameters:
-** dsncode -- the text of the DSN-style code.
-**
-** Returns:
-** The corresponding exit status.
-*/
-
-int
-dsntoexitstat(dsncode)
- char *dsncode;
-{
- int code2, code3;
-
- /* first the easy cases.... */
- if (*dsncode == '2')
- return EX_OK;
- if (*dsncode == '4')
- return EX_TEMPFAIL;
-
- /* reject other illegal values */
- if (*dsncode != '5')
- return EX_CONFIG;
-
- /* now decode the other two field parts */
- if (*++dsncode == '.')
- dsncode++;
- code2 = atoi(dsncode);
- while (*dsncode != '\0' && *dsncode != '.')
- dsncode++;
- if (*dsncode != '\0')
- dsncode++;
- code3 = atoi(dsncode);
-
- /* and do a nested switch to work them out */
- switch (code2)
- {
- case 0: /* Other or Undefined status */
- return EX_UNAVAILABLE;
-
- case 1: /* Address Status */
- switch (code3)
- {
- case 0: /* Other Address Status */
- return EX_DATAERR;
-
- case 1: /* Bad destination mailbox address */
- case 6: /* Mailbox has moved, No forwarding address */
- return EX_NOUSER;
-
- case 2: /* Bad destination system address */
- case 8: /* Bad senders system address */
- return EX_NOHOST;
-
- case 3: /* Bad destination mailbox address syntax */
- case 7: /* Bad senders mailbox address syntax */
- return EX_USAGE;
-
- case 4: /* Destination mailbox address ambiguous */
- return EX_UNAVAILABLE;
-
- case 5: /* Destination address valid */
- /* According to RFC1893, this can't happen */
- return EX_CONFIG;
- }
- break;
-
- case 2: /* Mailbox Status */
- switch (code3)
- {
- case 0: /* Other or Undefined mailbox status */
- case 1: /* Mailbox disabled, not accepting messages */
- case 2: /* Mailbox full */
- case 4: /* Mailing list expansion problem */
- return EX_UNAVAILABLE;
-
- case 3: /* Message length exceeds administrative lim */
- return EX_DATAERR;
- }
- break;
-
- case 3: /* System Status */
- return EX_OSERR;
-
- case 4: /* Network and Routing Status */
- switch (code3)
- {
- case 0: /* Other or undefined network or routing stat */
- return EX_IOERR;
-
- case 1: /* No answer from host */
- case 3: /* Routing server failure */
- case 5: /* Network congestion */
- return EX_TEMPFAIL;
-
- case 2: /* Bad connection */
- return EX_IOERR;
-
- case 4: /* Unable to route */
- return EX_PROTOCOL;
-
- case 6: /* Routing loop detected */
- return EX_CONFIG;
-
- case 7: /* Delivery time expired */
- return EX_UNAVAILABLE;
- }
- break;
-
- case 5: /* Protocol Status */
- return EX_PROTOCOL;
-
- case 6: /* Message Content or Media Status */
- return EX_UNAVAILABLE;
-
- case 7: /* Security Status */
- return EX_DATAERR;
- }
- return EX_UNAVAILABLE;
-}
-/*
-** 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;
- char *exitmsg;
-
- if (excode == NULL || *excode == '\0')
- return excode;
- i = (int) strtol(excode, &c, 10);
- if (*c != '\0')
- return excode;
- exitmsg = sm_sysexitmsg(i);
- if (exitmsg != NULL)
- return exitmsg;
- return excode;
-}
diff --git a/contrib/sendmail/src/timers.c b/contrib/sendmail/src/timers.c
deleted file mode 100644
index 43dc07d..0000000
--- a/contrib/sendmail/src/timers.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- * Contributed by Exactis.com, Inc.
- *
- */
-
-#include <sm/gen.h>
-SM_RCSID("@(#)$Id: timers.c,v 8.26 2006/08/15 23:24:58 ca Exp $")
-
-#if _FFR_TIMERS
-# include <sys/types.h>
-# include <sm/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];
- SM_VA_LOCAL_DECL
-
-# if 0
- if (!tTd(98, 30))
- return;
-# endif /* 0 */
- SM_VA_START(ap, msg);
- (void) sm_vsnprintf(buf, sizeof(buf), msg, ap);
- SM_VA_END(ap);
- sm_syslog(LOG_NOTICE, CurEnv->e_id, "%s; e_timers=0x%lx",
- buf, (unsigned 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",
- (unsigned 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)",
- (unsigned long) ptimer, i, NTimers);
- NTimers = i;
-
- /* clean up and return */
- errno = save_errno;
-}
-
-char *
-strtimer(ptimer)
- TIMER *ptimer;
-{
- static char buf[40];
-
- (void) sm_snprintf(buf, sizeof(buf), "%ld.%06ldr/%ld.%06ldc",
- ptimer->ti_wall_sec, ptimer->ti_wall_usec,
- ptimer->ti_cpu_sec, ptimer->ti_cpu_usec);
- return buf;
-}
-#endif /* _FFR_TIMERS */
diff --git a/contrib/sendmail/src/timers.h b/contrib/sendmail/src/timers.h
deleted file mode 100644
index d7faee1..0000000
--- a/contrib/sendmail/src/timers.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- * $Id: timers.h,v 8.6 2001/04/03 01:53:18 gshapiro 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/tls.c b/contrib/sendmail/src/tls.c
deleted file mode 100644
index 1a213ca..0000000
--- a/contrib/sendmail/src/tls.c
+++ /dev/null
@@ -1,1671 +0,0 @@
-/*
- * Copyright (c) 2000-2006 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: tls.c,v 8.107 2006/10/12 21:35:11 ca Exp $")
-
-#if STARTTLS
-# include <openssl/err.h>
-# include <openssl/bio.h>
-# include <openssl/pem.h>
-# ifndef HASURANDOMDEV
-# include <openssl/rand.h>
-# endif /* ! HASURANDOMDEV */
-# if !TLS_NO_RSA
-static RSA *rsa_tmp = NULL; /* temporary RSA key */
-static RSA *tmp_rsa_key __P((SSL *, int, int));
-# endif /* !TLS_NO_RSA */
-# if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x00907000L
-static int tls_verify_cb __P((X509_STORE_CTX *));
-# else /* !defined() || OPENSSL_VERSION_NUMBER < 0x00907000L */
-static int tls_verify_cb __P((X509_STORE_CTX *, void *));
-# endif /* !defined() || OPENSSL_VERSION_NUMBER < 0x00907000L */
-
-# if OPENSSL_VERSION_NUMBER > 0x00907000L
-static int x509_verify_cb __P((int, X509_STORE_CTX *));
-# endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
-
-# if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x00907000L
-# define CONST097
-# else /* !defined() || OPENSSL_VERSION_NUMBER < 0x00907000L */
-# define CONST097 const
-# endif /* !defined() || OPENSSL_VERSION_NUMBER < 0x00907000L */
-static void apps_ssl_info_cb __P((CONST097 SSL *, int , int));
-static bool tls_ok_f __P((char *, char *, int));
-static bool tls_safe_f __P((char *, long, bool));
-static int tls_verify_log __P((int, X509_STORE_CTX *, char *));
-
-# if !NO_DH
-static DH *get_dh512 __P((void));
-
-static unsigned char dh512_p[] =
-{
- 0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75,
- 0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F,
- 0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3,
- 0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12,
- 0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C,
- 0x47,0x74,0xE8,0x33
-};
-static unsigned char dh512_g[] =
-{
- 0x02
-};
-
-static DH *
-get_dh512()
-{
- DH *dh = NULL;
-
- if ((dh = DH_new()) == NULL)
- return NULL;
- dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
- dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL);
- if ((dh->p == NULL) || (dh->g == NULL))
- return NULL;
- return dh;
-}
-# endif /* !NO_DH */
-
-
-/*
-** TLS_RAND_INIT -- initialize STARTTLS random generator
-**
-** Parameters:
-** randfile -- name of file with random data
-** logl -- loglevel
-**
-** Returns:
-** success/failure
-**
-** Side Effects:
-** initializes PRNG for tls library.
-*/
-
-# define MIN_RAND_BYTES 128 /* 1024 bits */
-
-# define RF_OK 0 /* randfile OK */
-# define RF_MISS 1 /* randfile == NULL || *randfile == '\0' */
-# define RF_UNKNOWN 2 /* unknown prefix for randfile */
-
-# define RI_NONE 0 /* no init yet */
-# define RI_SUCCESS 1 /* init was successful */
-# define RI_FAIL 2 /* init failed */
-
-static bool tls_rand_init __P((char *, int));
-
-static bool
-tls_rand_init(randfile, logl)
- char *randfile;
- int logl;
-{
-# ifndef HASURANDOMDEV
- /* not required if /dev/urandom exists, OpenSSL does it internally */
-
- bool ok;
- int randdef;
- static int done = RI_NONE;
-
- /*
- ** initialize PRNG
- */
-
- /* did we try this before? if yes: return old value */
- if (done != RI_NONE)
- return done == RI_SUCCESS;
-
- /* set default values */
- ok = false;
- done = RI_FAIL;
- randdef = (randfile == NULL || *randfile == '\0') ? RF_MISS : RF_OK;
-# if EGD
- if (randdef == RF_OK && sm_strncasecmp(randfile, "egd:", 4) == 0)
- {
- randfile += 4;
- if (RAND_egd(randfile) < 0)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: RAND_egd(%s) failed: random number generator not seeded",
- randfile);
- }
- else
- ok = true;
- }
- else
-# endif /* EGD */
- if (randdef == RF_OK && sm_strncasecmp(randfile, "file:", 5) == 0)
- {
- int fd;
- long sff;
- struct stat st;
-
- randfile += 5;
- sff = SFF_SAFEDIRPATH | SFF_NOWLINK
- | SFF_NOGWFILES | SFF_NOWWFILES
- | SFF_NOGRFILES | SFF_NOWRFILES
- | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT;
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
- if ((fd = safeopen(randfile, O_RDONLY, 0, sff)) >= 0)
- {
- if (fstat(fd, &st) < 0)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS: can't fstat(%s)",
- randfile);
- }
- else
- {
- bool use, problem;
-
- use = true;
- problem = false;
-
- /* max. age of file: 10 minutes */
- if (st.st_mtime + 600 < curtime())
- {
- use = bitnset(DBS_INSUFFICIENTENTROPY,
- DontBlameSendmail);
- problem = true;
- if (LogLevel > logl)
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS: RandFile %s too old: %s",
- randfile,
- use ? "unsafe" :
- "unusable");
- }
- if (use && st.st_size < MIN_RAND_BYTES)
- {
- use = bitnset(DBS_INSUFFICIENTENTROPY,
- DontBlameSendmail);
- problem = true;
- if (LogLevel > logl)
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS: size(%s) < %d: %s",
- randfile,
- MIN_RAND_BYTES,
- use ? "unsafe" :
- "unusable");
- }
- if (use)
- ok = RAND_load_file(randfile, -1) >=
- MIN_RAND_BYTES;
- if (use && !ok)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: RAND_load_file(%s) failed: random number generator not seeded",
- randfile);
- }
- if (problem)
- ok = false;
- }
- if (ok || bitnset(DBS_INSUFFICIENTENTROPY,
- DontBlameSendmail))
- {
- /* add this even if fstat() failed */
- RAND_seed((void *) &st, sizeof(st));
- }
- (void) close(fd);
- }
- else
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: Warning: safeopen(%s) failed",
- randfile);
- }
- }
- else if (randdef == RF_OK)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: Error: no proper random file definition %s",
- randfile);
- randdef = RF_UNKNOWN;
- }
- if (randdef == RF_MISS)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: Error: missing random file definition");
- }
- if (!ok && bitnset(DBS_INSUFFICIENTENTROPY, DontBlameSendmail))
- {
- int i;
- long r;
- unsigned char buf[MIN_RAND_BYTES];
-
- /* assert((MIN_RAND_BYTES % sizeof(long)) == 0); */
- for (i = 0; i <= sizeof(buf) - sizeof(long); i += sizeof(long))
- {
- r = get_random();
- (void) memcpy(buf + i, (void *) &r, sizeof(long));
- }
- RAND_seed(buf, sizeof(buf));
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: Warning: random number generator not properly seeded");
- ok = true;
- }
- done = ok ? RI_SUCCESS : RI_FAIL;
- return ok;
-# else /* ! HASURANDOMDEV */
- return true;
-# endif /* ! HASURANDOMDEV */
-}
-/*
-** INIT_TLS_LIBRARY -- Calls functions which setup TLS library for global use.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** succeeded?
-*/
-
-bool
-init_tls_library()
-{
- /* basic TLS initialization, ignore result for now */
- SSL_library_init();
- SSL_load_error_strings();
-# if 0
- /* this is currently a macro for SSL_library_init */
- SSLeay_add_ssl_algorithms();
-# endif /* 0 */
-
- return tls_rand_init(RandFile, 7);
-}
-/*
-** TLS_SET_VERIFY -- request client certificate?
-**
-** Parameters:
-** ctx -- TLS context
-** ssl -- TLS structure
-** vrfy -- require certificate?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets verification state for TLS
-**
-# if TLS_VRFY_PER_CTX
-** Notice:
-** This is per TLS context, not per TLS structure;
-** the former is global, the latter per connection.
-** It would be nice to do this per connection, but this
-** doesn't work in the current TLS libraries :-(
-# endif * TLS_VRFY_PER_CTX *
-*/
-
-void
-tls_set_verify(ctx, ssl, vrfy)
- SSL_CTX *ctx;
- SSL *ssl;
- bool vrfy;
-{
-# if !TLS_VRFY_PER_CTX
- SSL_set_verify(ssl, vrfy ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
-# else /* !TLS_VRFY_PER_CTX */
- SSL_CTX_set_verify(ctx, vrfy ? SSL_VERIFY_PEER : SSL_VERIFY_NONE,
- NULL);
-# endif /* !TLS_VRFY_PER_CTX */
-}
-
-/*
-** status in initialization
-** these flags keep track of the status of the initialization
-** i.e., whether a file exists (_EX) and whether it can be used (_OK)
-** [due to permissions]
-*/
-
-# define TLS_S_NONE 0x00000000 /* none yet */
-# define TLS_S_CERT_EX 0x00000001 /* cert file exists */
-# define TLS_S_CERT_OK 0x00000002 /* cert file is ok */
-# define TLS_S_KEY_EX 0x00000004 /* key file exists */
-# define TLS_S_KEY_OK 0x00000008 /* key file is ok */
-# define TLS_S_CERTP_EX 0x00000010 /* CA cert path exists */
-# define TLS_S_CERTP_OK 0x00000020 /* CA cert path is ok */
-# define TLS_S_CERTF_EX 0x00000040 /* CA cert file exists */
-# define TLS_S_CERTF_OK 0x00000080 /* CA cert file is ok */
-# define TLS_S_CRLF_EX 0x00000100 /* CRL file exists */
-# define TLS_S_CRLF_OK 0x00000200 /* CRL 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 */
-
-/* Type of variable */
-# define TLS_T_OTHER 0
-# define TLS_T_SRV 1
-# define TLS_T_CLT 2
-
-/*
-** TLS_OK_F -- can var be an absolute filename?
-**
-** Parameters:
-** var -- filename
-** fn -- what is the filename used for?
-** type -- type of variable
-**
-** Returns:
-** ok?
-*/
-
-static bool
-tls_ok_f(var, fn, type)
- char *var;
- char *fn;
- int type;
-{
- /* must be absolute pathname */
- if (var != NULL && *var == '/')
- return true;
- if (LogLevel > 12)
- sm_syslog(LOG_WARNING, NOQID, "STARTTLS: %s%s missing",
- type == TLS_T_SRV ? "Server" :
- (type == TLS_T_CLT ? "Client" : ""), fn);
- return false;
-}
-/*
-** TLS_SAFE_F -- is a file safe to use?
-**
-** Parameters:
-** var -- filename
-** sff -- flags for safefile()
-** srv -- server side?
-**
-** Returns:
-** ok?
-*/
-
-static bool
-tls_safe_f(var, sff, srv)
- char *var;
- long sff;
- bool srv;
-{
- int ret;
-
- if ((ret = safefile(var, RunAsUid, RunAsGid, RunAsUserName, sff,
- S_IRUSR, NULL)) == 0)
- return true;
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID, "STARTTLS=%s: file %s unsafe: %s",
- srv ? "server" : "client", var, sm_errstring(ret));
- return false;
-}
-
-/*
-** TLS_OK_F -- macro to simplify calls to tls_ok_f
-**
-** Parameters:
-** var -- filename
-** fn -- what is the filename used for?
-** req -- is the file required?
-** st -- status bit to set if ok
-** type -- type of variable
-**
-** Side Effects:
-** uses r, ok; may change ok and status.
-**
-*/
-
-# define TLS_OK_F(var, fn, req, st, type) if (ok) \
- { \
- r = tls_ok_f(var, fn, type); \
- if (r) \
- status |= st; \
- else if (req) \
- ok = false; \
- }
-
-/*
-** TLS_UNR -- macro to return whether a file should be unreadable
-**
-** Parameters:
-** bit -- flag to test
-** req -- flags
-**
-** Returns:
-** 0/SFF_NORFILES
-*/
-# define TLS_UNR(bit, req) (bitset(bit, req) ? SFF_NORFILES : 0)
-# define TLS_OUNR(bit, req) (bitset(bit, req) ? SFF_NOWRFILES : 0)
-# define TLS_KEYSFF(req) \
- (bitnset(DBS_GROUPREADABLEKEYFILE, DontBlameSendmail) ? \
- TLS_OUNR(TLS_I_KEY_OUNR, req) : \
- TLS_UNR(TLS_I_KEY_UNR, req))
-
-/*
-** TLS_SAFE_F -- macro to simplify calls to tls_safe_f
-**
-** Parameters:
-** var -- filename
-** sff -- flags for safefile()
-** req -- is the file required?
-** ex -- does the file exist?
-** st -- status bit to set if ok
-** srv -- server side?
-**
-** Side Effects:
-** uses r, ok, ex; may change ok and status.
-**
-*/
-
-# define TLS_SAFE_F(var, sff, req, ex, st, srv) if (ex && ok) \
- { \
- r = tls_safe_f(var, sff, srv); \
- if (r) \
- status |= st; \
- else if (req) \
- ok = false; \
- }
-
-/*
-** INITTLS -- initialize TLS
-**
-** Parameters:
-** ctx -- pointer to context
-** req -- requirements for initialization (see sendmail.h)
-** srv -- server side?
-** certfile -- filename of certificate
-** keyfile -- filename of private key
-** cacertpath -- path to CAs
-** cacertfile -- file with CA(s)
-** dhparam -- parameters for DH
-**
-** Returns:
-** succeeded?
-*/
-
-/*
-** The session_id_context identifies the service that created a session.
-** This information is used to distinguish between multiple TLS-based
-** servers running on the same server. We use the name of the mail system.
-** Note: the session cache is not persistent.
-*/
-
-static char server_session_id_context[] = "sendmail8";
-
-/* 0.9.8a and b have a problem with SSL_OP_TLS_BLOCK_PADDING_BUG */
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
-# define SM_SSL_OP_TLS_BLOCK_PADDING_BUG 1
-#else
-# define SM_SSL_OP_TLS_BLOCK_PADDING_BUG 0
-#endif
-
-bool
-inittls(ctx, req, srv, certfile, keyfile, cacertpath, cacertfile, dhparam)
- SSL_CTX **ctx;
- unsigned long req;
- bool srv;
- char *certfile, *keyfile, *cacertpath, *cacertfile, *dhparam;
-{
-# if !NO_DH
- static DH *dh = NULL;
-# endif /* !NO_DH */
- int r;
- bool ok;
- long sff, status, options;
- char *who;
-# if _FFR_TLS_1
- char *cf2, *kf2;
-# endif /* _FFR_TLS_1 */
-# if SM_CONF_SHM
- extern int ShmId;
-# endif /* SM_CONF_SHM */
-# if OPENSSL_VERSION_NUMBER > 0x00907000L
- BIO *crl_file;
- X509_CRL *crl;
- X509_STORE *store;
-# endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
-#if SM_SSL_OP_TLS_BLOCK_PADDING_BUG
- long rt_version;
- STACK_OF(SSL_COMP) *comp_methods;
-#endif
-
- status = TLS_S_NONE;
- who = srv ? "server" : "client";
- if (ctx == NULL)
- {
- syserr("STARTTLS=%s, inittls: ctx == NULL", who);
- /* NOTREACHED */
- SM_ASSERT(ctx != NULL);
- }
-
- /* already initialized? (we could re-init...) */
- if (*ctx != NULL)
- return true;
- ok = true;
-
-# if _FFR_TLS_1
- /*
- ** look for a second filename: it must be separated by a ','
- ** no blanks allowed (they won't be skipped).
- ** we change a global variable here! this change will be undone
- ** before return from the function but only if it returns true.
- ** this isn't a problem since in a failure case this function
- ** won't be called again with the same (overwritten) values.
- ** otherwise each return must be replaced with a goto endinittls.
- */
-
- cf2 = NULL;
- kf2 = NULL;
- if (certfile != NULL && (cf2 = strchr(certfile, ',')) != NULL)
- {
- *cf2++ = '\0';
- if (keyfile != NULL && (kf2 = strchr(keyfile, ',')) != NULL)
- *kf2++ = '\0';
- }
-# endif /* _FFR_TLS_1 */
-
- /*
- ** Check whether files/paths are defined
- */
-
- TLS_OK_F(certfile, "CertFile", bitset(TLS_I_CERT_EX, req),
- TLS_S_CERT_EX, srv ? TLS_T_SRV : TLS_T_CLT);
- TLS_OK_F(keyfile, "KeyFile", bitset(TLS_I_KEY_EX, req),
- TLS_S_KEY_EX, srv ? TLS_T_SRV : TLS_T_CLT);
- TLS_OK_F(cacertpath, "CACertPath", bitset(TLS_I_CERTP_EX, req),
- TLS_S_CERTP_EX, TLS_T_OTHER);
- TLS_OK_F(cacertfile, "CACertFile", bitset(TLS_I_CERTF_EX, req),
- TLS_S_CERTF_EX, TLS_T_OTHER);
-
-# if OPENSSL_VERSION_NUMBER > 0x00907000L
- TLS_OK_F(CRLFile, "CRLFile", bitset(TLS_I_CRLF_EX, req),
- TLS_S_CRLF_EX, TLS_T_OTHER);
-# endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
-
-# if _FFR_TLS_1
- /*
- ** if the second file is specified it must exist
- ** XXX: it is possible here to define only one of those files
- */
-
- if (cf2 != NULL)
- {
- TLS_OK_F(cf2, "CertFile", bitset(TLS_I_CERT_EX, req),
- TLS_S_CERT2_EX, srv ? TLS_T_SRV : TLS_T_CLT);
- }
- if (kf2 != NULL)
- {
- TLS_OK_F(kf2, "KeyFile", bitset(TLS_I_KEY_EX, req),
- TLS_S_KEY2_EX, srv ? TLS_T_SRV : TLS_T_CLT);
- }
-# endif /* _FFR_TLS_1 */
-
- /*
- ** valid values for dhparam are (only the first char is checked)
- ** none no parameters: don't use DH
- ** 512 generate 512 bit parameters (fixed)
- ** 1024 generate 1024 bit parameters
- ** /file/name read parameters from /file/name
- ** default is: 1024 for server, 512 for client (OK? XXX)
- */
-
- if (bitset(TLS_I_TRY_DH, req))
- {
- if (dhparam != NULL)
- {
- char c = *dhparam;
-
- if (c == '1')
- req |= TLS_I_DH1024;
- else if (c == '5')
- req |= TLS_I_DH512;
- else if (c != 'n' && c != 'N' && c != '/')
- {
- if (LogLevel > 12)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: illegal value '%s' for DHParam",
- who, dhparam);
- dhparam = NULL;
- }
- }
- if (dhparam == NULL)
- dhparam = srv ? "1" : "5";
- else if (*dhparam == '/')
- {
- TLS_OK_F(dhparam, "DHParameters",
- bitset(TLS_I_DHPAR_EX, req),
- TLS_S_DHPAR_EX, TLS_T_OTHER);
- }
- }
- if (!ok)
- return ok;
-
- /* certfile etc. must be "safe". */
- sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
- | SFF_NOGWFILES | SFF_NOWWFILES
- | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT;
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
-
- TLS_SAFE_F(certfile, sff | TLS_UNR(TLS_I_CERT_UNR, req),
- bitset(TLS_I_CERT_EX, req),
- bitset(TLS_S_CERT_EX, status), TLS_S_CERT_OK, srv);
- TLS_SAFE_F(keyfile, sff | TLS_KEYSFF(req),
- bitset(TLS_I_KEY_EX, req),
- bitset(TLS_S_KEY_EX, status), TLS_S_KEY_OK, srv);
- TLS_SAFE_F(cacertfile, sff | TLS_UNR(TLS_I_CERTF_UNR, req),
- bitset(TLS_I_CERTF_EX, req),
- bitset(TLS_S_CERTF_EX, status), TLS_S_CERTF_OK, srv);
- TLS_SAFE_F(dhparam, sff | TLS_UNR(TLS_I_DHPAR_UNR, req),
- bitset(TLS_I_DHPAR_EX, req),
- bitset(TLS_S_DHPAR_EX, status), TLS_S_DHPAR_OK, srv);
-# if OPENSSL_VERSION_NUMBER > 0x00907000L
- TLS_SAFE_F(CRLFile, sff | TLS_UNR(TLS_I_CRLF_UNR, req),
- bitset(TLS_I_CRLF_EX, req),
- bitset(TLS_S_CRLF_EX, status), TLS_S_CRLF_OK, srv);
-# endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
- if (!ok)
- return ok;
-# if _FFR_TLS_1
- if (cf2 != NULL)
- {
- TLS_SAFE_F(cf2, sff | TLS_UNR(TLS_I_CERT_UNR, req),
- bitset(TLS_I_CERT_EX, req),
- bitset(TLS_S_CERT2_EX, status), TLS_S_CERT2_OK, srv);
- }
- if (kf2 != NULL)
- {
- TLS_SAFE_F(kf2, sff | TLS_KEYSFF(req),
- bitset(TLS_I_KEY_EX, req),
- bitset(TLS_S_KEY2_EX, status), TLS_S_KEY2_OK, srv);
- }
-# endif /* _FFR_TLS_1 */
-
- /* create a method and a new context */
- if ((*ctx = SSL_CTX_new(srv ? SSLv23_server_method() :
- SSLv23_client_method())) == NULL)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: SSL_CTX_new(SSLv23_%s_method()) failed",
- who, who);
- if (LogLevel > 9)
- tlslogerr(who);
- return false;
- }
-
-# if OPENSSL_VERSION_NUMBER > 0x00907000L
- if (CRLFile != NULL)
- {
- /* get a pointer to the current certificate validation store */
- store = SSL_CTX_get_cert_store(*ctx); /* does not fail */
- crl_file = BIO_new(BIO_s_file_internal());
- if (crl_file != NULL)
- {
- if (BIO_read_filename(crl_file, CRLFile) >= 0)
- {
- crl = PEM_read_bio_X509_CRL(crl_file, NULL,
- NULL, NULL);
- BIO_free(crl_file);
- X509_STORE_add_crl(store, crl);
- X509_CRL_free(crl);
- X509_STORE_set_flags(store,
- X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
- X509_STORE_set_verify_cb_func(store,
- x509_verify_cb);
- }
- else
- {
- if (LogLevel > 9)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: PEM_read_bio_X509_CRL(%s)=failed",
- who, CRLFile);
- }
-
- /* avoid memory leaks */
- BIO_free(crl_file);
- return false;
- }
-
- }
- else if (LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: BIO_new=failed", who);
- }
- else
- store = NULL;
-# if _FFR_CRLPATH
- if (CRLPath != NULL && store != NULL)
- {
- X509_LOOKUP *lookup;
-
- lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
- if (lookup == NULL)
- {
- if (LogLevel > 9)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: X509_STORE_add_lookup(hash)=failed",
- who, CRLFile);
- }
- return false;
- }
- X509_LOOKUP_add_dir(lookup, CRLPath, X509_FILETYPE_PEM);
- X509_STORE_set_flags(store,
- X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
- }
-# endif /* _FFR_CRLPATH */
-# endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
-
-# if TLS_NO_RSA
- /* turn off backward compatibility, required for no-rsa */
- SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2);
-# endif /* TLS_NO_RSA */
-
-
-# if !TLS_NO_RSA
- /*
- ** Create a temporary RSA key
- ** XXX Maybe we shouldn't create this always (even though it
- ** is only at startup).
- ** It is a time-consuming operation and it is not always necessary.
- ** maybe we should do it only on demand...
- */
-
- if (bitset(TLS_I_RSA_TMP, req)
-# if SM_CONF_SHM
- && ShmId != SM_SHM_NO_ID &&
- (rsa_tmp = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, NULL,
- NULL)) == NULL
-# else /* SM_CONF_SHM */
- && 0 /* no shared memory: no need to generate key now */
-# endif /* SM_CONF_SHM */
- )
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: RSA_generate_key failed",
- who);
- if (LogLevel > 9)
- tlslogerr(who);
- }
- return false;
- }
-# endif /* !TLS_NO_RSA */
-
- /*
- ** load private key
- ** XXX change this for DSA-only version
- */
-
- if (bitset(TLS_S_KEY_OK, status) &&
- SSL_CTX_use_PrivateKey_file(*ctx, keyfile,
- SSL_FILETYPE_PEM) <= 0)
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: SSL_CTX_use_PrivateKey_file(%s) failed",
- who, keyfile);
- if (LogLevel > 9)
- tlslogerr(who);
- }
- if (bitset(TLS_I_USE_KEY, req))
- return false;
- }
-
- /* get the certificate file */
- if (bitset(TLS_S_CERT_OK, status) &&
- SSL_CTX_use_certificate_file(*ctx, certfile,
- SSL_FILETYPE_PEM) <= 0)
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: SSL_CTX_use_certificate_file(%s) failed",
- who, certfile);
- if (LogLevel > 9)
- tlslogerr(who);
- }
- if (bitset(TLS_I_USE_CERT, req))
- return false;
- }
-
- /* check the private key */
- if (bitset(TLS_S_KEY_OK, status) &&
- (r = SSL_CTX_check_private_key(*ctx)) <= 0)
- {
- /* Private key does not match the certificate public key */
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: SSL_CTX_check_private_key failed(%s): %d",
- who, keyfile, r);
- if (LogLevel > 9)
- tlslogerr(who);
- }
- if (bitset(TLS_I_USE_KEY, req))
- return false;
- }
-
-# if _FFR_TLS_1
- /* XXX this code is pretty much duplicated from above! */
-
- /* load private key */
- if (bitset(TLS_S_KEY2_OK, status) &&
- SSL_CTX_use_PrivateKey_file(*ctx, kf2, SSL_FILETYPE_PEM) <= 0)
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: SSL_CTX_use_PrivateKey_file(%s) failed",
- who, kf2);
- if (LogLevel > 9)
- tlslogerr(who);
- }
- }
-
- /* get the certificate file */
- if (bitset(TLS_S_CERT2_OK, status) &&
- SSL_CTX_use_certificate_file(*ctx, cf2, SSL_FILETYPE_PEM) <= 0)
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: SSL_CTX_use_certificate_file(%s) failed",
- who, cf2);
- if (LogLevel > 9)
- tlslogerr(who);
- }
- }
-
- /* also check the private key */
- if (bitset(TLS_S_KEY2_OK, status) &&
- (r = SSL_CTX_check_private_key(*ctx)) <= 0)
- {
- /* Private key does not match the certificate public key */
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: SSL_CTX_check_private_key 2 failed: %d",
- who, r);
- if (LogLevel > 9)
- tlslogerr(who);
- }
- }
-# endif /* _FFR_TLS_1 */
-
- /* SSL_CTX_set_quiet_shutdown(*ctx, 1); violation of standard? */
-
- options = SSL_OP_ALL; /* bug compatibility? */
-#if SM_SSL_OP_TLS_BLOCK_PADDING_BUG
-
- /*
- ** In OpenSSL 0.9.8[ab], enabling zlib compression breaks the
- ** padding bug work-around, leading to false positives and
- ** failed connections. We may not interoperate with systems
- ** with the bug, but this is better than breaking on all 0.9.8[ab]
- ** systems that have zlib support enabled.
- ** Note: this checks the runtime version of the library, not
- ** just the compile time version.
- */
-
- rt_version = SSLeay();
- if (rt_version >= 0x00908000L && rt_version <= 0x0090802fL)
- {
- comp_methods = SSL_COMP_get_compression_methods();
- if (comp_methods != NULL && sk_SSL_COMP_num(comp_methods) > 0)
- options &= ~SSL_OP_TLS_BLOCK_PADDING_BUG;
- }
-#endif
- SSL_CTX_set_options(*ctx, options);
-
-# if !NO_DH
- /* Diffie-Hellman initialization */
- if (bitset(TLS_I_TRY_DH, req))
- {
- if (bitset(TLS_S_DHPAR_OK, status))
- {
- BIO *bio;
-
- if ((bio = BIO_new_file(dhparam, "r")) != NULL)
- {
- dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
- BIO_free(bio);
- if (dh == NULL && LogLevel > 7)
- {
- unsigned long err;
-
- err = ERR_get_error();
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: cannot read DH parameters(%s): %s",
- who, dhparam,
- ERR_error_string(err, NULL));
- if (LogLevel > 9)
- tlslogerr(who);
- }
- }
- else
- {
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: BIO_new_file(%s) failed",
- who, dhparam);
- if (LogLevel > 9)
- tlslogerr(who);
- }
- }
- }
- if (dh == NULL && bitset(TLS_I_DH1024, req))
- {
- DSA *dsa;
-
- /* this takes a while! (7-130s on a 450MHz AMD K6-2) */
- dsa = DSA_generate_parameters(1024, NULL, 0, NULL,
- NULL, 0, NULL);
- dh = DSA_dup_DH(dsa);
- DSA_free(dsa);
- }
- else
- if (dh == NULL && bitset(TLS_I_DH512, req))
- dh = get_dh512();
-
- if (dh == NULL)
- {
- if (LogLevel > 9)
- {
- unsigned long err;
-
- err = ERR_get_error();
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: cannot read or set DH parameters(%s): %s",
- who, dhparam,
- ERR_error_string(err, NULL));
- }
- if (bitset(TLS_I_REQ_DH, req))
- return false;
- }
- else
- {
- SSL_CTX_set_tmp_dh(*ctx, dh);
-
- /* important to avoid small subgroup attacks */
- SSL_CTX_set_options(*ctx, SSL_OP_SINGLE_DH_USE);
- if (LogLevel > 13)
- sm_syslog(LOG_INFO, NOQID,
- "STARTTLS=%s, Diffie-Hellman init, key=%d bit (%c)",
- who, 8 * DH_size(dh), *dhparam);
- DH_free(dh);
- }
- }
-# endif /* !NO_DH */
-
-
- /* XXX do we need this cache here? */
- if (bitset(TLS_I_CACHE, req))
- {
- SSL_CTX_sess_set_cache_size(*ctx, 1);
- SSL_CTX_set_timeout(*ctx, 1);
- SSL_CTX_set_session_id_context(*ctx,
- (void *) &server_session_id_context,
- sizeof(server_session_id_context));
- (void) SSL_CTX_set_session_cache_mode(*ctx,
- SSL_SESS_CACHE_SERVER);
- }
- else
- {
- (void) SSL_CTX_set_session_cache_mode(*ctx,
- SSL_SESS_CACHE_OFF);
- }
-
- /* load certificate locations and default CA paths */
- if (bitset(TLS_S_CERTP_EX, status) && bitset(TLS_S_CERTF_EX, status))
- {
- if ((r = SSL_CTX_load_verify_locations(*ctx, cacertfile,
- cacertpath)) == 1)
- {
-# if !TLS_NO_RSA
- if (bitset(TLS_I_RSA_TMP, req))
- SSL_CTX_set_tmp_rsa_callback(*ctx, tmp_rsa_key);
-# endif /* !TLS_NO_RSA */
-
- /*
- ** We have to install our own verify callback:
- ** SSL_VERIFY_PEER requests a client cert but even
- ** though *FAIL_IF* isn't set, the connection
- ** will be aborted if the client presents a cert
- ** that is not "liked" (can't be verified?) by
- ** the TLS library :-(
- */
-
- /*
- ** XXX currently we could call tls_set_verify()
- ** but we hope that that function will later on
- ** only set the mode per connection.
- */
- SSL_CTX_set_verify(*ctx,
- bitset(TLS_I_NO_VRFY, req) ? SSL_VERIFY_NONE
- : SSL_VERIFY_PEER,
- NULL);
-
- /* install verify callback */
- SSL_CTX_set_cert_verify_callback(*ctx, tls_verify_cb,
- NULL);
- SSL_CTX_set_client_CA_list(*ctx,
- SSL_load_client_CA_file(cacertfile));
- }
- else
- {
- /*
- ** can't load CA data; do we care?
- ** the data is necessary to authenticate the client,
- ** which in turn would be necessary
- ** if we want to allow relaying based on it.
- */
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: load verify locs %s, %s failed: %d",
- who, cacertpath, cacertfile, r);
- if (LogLevel > 9)
- tlslogerr(who);
- }
- if (bitset(TLS_I_VRFY_LOC, req))
- return false;
- }
- }
-
- /* XXX: make this dependent on an option? */
- if (tTd(96, 9))
- SSL_CTX_set_info_callback(*ctx, apps_ssl_info_cb);
-
-# if _FFR_TLS_1
- /* install our own cipher list */
- if (CipherList != NULL && *CipherList != '\0')
- {
- if (SSL_CTX_set_cipher_list(*ctx, CipherList) <= 0)
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, error: SSL_CTX_set_cipher_list(%s) failed, list ignored",
- who, CipherList);
-
- if (LogLevel > 9)
- tlslogerr(who);
- }
- /* failure if setting to this list is required? */
- }
- }
-# endif /* _FFR_TLS_1 */
- if (LogLevel > 12)
- sm_syslog(LOG_INFO, NOQID, "STARTTLS=%s, init=%d", who, ok);
-
-# if _FFR_TLS_1
-# if 0
- /*
- ** this label is required if we want to have a "clean" exit
- ** see the comments above at the initialization of cf2
- */
-
- endinittls:
-# endif /* 0 */
-
- /* undo damage to global variables */
- if (cf2 != NULL)
- *--cf2 = ',';
- if (kf2 != NULL)
- *--kf2 = ',';
-# endif /* _FFR_TLS_1 */
-
- return ok;
-}
-/*
-** TLS_GET_INFO -- get information about TLS connection
-**
-** Parameters:
-** ssl -- TLS connection structure
-** srv -- server or client
-** host -- hostname of other side
-** mac -- macro storage
-** certreq -- did we ask for a cert?
-**
-** Returns:
-** result of authentication.
-**
-** Side Effects:
-** sets macros: {cipher}, {tls_version}, {verify},
-** {cipher_bits}, {alg_bits}, {cert}, {cert_subject},
-** {cert_issuer}, {cn_subject}, {cn_issuer}
-*/
-
-int
-tls_get_info(ssl, srv, host, mac, certreq)
- SSL *ssl;
- bool srv;
- char *host;
- MACROS_T *mac;
- bool certreq;
-{
- SSL_CIPHER *c;
- int b, r;
- long verifyok;
- char *s, *who;
- char bitstr[16];
- X509 *cert;
-
- c = SSL_get_current_cipher(ssl);
-
- /* cast is just workaround for compiler warning */
- macdefine(mac, A_TEMP, macid("{cipher}"),
- (char *) SSL_CIPHER_get_name(c));
- b = SSL_CIPHER_get_bits(c, &r);
- (void) sm_snprintf(bitstr, sizeof(bitstr), "%d", b);
- macdefine(mac, A_TEMP, macid("{cipher_bits}"), bitstr);
- (void) sm_snprintf(bitstr, sizeof(bitstr), "%d", r);
- macdefine(mac, A_TEMP, macid("{alg_bits}"), bitstr);
- s = SSL_CIPHER_get_version(c);
- if (s == NULL)
- s = "UNKNOWN";
- macdefine(mac, A_TEMP, macid("{tls_version}"), s);
-
- who = srv ? "server" : "client";
- cert = SSL_get_peer_certificate(ssl);
- verifyok = SSL_get_verify_result(ssl);
- if (LogLevel > 14)
- sm_syslog(LOG_INFO, NOQID,
- "STARTTLS=%s, get_verify: %ld get_peer: 0x%lx",
- who, verifyok, (unsigned long) cert);
- if (cert != NULL)
- {
- unsigned int n;
- unsigned char md[EVP_MAX_MD_SIZE];
- char buf[MAXNAME];
-
- X509_NAME_oneline(X509_get_subject_name(cert),
- buf, sizeof(buf));
- macdefine(mac, A_TEMP, macid("{cert_subject}"),
- xtextify(buf, "<>\")"));
- X509_NAME_oneline(X509_get_issuer_name(cert),
- buf, sizeof(buf));
- macdefine(mac, A_TEMP, macid("{cert_issuer}"),
- xtextify(buf, "<>\")"));
- X509_NAME_get_text_by_NID(X509_get_subject_name(cert),
- NID_commonName, buf, sizeof(buf));
- macdefine(mac, A_TEMP, macid("{cn_subject}"),
- xtextify(buf, "<>\")"));
- X509_NAME_get_text_by_NID(X509_get_issuer_name(cert),
- NID_commonName, buf, sizeof(buf));
- macdefine(mac, A_TEMP, macid("{cn_issuer}"),
- xtextify(buf, "<>\")"));
- n = 0;
- if (X509_digest(cert, EVP_md5(), md, &n) != 0 && n > 0)
- {
- char md5h[EVP_MAX_MD_SIZE * 3];
- static const char hexcodes[] = "0123456789ABCDEF";
-
- SM_ASSERT((n * 3) + 2 < sizeof(md5h));
- for (r = 0; r < (int) n; r++)
- {
- md5h[r * 3] = hexcodes[(md[r] & 0xf0) >> 4];
- md5h[(r * 3) + 1] = hexcodes[(md[r] & 0x0f)];
- md5h[(r * 3) + 2] = ':';
- }
- md5h[(n * 3) - 1] = '\0';
- macdefine(mac, A_TEMP, macid("{cert_md5}"), md5h);
- }
- else
- macdefine(mac, A_TEMP, macid("{cert_md5}"), "");
- }
- else
- {
- macdefine(mac, A_PERM, macid("{cert_subject}"), "");
- macdefine(mac, A_PERM, macid("{cert_issuer}"), "");
- macdefine(mac, A_PERM, macid("{cn_subject}"), "");
- macdefine(mac, A_PERM, macid("{cn_issuer}"), "");
- macdefine(mac, A_TEMP, macid("{cert_md5}"), "");
- }
- switch (verifyok)
- {
- case X509_V_OK:
- if (cert != NULL)
- {
- s = "OK";
- r = TLS_AUTH_OK;
- }
- else
- {
- s = certreq ? "NO" : "NOT",
- r = TLS_AUTH_NO;
- }
- break;
- default:
- s = "FAIL";
- r = TLS_AUTH_FAIL;
- break;
- }
- macdefine(mac, A_PERM, macid("{verify}"), s);
- if (cert != NULL)
- X509_free(cert);
-
- /* do some logging */
- if (LogLevel > 8)
- {
- char *vers, *s1, *s2, *cbits, *algbits;
-
- vers = macget(mac, macid("{tls_version}"));
- cbits = macget(mac, macid("{cipher_bits}"));
- algbits = macget(mac, macid("{alg_bits}"));
- s1 = macget(mac, macid("{verify}"));
- s2 = macget(mac, macid("{cipher}"));
-
- /* XXX: maybe cut off ident info? */
- sm_syslog(LOG_INFO, NOQID,
- "STARTTLS=%s, relay=%.100s, version=%.16s, verify=%.16s, cipher=%.64s, bits=%.6s/%.6s",
- who,
- host == NULL ? "local" : host,
- vers, s1, s2, /* sm_snprintf() can deal with NULL */
- algbits == NULL ? "0" : algbits,
- cbits == NULL ? "0" : cbits);
- if (LogLevel > 11)
- {
- /*
- ** Maybe run xuntextify on the strings?
- ** That is easier to read but makes it maybe a bit
- ** more complicated to figure out the right values
- ** for the access map...
- */
-
- s1 = macget(mac, macid("{cert_subject}"));
- s2 = macget(mac, macid("{cert_issuer}"));
- sm_syslog(LOG_INFO, NOQID,
- "STARTTLS=%s, cert-subject=%.256s, cert-issuer=%.256s, verifymsg=%s",
- who, s1, s2,
- X509_verify_cert_error_string(verifyok));
- }
- }
- return r;
-}
-/*
-** ENDTLS -- shutdown secure connection
-**
-** Parameters:
-** ssl -- SSL connection information.
-** side -- server/client (for logging).
-**
-** Returns:
-** success? (EX_* code)
-*/
-
-int
-endtls(ssl, side)
- SSL *ssl;
- char *side;
-{
- int ret = EX_OK;
-
- if (ssl != NULL)
- {
- int r;
-
- if ((r = SSL_shutdown(ssl)) < 0)
- {
- if (LogLevel > 11)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, SSL_shutdown failed: %d",
- side, r);
- tlslogerr(side);
- }
- ret = EX_SOFTWARE;
- }
-# if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER > 0x0090602fL
-
- /*
- ** Bug in OpenSSL (at least up to 0.9.6b):
- ** From: Lutz.Jaenicke@aet.TU-Cottbus.DE
- ** Message-ID: <20010723152244.A13122@serv01.aet.tu-cottbus.de>
- ** To: openssl-users@openssl.org
- ** Subject: Re: SSL_shutdown() woes (fwd)
- **
- ** The side sending the shutdown alert first will
- ** not care about the answer of the peer but will
- ** immediately return with a return value of "0"
- ** (ssl/s3_lib.c:ssl3_shutdown()). SSL_get_error will evaluate
- ** the value of "0" and as the shutdown alert of the peer was
- ** not received (actually, the program did not even wait for
- ** the answer), an SSL_ERROR_SYSCALL is flagged, because this
- ** is the default rule in case everything else does not apply.
- **
- ** For your server the problem is different, because it
- ** receives the shutdown first (setting SSL_RECEIVED_SHUTDOWN),
- ** then sends its response (SSL_SENT_SHUTDOWN), so for the
- ** server the shutdown was successfull.
- **
- ** As is by know, you would have to call SSL_shutdown() once
- ** and ignore an SSL_ERROR_SYSCALL returned. Then call
- ** SSL_shutdown() again to actually get the server's response.
- **
- ** In the last discussion, Bodo Moeller concluded that a
- ** rewrite of the shutdown code would be necessary, but
- ** probably with another API, as the change would not be
- ** compatible to the way it is now. Things do not become
- ** easier as other programs do not follow the shutdown
- ** guidelines anyway, so that a lot error conditions and
- ** compitibility issues would have to be caught.
- **
- ** For now the recommondation is to ignore the error message.
- */
-
- else if (r == 0)
- {
- if (LogLevel > 15)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s, SSL_shutdown not done",
- side);
- tlslogerr(side);
- }
- ret = EX_SOFTWARE;
- }
-# endif /* !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER > 0x0090602fL */
- SSL_free(ssl);
- ssl = NULL;
- }
- return ret;
-}
-
-# if !TLS_NO_RSA
-/*
-** TMP_RSA_KEY -- return temporary RSA key
-**
-** Parameters:
-** s -- TLS connection structure
-** export --
-** keylength --
-**
-** Returns:
-** temporary RSA key.
-*/
-
-# ifndef MAX_RSA_TMP_CNT
-# define MAX_RSA_TMP_CNT 1000 /* XXX better value? */
-# endif /* ! MAX_RSA_TMP_CNT */
-
-/* ARGUSED0 */
-static RSA *
-tmp_rsa_key(s, export, keylength)
- SSL *s;
- int export;
- int keylength;
-{
-# if SM_CONF_SHM
- extern int ShmId;
- extern int *PRSATmpCnt;
-
- if (ShmId != SM_SHM_NO_ID && rsa_tmp != NULL &&
- ++(*PRSATmpCnt) < MAX_RSA_TMP_CNT)
- return rsa_tmp;
-# endif /* SM_CONF_SHM */
-
- if (rsa_tmp != NULL)
- RSA_free(rsa_tmp);
- rsa_tmp = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, NULL, NULL);
- if (rsa_tmp == NULL)
- {
- if (LogLevel > 0)
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS=server, tmp_rsa_key: RSA_generate_key failed!");
- }
- else
- {
-# if SM_CONF_SHM
-# if 0
- /*
- ** XXX we can't (yet) share the new key...
- ** The RSA structure contains pointers hence it can't be
- ** easily kept in shared memory. It must be transformed
- ** into a continous memory region first, then stored,
- ** and later read out again (each time re-transformed).
- */
-
- if (ShmId != SM_SHM_NO_ID)
- *PRSATmpCnt = 0;
-# endif /* 0 */
-# endif /* SM_CONF_SHM */
- if (LogLevel > 9)
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS=server, tmp_rsa_key: new temp RSA key");
- }
- return rsa_tmp;
-}
-# endif /* !TLS_NO_RSA */
-/*
-** APPS_SSL_INFO_CB -- info callback for TLS connections
-**
-** Parameters:
-** s -- TLS connection structure
-** where -- state in handshake
-** ret -- return code of last operation
-**
-** Returns:
-** none.
-*/
-
-static void
-apps_ssl_info_cb(s, where, ret)
- CONST097 SSL *s;
- int where;
- int ret;
-{
- int w;
- char *str;
- BIO *bio_err = NULL;
-
- if (LogLevel > 14)
- sm_syslog(LOG_INFO, NOQID,
- "STARTTLS: info_callback where=0x%x, ret=%d",
- where, ret);
-
- w = where & ~SSL_ST_MASK;
- if (bio_err == NULL)
- bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
-
- if (bitset(SSL_ST_CONNECT, w))
- str = "SSL_connect";
- else if (bitset(SSL_ST_ACCEPT, w))
- str = "SSL_accept";
- else
- str = "undefined";
-
- if (bitset(SSL_CB_LOOP, where))
- {
- if (LogLevel > 12)
- sm_syslog(LOG_NOTICE, NOQID,
- "STARTTLS: %s:%s",
- str, SSL_state_string_long(s));
- }
- else if (bitset(SSL_CB_ALERT, where))
- {
- str = bitset(SSL_CB_READ, where) ? "read" : "write";
- if (LogLevel > 12)
- sm_syslog(LOG_NOTICE, NOQID,
- "STARTTLS: SSL3 alert %s:%s:%s",
- str, SSL_alert_type_string_long(ret),
- SSL_alert_desc_string_long(ret));
- }
- else if (bitset(SSL_CB_EXIT, where))
- {
- if (ret == 0)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: %s:failed in %s",
- str, SSL_state_string_long(s));
- }
- else if (ret < 0)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS: %s:error in %s",
- str, SSL_state_string_long(s));
- }
- }
-}
-/*
-** TLS_VERIFY_LOG -- log verify error for TLS certificates
-**
-** Parameters:
-** ok -- verify ok?
-** ctx -- x509 context
-**
-** Returns:
-** 0 -- fatal error
-** 1 -- ok
-*/
-
-static int
-tls_verify_log(ok, ctx, name)
- int ok;
- X509_STORE_CTX *ctx;
- char *name;
-{
- SSL *ssl;
- X509 *cert;
- int reason, depth;
- char buf[512];
-
- cert = X509_STORE_CTX_get_current_cert(ctx);
- reason = X509_STORE_CTX_get_error(ctx);
- depth = X509_STORE_CTX_get_error_depth(ctx);
- ssl = (SSL *) X509_STORE_CTX_get_ex_data(ctx,
- SSL_get_ex_data_X509_STORE_CTX_idx());
-
- if (ssl == NULL)
- {
- /* internal error */
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS: internal error: tls_verify_cb: ssl == NULL");
- return 0;
- }
-
- X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
- sm_syslog(LOG_INFO, NOQID,
- "STARTTLS: %s cert verify: depth=%d %s, state=%d, reason=%s",
- name, 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
-# if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x00907000L
-tls_verify_cb(ctx)
- X509_STORE_CTX *ctx;
-# else /* !defined() || OPENSSL_VERSION_NUMBER < 0x00907000L */
-tls_verify_cb(ctx, unused)
- X509_STORE_CTX *ctx;
- void *unused;
-# endif /* !defined() || OPENSSL_VERSION_NUMBER < 0x00907000L */
-{
- int ok;
-
- ok = X509_verify_cert(ctx);
- if (ok == 0)
- {
- if (LogLevel > 13)
- return tls_verify_log(ok, ctx, "TLS");
- return 1; /* override it */
- }
- return ok;
-}
-/*
-** TLSLOGERR -- log the errors from the TLS error stack
-**
-** Parameters:
-** who -- server/client (for logging).
-**
-** Returns:
-** none.
-*/
-
-void
-tlslogerr(who)
- const char *who;
-{
- unsigned long l;
- int line, flags;
- unsigned long es;
- char *file, *data;
- char buf[256];
-# define CP (const char **)
-
- es = CRYPTO_thread_id();
- while ((l = ERR_get_error_line_data(CP &file, &line, CP &data, &flags))
- != 0)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=%s: %lu:%s:%s:%d:%s", who, es,
- ERR_error_string(l, buf),
- file, line,
- bitset(ERR_TXT_STRING, flags) ? data : "");
- }
-}
-
-# if OPENSSL_VERSION_NUMBER > 0x00907000L
-/*
-** X509_VERIFY_CB -- verify callback
-**
-** Parameters:
-** ctx -- x509 context
-**
-** Returns:
-** accept connection?
-** currently: always yes.
-*/
-
-static int
-x509_verify_cb(ok, ctx)
- int ok;
- X509_STORE_CTX *ctx;
-{
- if (ok == 0)
- {
- if (LogLevel > 13)
- tls_verify_log(ok, ctx, "x509");
- if (ctx->error == X509_V_ERR_UNABLE_TO_GET_CRL)
- {
- ctx->error = 0;
- return 1; /* override it */
- }
- }
- return ok;
-}
-# endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
-#endif /* STARTTLS */
diff --git a/contrib/sendmail/src/trace.c b/contrib/sendmail/src/trace.c
deleted file mode 100644
index 4a9051e..0000000
--- a/contrib/sendmail/src/trace.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * 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.
- *
- */
-
-#include <sendmail.h>
-#include <sm/debug.h>
-#include <sm/string.h>
-
-SM_RCSID("@(#)$Id: trace.c,v 8.38 2002/12/05 17:28:35 ca Exp $")
-
-static char *tTnewflag __P((char *));
-static char *tToldflag __P((char *));
-
-/*
-** TtSETUP -- set up for trace package.
-**
-** Parameters:
-** vect -- pointer to trace vector.
-** size -- number of flags in trace vector.
-** defflags -- flags to set if no value given.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** environment is set up.
-*/
-
-static unsigned char *tTvect;
-static unsigned int tTsize;
-static char *DefFlags;
-
-void
-tTsetup(vect, size, defflags)
- unsigned char *vect;
- unsigned int size;
- char *defflags;
-{
- tTvect = vect;
- tTsize = size;
- DefFlags = defflags;
-}
-
-/*
-** tToldflag -- process an old style trace flag
-**
-** Parameters:
-** s -- points to a [\0, \t] terminated string,
-** and the initial character is a digit.
-**
-** Returns:
-** pointer to terminating [\0, \t] character
-**
-** Side Effects:
-** modifies tTvect
-*/
-
-static char *
-tToldflag(s)
- register char *s;
-{
- unsigned int first, last;
- register unsigned int i;
-
- /* find first flag to set */
- i = 0;
- while (isascii(*s) && isdigit(*s) && i < tTsize)
- i = i * 10 + (*s++ - '0');
-
- /*
- ** skip over rest of a too large number
- ** Maybe we should complain if out-of-bounds values are used.
- */
-
- while (isascii(*s) && isdigit(*s) && i >= tTsize)
- s++;
- first = i;
-
- /* find last flag to set */
- if (*s == '-')
- {
- i = 0;
- while (isascii(*++s) && isdigit(*s) && i < tTsize)
- i = i * 10 + (*s - '0');
-
- /* skip over rest of a too large number */
- while (isascii(*s) && isdigit(*s) && i >= tTsize)
- s++;
- }
- last = i;
-
- /* find the level to set it to */
- i = 1;
- if (*s == '.')
- {
- i = 0;
- while (isascii(*++s) && isdigit(*s))
- i = i * 10 + (*s - '0');
- }
-
- /* clean up args */
- if (first >= tTsize)
- first = tTsize - 1;
- if (last >= tTsize)
- last = tTsize - 1;
-
- /* set the flags */
- while (first <= last)
- tTvect[first++] = (unsigned char) i;
-
- /* skip trailing junk */
- while (*s != '\0' && *s != ',' && *s != ' ' && *s != '\t')
- ++s;
-
- return s;
-}
-
-/*
-** tTnewflag -- process a new style trace flag
-**
-** Parameters:
-** s -- Points to a non-empty [\0, \t] terminated string,
-** of which the initial character is not a digit.
-**
-** Returns:
-** pointer to terminating [\0, \t] character
-**
-** Side Effects:
-** adds trace flag to libsm debug database
-*/
-
-static char *
-tTnewflag(s)
- register char *s;
-{
- char *pat, *endpat;
- int level;
-
- pat = s;
- while (*s != '\0' && *s != ',' && *s != ' ' && *s != '\t' && *s != '.')
- ++s;
- endpat = s;
- if (*s == '.')
- {
- ++s;
- level = 0;
- while (isascii(*s) && isdigit(*s))
- {
- level = level * 10 + (*s - '0');
- ++s;
- }
- if (level < 0)
- level = 0;
- }
- else
- {
- level = 1;
- }
-
- sm_debug_addsetting_x(sm_strndup_x(pat, endpat - pat), level);
-
- /* skip trailing junk */
- while (*s != '\0' && *s != ',' && *s != ' ' && *s != '\t')
- ++s;
-
- return s;
-}
-
-/*
-** TtFLAG -- process an external trace flag list.
-**
-** Parameters:
-** s -- the trace flag.
-**
-** The syntax of a trace flag list is as follows:
-**
-** <flags> ::= <flag> | <flags> "," <flag>
-** <flag> ::= <categories> | <categories> "." <level>
-** <categories> ::= <int> | <int> "-" <int> | <pattern>
-** <pattern> ::= <an sh glob pattern matching a C identifier>
-**
-** White space is ignored before and after a flag.
-** However, note that we skip over anything we don't
-** understand, rather than report an error.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** sets/clears old-style trace flags.
-** registers new-style trace flags with the libsm debug package.
-*/
-
-void
-tTflag(s)
- register char *s;
-{
- if (s == NULL || *s == '\0')
- s = DefFlags;
-
- for (;;)
- {
- if (*s == '\0')
- return;
- if (*s == ',' || *s == ' ' || *s == '\t')
- {
- ++s;
- continue;
- }
- if (isascii(*s) && isdigit(*s))
- s = tToldflag(s);
- else
- s = tTnewflag(s);
- }
-}
diff --git a/contrib/sendmail/src/udb.c b/contrib/sendmail/src/udb.c
deleted file mode 100644
index 29470e7..0000000
--- a/contrib/sendmail/src/udb.c
+++ /dev/null
@@ -1,1314 +0,0 @@
-/*
- * Copyright (c) 1998-2003, 2006 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.
- *
- */
-
-#include <sendmail.h>
-#include "map.h"
-
-#if USERDB
-SM_RCSID("@(#)$Id: udb.c,v 8.164 2006/12/19 19:49:51 ca Exp $ (with USERDB)")
-#else /* USERDB */
-SM_RCSID("@(#)$Id: udb.c,v 8.164 2006/12/19 19:49:51 ca Exp $ (without USERDB)")
-#endif /* USERDB */
-
-#if USERDB
-
-#include <sm/sendmail.h>
-# if NEWDB
-# include "sm/bdb.h"
-# else /* NEWDB */
-# define DBT struct _data_base_thang_
-DBT
-{
- void *data; /* pointer to data */
- size_t size; /* length of data */
-};
-# endif /* NEWDB */
-
-/*
-** UDB.C -- interface between sendmail and Berkeley User Data Base.
-**
-** This depends on the 4.4BSD db package.
-*/
-
-
-struct udbent
-{
- char *udb_spec; /* string version of spec */
- int udb_type; /* type of entry */
- pid_t udb_pid; /* PID of process which opened db */
- char *udb_default; /* default host for outgoing mail */
- union
- {
-# if NETINET || NETINET6
- /* type UE_REMOTE -- do remote call for lookup */
- struct
- {
- 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
-# 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
-
-# if 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 /* 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 MAXUDBENT 10 /* maximum number of UDB entries */
-
-
-struct udb_option
-{
- char *udbo_name;
- char *udbo_val;
-};
-
-# if HESIOD
-static int hes_udb_get __P((DBT *, DBT *));
-# endif /* HESIOD */
-static char *udbmatch __P((char *, char *, SM_RPOOL_T *));
-static int _udbx_init __P((ENVELOPE *));
-static int _udb_parsespec __P((char *, struct udb_option [], int));
-
-/*
-** UDBEXPAND -- look up user in database and expand
-**
-** Parameters:
-** a -- address to expand.
-** sendq -- pointer to head of sendq to put the expansions in.
-** aliaslevel -- the current alias nesting depth.
-** e -- the current envelope.
-**
-** Returns:
-** EX_TEMPFAIL -- if something "odd" happened -- probably due
-** to accessing a file on an NFS server that is down.
-** EX_OK -- otherwise.
-**
-** Side Effects:
-** Modifies sendq.
-*/
-
-static struct udbent UdbEnts[MAXUDBENT + 1];
-static bool UdbInitialized = false;
-
-int
-udbexpand(a, sendq, aliaslevel, e)
- register ADDRESS *a;
- ADDRESS **sendq;
- int aliaslevel;
- register ENVELOPE *e;
-{
- int i;
- DBT key;
- DBT info;
- bool breakout;
- register struct udbent *up;
- int keylen;
- int naddrs;
- char *user;
- char keybuf[MAXUDBKEY];
-
- memset(&key, '\0', sizeof(key));
- memset(&info, '\0', sizeof(info));
-
- if (tTd(28, 1))
- sm_dprintf("udbexpand(%s)\n", a->q_paddr);
-
- /* make certain we are supposed to send to this address */
- if (!QS_IS_SENDABLE(a->q_state))
- return EX_OK;
- e->e_to = a->q_paddr;
-
- /* on first call, locate the database */
- if (!UdbInitialized)
- {
- if (_udbx_init(e) == EX_TEMPFAIL)
- return EX_TEMPFAIL;
- }
-
- /* short circuit the process if no chance of a match */
- if (UdbSpec == NULL || UdbSpec[0] == '\0')
- return EX_OK;
-
- /* extract user to do userdb matching on */
- user = a->q_user;
-
- /* short circuit name begins with '\\' since it can't possibly match */
- /* (might want to treat this as unquoted instead) */
- if (user[0] == '\\')
- return EX_OK;
-
- /* if name begins with a colon, it indicates our metadata */
- if (user[0] == ':')
- return EX_OK;
-
- keylen = sm_strlcpyn(keybuf, sizeof(keybuf), 2, user, ":maildrop");
-
- /* if name is too long, assume it won't match */
- if (keylen >= sizeof(keybuf))
- return EX_OK;
-
- /* build actual database key */
-
- breakout = false;
- for (up = UdbEnts; !breakout; up++)
- {
- int usersize;
- int userleft;
- char userbuf[MEMCHUNKSIZE];
-# if HESIOD && HES_GETMAILHOST
- char pobuf[MAXNAME];
-# endif /* HESIOD && HES_GETMAILHOST */
-# if defined(NEWDB) && DB_VERSION_MAJOR > 1
- DBC *dbc = NULL;
-# endif /* defined(NEWDB) && DB_VERSION_MAJOR > 1 */
-
- user = userbuf;
- userbuf[0] = '\0';
- usersize = sizeof(userbuf);
- userleft = sizeof(userbuf) - 1;
-
- /*
- ** Select action based on entry type.
- **
- ** On dropping out of this switch, "class" should
- ** explain the type of the data, and "user" should
- ** contain the user information.
- */
-
- switch (up->udb_type)
- {
-# if NEWDB
- case UDB_DBFETCH:
- key.data = keybuf;
- key.size = keylen;
- if (tTd(28, 80))
- sm_dprintf("udbexpand: trying %s (%d) via db\n",
- keybuf, keylen);
-# if DB_VERSION_MAJOR < 2
- i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
-# else /* DB_VERSION_MAJOR < 2 */
- i = 0;
- if (dbc == NULL &&
-# if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6
- (errno = (*up->udb_dbp->cursor)(up->udb_dbp,
- NULL, &dbc, 0)) != 0)
-# else /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
- (errno = (*up->udb_dbp->cursor)(up->udb_dbp,
- NULL, &dbc)) != 0)
-# 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 /* DB_VERSION_MAJOR < 2 */
- if (i > 0 || info.size <= 0)
- {
- if (tTd(28, 2))
- sm_dprintf("udbexpand: no match on %s (%d)\n",
- keybuf, keylen);
-# if DB_VERSION_MAJOR > 1
- if (dbc != NULL)
- {
- (void) dbc->c_close(dbc);
- dbc = NULL;
- }
-# endif /* DB_VERSION_MAJOR > 1 */
- break;
- }
- if (tTd(28, 80))
- sm_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 &&
- memcmp(key.data, keybuf, keylen) == 0)
- {
- char *p;
-
- if (bitset(EF_VRFYONLY, e->e_flags))
- {
- a->q_state = QS_VERIFIED;
-# if DB_VERSION_MAJOR > 1
- if (dbc != NULL)
- {
- (void) dbc->c_close(dbc);
- dbc = NULL;
- }
-# endif /* DB_VERSION_MAJOR > 1 */
- return EX_OK;
- }
-
- breakout = true;
- if (info.size >= userleft - 1)
- {
- char *nuser;
- int size = MEMCHUNKSIZE;
-
- if (info.size > MEMCHUNKSIZE)
- size = info.size;
- nuser = sm_malloc_x(usersize + size);
-
- memmove(nuser, user, usersize);
- if (user != userbuf)
- sm_free(user); /* XXX */
- user = nuser;
- usersize += size;
- userleft += size;
- }
- p = &user[strlen(user)];
- if (p != user)
- {
- *p++ = ',';
- userleft--;
- }
- memmove(p, info.data, info.size);
- p[info.size] = '\0';
- userleft -= info.size;
-
- /* get the next record */
-# if DB_VERSION_MAJOR < 2
- i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
-# else /* DB_VERSION_MAJOR < 2 */
- i = 0;
- if ((errno = dbc->c_get(dbc, &key,
- &info, DB_NEXT)) != 0)
- i = 1;
-# endif /* DB_VERSION_MAJOR < 2 */
- }
-
-# if DB_VERSION_MAJOR > 1
- if (dbc != NULL)
- {
- (void) dbc->c_close(dbc);
- dbc = NULL;
- }
-# endif /* DB_VERSION_MAJOR > 1 */
-
- /* if nothing ever matched, try next database */
- if (!breakout)
- break;
-
- message("expanded to %s", user);
- if (LogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "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))
- {
- sm_dprintf("udbexpand: QS_EXPANDED ");
- printaddr(sm_debug_file(), a, false);
- }
- a->q_state = QS_EXPANDED;
- }
- if (i < 0)
- {
- syserr("udbexpand: db-get %.*s stat %d",
- (int) key.size, (char *) key.data, i);
- return EX_TEMPFAIL;
- }
-
- /*
- ** If this address has a -request address, reflect
- ** it into the envelope.
- */
-
- memset(&key, '\0', sizeof(key));
- memset(&info, '\0', sizeof(info));
- (void) sm_strlcpyn(keybuf, sizeof(keybuf), 2, a->q_user,
- ":mailsender");
- keylen = strlen(keybuf);
- key.data = keybuf;
- key.size = keylen;
-
-# if DB_VERSION_MAJOR < 2
- i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
-# else /* DB_VERSION_MAJOR < 2 */
- i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
- &key, &info, 0);
-# endif /* DB_VERSION_MAJOR < 2 */
- if (i != 0 || info.size <= 0)
- break;
- a->q_owner = sm_rpool_malloc_x(e->e_rpool,
- info.size + 1);
- memmove(a->q_owner, info.data, info.size);
- a->q_owner[info.size] = '\0';
-
- /* announce delivery; NORECEIPT bit set later */
- if (e->e_xfp != NULL)
- {
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "Message delivered to mailing list %s\n",
- a->q_paddr);
- }
- e->e_flags |= EF_SENDRECEIPT;
- a->q_flags |= QDELIVERED|QEXPANDED;
- break;
-# endif /* NEWDB */
-
-# if HESIOD
- case UDB_HESIOD:
- key.data = keybuf;
- key.size = keylen;
- if (tTd(28, 80))
- sm_dprintf("udbexpand: trying %s (%d) via hesiod\n",
- keybuf, keylen);
- /* look up the key via hesiod */
- i = hes_udb_get(&key, &info);
- if (i < 0)
- {
- syserr("udbexpand: hesiod-get %.*s stat %d",
- (int) key.size, (char *) key.data, i);
- return EX_TEMPFAIL;
- }
- else if (i > 0 || info.size <= 0)
- {
-# if HES_GETMAILHOST
- struct hes_postoffice *hp;
-# endif /* HES_GETMAILHOST */
-
- if (tTd(28, 2))
- sm_dprintf("udbexpand: no match on %s (%d)\n",
- (char *) keybuf, (int) keylen);
-# if HES_GETMAILHOST
- if (tTd(28, 8))
- sm_dprintf(" ... trying hes_getmailhost(%s)\n",
- a->q_user);
- hp = hes_getmailhost(a->q_user);
- if (hp == NULL)
- {
- if (hes_error() == HES_ER_NET)
- {
- syserr("udbexpand: hesiod-getmail %s stat %d",
- a->q_user, hes_error());
- return EX_TEMPFAIL;
- }
- if (tTd(28, 2))
- sm_dprintf("hes_getmailhost(%s): %d\n",
- a->q_user, hes_error());
- break;
- }
- if (strlen(hp->po_name) + strlen(hp->po_host) >
- sizeof(pobuf) - 2)
- {
- if (tTd(28, 2))
- sm_dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n",
- a->q_user,
- hp->po_name,
- hp->po_host);
- break;
- }
- info.data = pobuf;
- (void) sm_snprintf(pobuf, sizeof(pobuf),
- "%s@%s", hp->po_name, hp->po_host);
- info.size = strlen(info.data);
-# else /* HES_GETMAILHOST */
- break;
-# endif /* HES_GETMAILHOST */
- }
- if (tTd(28, 80))
- sm_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_state = QS_VERIFIED;
- return EX_OK;
- }
-
- breakout = true;
- if (info.size >= usersize)
- user = sm_malloc_x(info.size + 1);
- memmove(user, info.data, info.size);
- user[info.size] = '\0';
-
- message("hesioded to %s", user);
- if (LogLevel > 10)
- sm_syslog(LOG_INFO, e->e_id,
- "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))
- {
- sm_dprintf("udbexpand: QS_EXPANDED ");
- printaddr(sm_debug_file(), a, false);
- }
- a->q_state = QS_EXPANDED;
- }
-
- /*
- ** If this address has a -request address, reflect
- ** it into the envelope.
- */
-
- (void) sm_strlcpyn(keybuf, sizeof(keybuf), 2, a->q_user,
- ":mailsender");
- keylen = strlen(keybuf);
- key.data = keybuf;
- key.size = keylen;
- i = hes_udb_get(&key, &info);
- if (i != 0 || info.size <= 0)
- break;
- a->q_owner = sm_rpool_malloc_x(e->e_rpool,
- info.size + 1);
- memmove(a->q_owner, info.data, info.size);
- a->q_owner[info.size] = '\0';
- break;
-# endif /* HESIOD */
-
- case UDB_REMOTE:
- /* not yet implemented */
- break;
-
- 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)
- {
- usersize = i + 1;
- user = sm_malloc_x(usersize);
- }
- (void) sm_strlcpyn(user, usersize, 3,
- a->q_user, "@", up->udb_fwdhost);
- message("expanded to %s", user);
- a->q_flags &= ~QSELFREF;
- naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
- if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
- {
- if (tTd(28, 5))
- {
- sm_dprintf("udbexpand: QS_EXPANDED ");
- printaddr(sm_debug_file(), a, false);
- }
- a->q_state = QS_EXPANDED;
- }
- breakout = true;
- break;
-
- case UDB_EOLIST:
- breakout = true;
- break;
-
- default:
- /* unknown entry type */
- break;
- }
- /* XXX if an exception occurs, there is a storage leak */
- if (user != userbuf)
- sm_free(user); /* XXX */
- }
- return EX_OK;
-}
-/*
-** UDBSENDER -- return canonical external name of sender, given local name
-**
-** Parameters:
-** sender -- the name of the sender on the local machine.
-** rpool -- resource pool from which to allocate result
-**
-** Returns:
-** The external name for this sender, if derivable from the
-** database. Storage allocated from rpool.
-** NULL -- if nothing is changed from the database.
-**
-** Side Effects:
-** none.
-*/
-
-char *
-udbsender(sender, rpool)
- char *sender;
- SM_RPOOL_T *rpool;
-{
- return udbmatch(sender, "mailname", rpool);
-}
-/*
-** UDBMATCH -- match user in field, return result of lookup.
-**
-** Parameters:
-** user -- the name of the user.
-** field -- the field to lookup.
-** rpool -- resource pool from which to allocate result
-**
-** Returns:
-** The external name for this sender, if derivable from the
-** database. Storage allocated from rpool.
-** NULL -- if nothing is changed from the database.
-**
-** Side Effects:
-** none.
-*/
-
-static char *
-udbmatch(user, field, rpool)
- char *user;
- char *field;
- SM_RPOOL_T *rpool;
-{
- register char *p;
- register struct udbent *up;
- int i;
- int keylen;
- DBT key, info;
- char keybuf[MAXUDBKEY];
-
- if (tTd(28, 1))
- sm_dprintf("udbmatch(%s, %s)\n", user, field);
-
- if (!UdbInitialized)
- {
- if (_udbx_init(CurEnv) == EX_TEMPFAIL)
- return NULL;
- }
-
- /* short circuit if no spec */
- if (UdbSpec == NULL || UdbSpec[0] == '\0')
- return NULL;
-
- /* short circuit name begins with '\\' since it can't possibly match */
- if (user[0] == '\\')
- return NULL;
-
- /* long names can never match and are a pain to deal with */
- i = strlen(field);
- if (i < sizeof("maildrop"))
- i = sizeof("maildrop");
- if ((strlen(user) + i) > sizeof(keybuf) - 4)
- return NULL;
-
- /* names beginning with colons indicate metadata */
- if (user[0] == ':')
- return NULL;
-
- /* build database key */
- (void) sm_strlcpyn(keybuf, sizeof(keybuf), 3, user, ":", field);
- keylen = strlen(keybuf);
-
- for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
- {
- /*
- ** Select action based on entry type.
- */
-
- switch (up->udb_type)
- {
-# if NEWDB
- case UDB_DBFETCH:
- memset(&key, '\0', sizeof(key));
- memset(&info, '\0', sizeof(info));
- key.data = keybuf;
- key.size = keylen;
-# if DB_VERSION_MAJOR < 2
- i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
-# else /* DB_VERSION_MAJOR < 2 */
- i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
- &key, &info, 0);
-# endif /* DB_VERSION_MAJOR < 2 */
- if (i != 0 || info.size <= 0)
- {
- if (tTd(28, 2))
- sm_dprintf("udbmatch: no match on %s (%d) via db\n",
- keybuf, keylen);
- continue;
- }
-
- p = sm_rpool_malloc_x(rpool, info.size + 1);
- memmove(p, info.data, info.size);
- p[info.size] = '\0';
- if (tTd(28, 1))
- sm_dprintf("udbmatch ==> %s\n", p);
- return p;
-# endif /* NEWDB */
-
-# if HESIOD
- case UDB_HESIOD:
- key.data = keybuf;
- key.size = keylen;
- i = hes_udb_get(&key, &info);
- if (i != 0 || info.size <= 0)
- {
- if (tTd(28, 2))
- sm_dprintf("udbmatch: no match on %s (%d) via hesiod\n",
- keybuf, keylen);
- continue;
- }
-
- p = sm_rpool_malloc_x(rpool, info.size + 1);
- memmove(p, info.data, info.size);
- p[info.size] = '\0';
- if (tTd(28, 1))
- sm_dprintf("udbmatch ==> %s\n", p);
- return p;
-# endif /* HESIOD */
- }
- }
-
- if (strcmp(field, "mailname") != 0)
- return NULL;
-
- /*
- ** Nothing yet. Search again for a default case. But only
- ** use it if we also have a forward (:maildrop) pointer already
- ** in the database.
- */
-
- /* build database key */
- (void) sm_strlcpyn(keybuf, sizeof(keybuf), 2, user, ":maildrop");
- keylen = strlen(keybuf);
-
- for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
- {
- switch (up->udb_type)
- {
-# if NEWDB
- case UDB_DBFETCH:
- /* get the default case for this database */
- if (up->udb_default == NULL)
- {
- memset(&key, '\0', sizeof(key));
- memset(&info, '\0', sizeof(info));
- key.data = ":default:mailname";
- key.size = strlen(key.data);
-# if DB_VERSION_MAJOR < 2
- i = (*up->udb_dbp->get)(up->udb_dbp,
- &key, &info, 0);
-# else /* DB_VERSION_MAJOR < 2 */
- i = errno = (*up->udb_dbp->get)(up->udb_dbp,
- NULL, &key,
- &info, 0);
-# endif /* DB_VERSION_MAJOR < 2 */
- if (i != 0 || info.size <= 0)
- {
- /* no default case */
- up->udb_default = "";
- continue;
- }
-
- /* save the default case */
- up->udb_default = sm_pmalloc_x(info.size + 1);
- 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 */
- memset(&key, '\0', sizeof(key));
- memset(&info, '\0', sizeof(info));
- key.data = keybuf;
- key.size = keylen;
-# if DB_VERSION_MAJOR < 2
- i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
-# else /* DB_VERSION_MAJOR < 2 */
- i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
- &key, &info, 0);
-# endif /* DB_VERSION_MAJOR < 2 */
- if (i != 0 || info.size <= 0)
- {
- /* nope -- no aliasing for this user */
- continue;
- }
-
- /* they exist -- build the actual address */
- i = strlen(user) + strlen(up->udb_default) + 2;
- p = sm_rpool_malloc_x(rpool, i);
- (void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default);
- if (tTd(28, 1))
- sm_dprintf("udbmatch ==> %s\n", p);
- return p;
-# endif /* NEWDB */
-
-# if 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);
-
- if (i != 0 || info.size <= 0)
- {
- /* no default case */
- up->udb_default = "";
- continue;
- }
-
- /* save the default case */
- up->udb_default = sm_pmalloc_x(info.size + 1);
- 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 */
- key.data = keybuf;
- key.size = keylen;
- i = hes_udb_get(&key, &info);
- if (i != 0 || info.size <= 0)
- {
- /* nope -- no aliasing for this user */
- continue;
- }
-
- /* they exist -- build the actual address */
- i = strlen(user) + strlen(up->udb_default) + 2;
- p = sm_rpool_malloc_x(rpool, i);
- (void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default);
- if (tTd(28, 1))
- sm_dprintf("udbmatch ==> %s\n", p);
- return p;
- break;
-# endif /* HESIOD */
- }
- }
-
- /* still nothing.... too bad */
- return NULL;
-}
-/*
-** UDB_MAP_LOOKUP -- look up arbitrary entry in user database map
-**
-** Parameters:
-** map -- the map being queried.
-** name -- the name to look up.
-** av -- arguments to the map lookup.
-** statp -- to get any error status.
-**
-** Returns:
-** NULL if name not found in map.
-** The rewritten name otherwise.
-*/
-
-/* ARGSUSED3 */
-char *
-udb_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
-{
- char *val;
- char *key;
- char *SM_NONVOLATILE result = NULL;
- char keybuf[MAXNAME + 1];
-
- if (tTd(28, 20) || tTd(38, 20))
- sm_dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name);
-
- if (bitset(MF_NOFOLDCASE, map->map_mflags))
- {
- key = name;
- }
- else
- {
- int keysize = strlen(name);
-
- if (keysize > sizeof(keybuf) - 1)
- keysize = sizeof(keybuf) - 1;
- memmove(keybuf, name, keysize);
- keybuf[keysize] = '\0';
- makelower(keybuf);
- key = keybuf;
- }
- val = udbmatch(key, map->map_file, NULL);
- if (val == NULL)
- return NULL;
- SM_TRY
- if (bitset(MF_MATCHONLY, map->map_mflags))
- result = map_rewrite(map, name, strlen(name), NULL);
- else
- result = map_rewrite(map, val, strlen(val), av);
- SM_FINALLY
- sm_free(val);
- SM_END_TRY
- return result;
-}
-/*
-** _UDBX_INIT -- parse the UDB specification, opening any valid entries.
-**
-** Parameters:
-** e -- the current envelope.
-**
-** Returns:
-** EX_TEMPFAIL -- if it appeared it couldn't get hold of a
-** database due to a host being down or some similar
-** (recoverable) situation.
-** EX_OK -- otherwise.
-**
-** Side Effects:
-** Fills in the UdbEnts structure from UdbSpec.
-*/
-
-# define MAXUDBOPTS 27
-
-static int
-_udbx_init(e)
- ENVELOPE *e;
-{
- int ents = 0;
- register char *p;
- register struct udbent *up;
-
- if (UdbInitialized)
- return EX_OK;
-
-# ifdef UDB_DEFAULT_SPEC
- if (UdbSpec == NULL)
- UdbSpec = UDB_DEFAULT_SPEC;
-# endif /* UDB_DEFAULT_SPEC */
-
- p = UdbSpec;
- up = UdbEnts;
- while (p != NULL)
- {
- char *spec;
- int l;
- struct udb_option opts[MAXUDBOPTS + 1];
-
- while (*p == ' ' || *p == '\t' || *p == ',')
- p++;
- if (*p == '\0')
- break;
- spec = p;
- p = strchr(p, ',');
- if (p != NULL)
- *p++ = '\0';
-
- if (ents >= MAXUDBENT)
- {
- syserr("Maximum number of UDB entries exceeded");
- break;
- }
-
- /* extract options */
- (void) _udb_parsespec(spec, opts, MAXUDBOPTS);
-
- /*
- ** Decode database specification.
- **
- ** In the sendmail tradition, the leading character
- ** defines the semantics of the rest of the entry.
- **
- ** @hostname -- forward email to the indicated host.
- ** This should be the last in the list,
- ** since it always matches the input.
- ** /dbname -- search the named database on the local
- ** host using the Berkeley db package.
- ** Hesiod -- search the named database with BIND
- ** using the MIT Hesiod package.
- */
-
- switch (*spec)
- {
- case '@': /* forward to remote host */
- up->udb_type = UDB_FORWARD;
- up->udb_pid = CurrentPid;
- up->udb_fwdhost = spec + 1;
- ents++;
- up++;
- break;
-
-# if HESIOD
- case 'h': /* use hesiod */
- case 'H':
- if (sm_strcasecmp(spec, "hesiod") != 0)
- goto badspec;
- up->udb_type = UDB_HESIOD;
- up->udb_pid = CurrentPid;
- ents++;
- up++;
- break;
-# endif /* HESIOD */
-
-# if NEWDB
- case '/': /* look up remote name */
- l = strlen(spec);
- if (l > 3 && strcmp(&spec[l - 3], ".db") == 0)
- {
- up->udb_dbname = spec;
- }
- else
- {
- up->udb_dbname = sm_pmalloc_x(l + 4);
- (void) sm_strlcpyn(up->udb_dbname, l + 4, 2,
- spec, ".db");
- }
- errno = 0;
-# if DB_VERSION_MAJOR < 2
- up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY,
- 0644, DB_BTREE, NULL);
-# else /* DB_VERSION_MAJOR < 2 */
- {
- int flags = DB_RDONLY;
-# if DB_VERSION_MAJOR > 2
- int ret;
-# endif /* DB_VERSION_MAJOR > 2 */
-
- SM_DB_FLAG_ADD(flags);
- 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,
- DBTXN
- up->udb_dbname,
- NULL,
- DB_BTREE,
- flags,
- 0644);
- if (ret != 0)
- {
-#ifdef DB_OLD_VERSION
- if (ret == DB_OLD_VERSION)
- ret = EINVAL;
-#endif /* DB_OLD_VERSION */
- (void) up->udb_dbp->close(up->udb_dbp, 0);
- up->udb_dbp = NULL;
- }
- }
- 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 save_errno = errno;
-
-# if DB_VERSION_MAJOR < 2
- sm_dprintf("dbopen(%s): %s\n",
-# else /* DB_VERSION_MAJOR < 2 */
- sm_dprintf("db_open(%s): %s\n",
-# endif /* DB_VERSION_MAJOR < 2 */
- up->udb_dbname,
- sm_errstring(errno));
- 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_VERSION_MAJOR < 2 */
- "db_open(%s): %s",
-# endif /* DB_VERSION_MAJOR < 2 */
- up->udb_dbname,
- sm_errstring(errno));
- up->udb_type = UDB_EOLIST;
- if (up->udb_dbname != spec)
- sm_free(up->udb_dbname); /* XXX */
- goto tempfail;
- }
- if (up->udb_dbname != spec)
- sm_free(up->udb_dbname); /* XXX */
- break;
- }
- if (tTd(28, 1))
- {
-# if DB_VERSION_MAJOR < 2
- sm_dprintf("_udbx_init: dbopen(%s)\n",
-# else /* DB_VERSION_MAJOR < 2 */
- sm_dprintf("_udbx_init: db_open(%s)\n",
-# endif /* DB_VERSION_MAJOR < 2 */
- up->udb_dbname);
- }
- up->udb_type = UDB_DBFETCH;
- up->udb_pid = CurrentPid;
- ents++;
- up++;
- break;
-# endif /* NEWDB */
-
- default:
-# if HESIOD
-badspec:
-# endif /* HESIOD */
- syserr("Unknown UDB spec %s", spec);
- break;
- }
- }
- up->udb_type = UDB_EOLIST;
-
- if (tTd(28, 4))
- {
- for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
- {
- switch (up->udb_type)
- {
- case UDB_REMOTE:
- sm_dprintf("REMOTE: addr %s, timeo %d\n",
- anynet_ntoa((SOCKADDR *) &up->udb_addr),
- up->udb_timeout);
- break;
-
- case UDB_DBFETCH:
-# if NEWDB
- sm_dprintf("FETCH: file %s\n",
- up->udb_dbname);
-# else /* NEWDB */
- sm_dprintf("FETCH\n");
-# endif /* NEWDB */
- break;
-
- case UDB_FORWARD:
- sm_dprintf("FORWARD: host %s\n",
- up->udb_fwdhost);
- break;
-
- case UDB_HESIOD:
- sm_dprintf("HESIOD\n");
- break;
-
- default:
- sm_dprintf("UNKNOWN\n");
- break;
- }
- }
- }
-
- UdbInitialized = true;
- errno = 0;
- return EX_OK;
-
- /*
- ** On temporary failure, back out anything we've already done
- */
-
- tempfail:
-# if NEWDB
- for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
- {
- if (up->udb_type == UDB_DBFETCH)
- {
-# if DB_VERSION_MAJOR < 2
- (*up->udb_dbp->close)(up->udb_dbp);
-# else /* DB_VERSION_MAJOR < 2 */
- errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
-# endif /* DB_VERSION_MAJOR < 2 */
- if (tTd(28, 1))
- sm_dprintf("_udbx_init: db->close(%s)\n",
- up->udb_dbname);
- }
- }
-# endif /* NEWDB */
- return EX_TEMPFAIL;
-}
-
-static int
-_udb_parsespec(udbspec, opt, maxopts)
- char *udbspec;
- struct udb_option opt[];
- int maxopts;
-{
- register char *spec;
- register char *spec_end;
- register int optnum;
-
- spec_end = strchr(udbspec, ':');
- for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
- {
- register char *p;
-
- while (isascii(*spec) && isspace(*spec))
- spec++;
- spec_end = strchr(spec, ':');
- if (spec_end != NULL)
- *spec_end++ = '\0';
-
- opt[optnum].udbo_name = spec;
- opt[optnum].udbo_val = NULL;
- p = strchr(spec, '=');
- if (p != NULL)
- opt[optnum].udbo_val = ++p;
- }
- return optnum;
-}
-/*
-** _UDBX_CLOSE -- close all file based UDB entries.
-**
-** Parameters:
-** none
-**
-** Returns:
-** none
-*/
-void
-_udbx_close()
-{
- struct udbent *up;
-
- if (!UdbInitialized)
- return;
-
- for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
- {
- if (up->udb_pid != CurrentPid)
- continue;
-
-# if NEWDB
- if (up->udb_type == UDB_DBFETCH)
- {
-# if DB_VERSION_MAJOR < 2
- (*up->udb_dbp->close)(up->udb_dbp);
-# else /* DB_VERSION_MAJOR < 2 */
- errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
-# endif /* DB_VERSION_MAJOR < 2 */
- }
- if (tTd(28, 1))
- sm_dprintf("_udbx_init: db->close(%s)\n",
- up->udb_dbname);
-# endif /* NEWDB */
- }
-}
-
-# if HESIOD
-
-static int
-hes_udb_get(key, info)
- DBT *key;
- DBT *info;
-{
- char *name, *type;
- char **hp;
- char kbuf[MAXUDBKEY + 1];
-
- if (sm_strlcpy(kbuf, key->data, sizeof(kbuf)) >= sizeof(kbuf))
- return 0;
- name = kbuf;
- type = strrchr(name, ':');
- if (type == NULL)
- return 1;
- *type++ = '\0';
- if (strchr(name, '@') != NULL)
- return 1;
-
- if (tTd(28, 1))
- sm_dprintf("hes_udb_get(%s, %s)\n", name, type);
-
- /* make the hesiod query */
-# ifdef HESIOD_INIT
- if (HesiodContext == NULL && hesiod_init(&HesiodContext) != 0)
- return -1;
- hp = hesiod_resolve(HesiodContext, name, type);
-# else /* HESIOD_INIT */
- hp = hes_resolve(name, type);
-# endif /* HESIOD_INIT */
- *--type = ':';
-# ifdef HESIOD_INIT
- if (hp == NULL)
- return 1;
- if (*hp == NULL)
- {
- hesiod_free_list(HesiodContext, hp);
- if (errno == ECONNREFUSED || errno == EMSGSIZE)
- return -1;
- return 1;
- }
-# else /* HESIOD_INIT */
- if (hp == NULL || hp[0] == NULL)
- {
- /* network problem or timeout */
- if (hes_error() == HES_ER_NET)
- return -1;
-
- return 1;
- }
-# endif /* HESIOD_INIT */
- else
- {
- /*
- ** If there are multiple matches, just return the
- ** first one.
- **
- ** XXX These should really be returned; for example,
- ** XXX it is legal for :maildrop to be multi-valued.
- */
-
- info->data = hp[0];
- info->size = (size_t) strlen(info->data);
- }
-
- if (tTd(28, 80))
- sm_dprintf("hes_udb_get => %s\n", *hp);
-
- return 0;
-}
-# endif /* HESIOD */
-
-#else /* USERDB */
-
-int
-udbexpand(a, sendq, aliaslevel, e)
- ADDRESS *a;
- ADDRESS **sendq;
- int aliaslevel;
- ENVELOPE *e;
-{
- return EX_OK;
-}
-
-#endif /* USERDB */
diff --git a/contrib/sendmail/src/usersmtp.c b/contrib/sendmail/src/usersmtp.c
deleted file mode 100644
index 724f10c..0000000
--- a/contrib/sendmail/src/usersmtp.c
+++ /dev/null
@@ -1,3318 +0,0 @@
-/*
- * Copyright (c) 1998-2006 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: usersmtp.c,v 8.470 2007/10/17 21:35:30 ca Exp $")
-
-#include <sysexits.h>
-
-
-static void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
-static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
-static int smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
-
-#if SASL
-extern void *sm_sasl_malloc __P((unsigned long));
-extern void sm_sasl_free __P((void *));
-#endif /* SASL */
-
-/*
-** USERSMTP -- run SMTP protocol from the user end.
-**
-** This protocol is described in RFC821.
-*/
-
-#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
-#define SMTPCLOSING 421 /* "Service Shutting Down" */
-
-#define ENHSCN(e, d) ((e) == NULL ? (d) : (e))
-
-#define ENHSCN_RPOOL(e, d, rpool) \
- ((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
-
-static char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
-static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
-static bool SmtpNeedIntro; /* need "while talking" in transcript */
-/*
-** SMTPINIT -- initialize SMTP.
-**
-** Opens the connection and sends the initial protocol.
-**
-** Parameters:
-** m -- mailer to create connection to.
-** mci -- the mailer connection info.
-** e -- the envelope.
-** onlyhelo -- send only helo command?
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** creates connection and sends initial protocol.
-*/
-
-void
-smtpinit(m, mci, e, onlyhelo)
- MAILER *m;
- register MCI *mci;
- ENVELOPE *e;
- bool onlyhelo;
-{
- register int r;
- int state;
- register char *p;
- register char *hn;
- char *enhsc;
-
- enhsc = NULL;
- if (tTd(18, 1))
- {
- sm_dprintf("smtpinit ");
- mci_dump(sm_debug_file(), mci, false);
- }
-
- /*
- ** Open the connection to the mailer.
- */
-
- SmtpError[0] = '\0';
- SmtpMsgBuffer[0] = '\0';
- CurHostName = mci->mci_host; /* XXX UGLY XXX */
- if (CurHostName == NULL)
- CurHostName = MyHostName;
- SmtpNeedIntro = true;
- state = mci->mci_state;
- switch (state)
- {
- case MCIS_MAIL:
- case MCIS_RCPT:
- case MCIS_DATA:
- /* need to clear old information */
- smtprset(m, mci, e);
- /* FALLTHROUGH */
-
- case MCIS_OPEN:
- if (!onlyhelo)
- return;
- break;
-
- case MCIS_ERROR:
- case MCIS_QUITING:
- case MCIS_SSD:
- /* shouldn't happen */
- smtpquit(m, mci, e);
- /* FALLTHROUGH */
-
- case MCIS_CLOSED:
- syserr("451 4.4.0 smtpinit: state CLOSED (was %d)", state);
- return;
-
- case MCIS_OPENING:
- break;
- }
- if (onlyhelo)
- goto helo;
-
- mci->mci_state = MCIS_OPENING;
- clrsessenvelope(e);
-
- /*
- ** Get the greeting message.
- ** This should appear spontaneously. Give it five minutes to
- ** happen.
- */
-
- SmtpPhase = mci->mci_phase = "client greeting";
- 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,
- XS_DEFAULT);
- if (r < 0)
- goto tempfail1;
- if (REPLYTYPE(r) == 4)
- goto tempfail2;
- if (REPLYTYPE(r) != 2)
- goto unavailable;
-
- /*
- ** Send the HELO command.
- ** 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 _FFR_IGNORE_EXT_ON_HELO
- mci->mci_flags &= ~MCIF_HELO;
-#endif /* _FFR_IGNORE_EXT_ON_HELO */
- if (bitnset(M_LMTP, m->m_flags))
- {
- smtpmessage("LHLO %s", m, mci, hn);
- SmtpPhase = mci->mci_phase = "client LHLO";
- }
- else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
- !bitnset(M_FSMTP, m->m_flags))
- {
- smtpmessage("EHLO %s", m, mci, hn);
- SmtpPhase = mci->mci_phase = "client EHLO";
- }
- else
- {
- smtpmessage("HELO %s", m, mci, hn);
- SmtpPhase = mci->mci_phase = "client HELO";
-#if _FFR_IGNORE_EXT_ON_HELO
- mci->mci_flags |= MCIF_HELO;
-#endif /* _FFR_IGNORE_EXT_ON_HELO */
- }
- sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
- CurHostName, mci->mci_phase);
- r = reply(m, mci, e,
- bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
- : TimeOuts.to_helo,
- helo_options, NULL, XS_DEFAULT);
- if (r < 0)
- goto tempfail1;
- else if (REPLYTYPE(r) == 5)
- {
- if (bitset(MCIF_ESMTP, mci->mci_flags) &&
- !bitnset(M_LMTP, m->m_flags))
- {
- /* try old SMTP instead */
- mci->mci_flags &= ~MCIF_ESMTP;
- goto tryhelo;
- }
- goto unavailable;
- }
- else if (REPLYTYPE(r) != 2)
- goto tempfail2;
-
- /*
- ** Check to see if we actually ended up talking to ourself.
- ** This means we didn't know about an alias or MX, or we managed
- ** to connect to an echo server.
- */
-
- p = strchr(&SmtpReplyBuffer[4], ' ');
- if (p != NULL)
- *p = '\0';
- if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
- !bitnset(M_LMTP, m->m_flags) &&
- sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
- {
- syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
- CurHostName);
- mci_setstat(mci, EX_CONFIG, "5.3.5",
- "553 5.3.5 system config error");
- mci->mci_errno = 0;
- smtpquit(m, mci, e);
- return;
- }
-
- /*
- ** If this is expected to be another sendmail, send some internal
- ** commands.
- ** If we're running as MSP, "propagate" -v flag if possible.
- */
-
- if ((UseMSP && Verbose && bitset(MCIF_VERB, mci->mci_flags))
-# if !_FFR_DEPRECATE_MAILER_FLAG_I
- || bitnset(M_INTERNAL, m->m_flags)
-# endif /* !_FFR_DEPRECATE_MAILER_FLAG_I */
- )
- {
- /* tell it to be verbose */
- smtpmessage("VERB", m, mci);
- r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc,
- XS_DEFAULT);
- if (r < 0)
- goto tempfail1;
- }
-
- if (mci->mci_state != MCIS_CLOSED)
- {
- mci->mci_state = MCIS_OPEN;
- return;
- }
-
- /* got a 421 error code during startup */
-
- tempfail1:
- mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
- if (mci->mci_state != MCIS_CLOSED)
- smtpquit(m, mci, e);
- return;
-
- tempfail2:
- /* XXX should use code from other end iff ENHANCEDSTATUSCODES */
- mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
- SmtpReplyBuffer);
- if (mci->mci_state != MCIS_CLOSED)
- smtpquit(m, mci, e);
- return;
-
- unavailable:
- mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
- smtpquit(m, mci, e);
- return;
-}
-/*
-** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
-**
-** Parameters:
-** 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.
-*/
-
-static void
-esmtp_check(line, firstline, m, mci, e)
- char *line;
- bool firstline;
- MAILER *m;
- register MCI *mci;
- ENVELOPE *e;
-{
- if (strstr(line, "ESMTP") != NULL)
- mci->mci_flags |= MCIF_ESMTP;
-
- /*
- ** Dirty hack below. Quoting the author:
- ** This was a response to people who wanted SMTP transmission to be
- ** just-send-8 by default. Essentially, you could put this tag into
- ** your greeting message to behave as though the F=8 flag was set on
- ** the mailer.
- */
-
- if (strstr(line, "8BIT-OK") != NULL)
- mci->mci_flags |= MCIF_8BITOK;
-}
-
-#if SASL
-/* specify prototype so compiler can check calls */
-static char *str_union __P((char *, char *, SM_RPOOL_T *));
-
-/*
-** STR_UNION -- create the union of two lists
-**
-** Parameters:
-** s1, s2 -- lists of items (separated by single blanks).
-** rpool -- resource pool from which result is allocated.
-**
-** Returns:
-** the union of both lists.
-*/
-
-static char *
-str_union(s1, s2, rpool)
- char *s1, *s2;
- SM_RPOOL_T *rpool;
-{
- 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 *) sm_rpool_malloc(rpool, rl + 2);
- if (res == NULL)
- {
- if (l1 > l2)
- return s1;
- return s2;
- }
- (void) sm_strlcpy(res, s1, rl);
- hr = res + l1;
- h1 = s2;
- h = s2;
-
- /* walk through s2 */
- while (h != NULL && *h1 != '\0')
- {
- /* is there something after the current word? */
- if ((h = strchr(h1, ' ')) != NULL)
- *h = '\0';
- l1 = strlen(h1);
-
- /* does the current word appear in s1 ? */
- if (iteminlist(h1, s1, " ") == NULL)
- {
- /* add space as delimiter */
- *hr++ = ' ';
-
- /* copy the item */
- memcpy(hr, h1, l1);
-
- /* advance pointer in result list */
- hr += l1;
- *hr = '\0';
- }
- if (h != NULL)
- {
- /* there are more items */
- *h = ' ';
- h1 = h + 1;
- }
- }
- return res;
-}
-#endif /* SASL */
-
-/*
-** HELO_OPTIONS -- process the options on a HELO line.
-**
-** Parameters:
-** 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 (unused).
-**
-** Returns:
-** none.
-*/
-
-static void
-helo_options(line, firstline, m, mci, e)
- char *line;
- bool firstline;
- MAILER *m;
- register MCI *mci;
- ENVELOPE *e;
-{
- register char *p;
-#if _FFR_IGNORE_EXT_ON_HELO
- static bool logged = false;
-#endif /* _FFR_IGNORE_EXT_ON_HELO */
-
- if (firstline)
- {
-#if SASL
- mci->mci_saslcap = NULL;
-#endif /* SASL */
-#if _FFR_IGNORE_EXT_ON_HELO
- logged = false;
-#endif /* _FFR_IGNORE_EXT_ON_HELO */
- return;
- }
-#if _FFR_IGNORE_EXT_ON_HELO
- else if (bitset(MCIF_HELO, mci->mci_flags))
- {
- if (LogLevel > 8 && !logged)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "server=%s [%s] returned extensions despite HELO command",
- macvalue(macid("{server_name}"), e),
- macvalue(macid("{server_addr}"), e));
- logged = true;
- }
- return;
- }
-#endif /* _FFR_IGNORE_EXT_ON_HELO */
-
- if (strlen(line) < 5)
- return;
- line += 4;
- p = strpbrk(line, " =");
- if (p != NULL)
- *p++ = '\0';
- if (sm_strcasecmp(line, "size") == 0)
- {
- mci->mci_flags |= MCIF_SIZE;
- if (p != NULL)
- mci->mci_maxsize = atol(p);
- }
- else if (sm_strcasecmp(line, "8bitmime") == 0)
- {
- mci->mci_flags |= MCIF_8BITMIME;
- mci->mci_flags &= ~MCIF_7BIT;
- }
- else if (sm_strcasecmp(line, "expn") == 0)
- mci->mci_flags |= MCIF_EXPN;
- else if (sm_strcasecmp(line, "dsn") == 0)
- mci->mci_flags |= MCIF_DSN;
- else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0)
- mci->mci_flags |= MCIF_ENHSTAT;
- else if (sm_strcasecmp(line, "pipelining") == 0)
- mci->mci_flags |= MCIF_PIPELINED;
- else if (sm_strcasecmp(line, "verb") == 0)
- mci->mci_flags |= MCIF_VERB;
-#if STARTTLS
- else if (sm_strcasecmp(line, "starttls") == 0)
- mci->mci_flags |= MCIF_TLS;
-#endif /* STARTTLS */
- else if (sm_strcasecmp(line, "deliverby") == 0)
- {
- mci->mci_flags |= MCIF_DLVR_BY;
- if (p != NULL)
- mci->mci_min_by = atol(p);
- }
-#if SASL
- else if (sm_strcasecmp(line, "auth") == 0)
- {
- if (p != NULL && *p != '\0')
- {
- if (mci->mci_saslcap != NULL)
- {
- /*
- ** Create the union with previous auth
- ** offerings because we recognize "auth "
- ** and "auth=" (old format).
- */
-
- mci->mci_saslcap = str_union(mci->mci_saslcap,
- p, mci->mci_rpool);
- mci->mci_flags |= MCIF_AUTH;
- }
- else
- {
- int l;
-
- l = strlen(p) + 1;
- mci->mci_saslcap = (char *)
- sm_rpool_malloc(mci->mci_rpool, l);
- if (mci->mci_saslcap != NULL)
- {
- (void) sm_strlcpy(mci->mci_saslcap, p,
- l);
- mci->mci_flags |= MCIF_AUTH;
- }
- }
- }
- }
-#endif /* SASL */
-}
-#if SASL
-
-static int getsimple __P((void *, int, const char **, unsigned *));
-static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **));
-static int saslgetrealm __P((void *, int, const char **, const char **));
-static int readauth __P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
-static int getauth __P((MCI *, ENVELOPE *, SASL_AI_T *));
-static char *removemech __P((char *, char *, SM_RPOOL_T *));
-static int attemptauth __P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
-
-static sasl_callback_t callbacks[] =
-{
- { SASL_CB_GETREALM, &saslgetrealm, NULL },
-#define CB_GETREALM_IDX 0
- { SASL_CB_PASS, &getsecret, NULL },
-#define CB_PASS_IDX 1
- { SASL_CB_USER, &getsimple, NULL },
-#define CB_USER_IDX 2
- { SASL_CB_AUTHNAME, &getsimple, NULL },
-#define CB_AUTHNAME_IDX 3
- { SASL_CB_VERIFYFILE, &safesaslfile, NULL },
-#define CB_SAFESASL_IDX 4
- { SASL_CB_LIST_END, NULL, NULL }
-};
-
-/*
-** INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
-**
-** Parameters:
-** none.
-**
-** Returns:
-** SASL_OK -- if successful.
-** SASL error code -- otherwise.
-**
-** Side Effects:
-** checks/sets sasl_clt_init.
-**
-** Note:
-** Callbacks are ignored if sasl_client_init() has
-** been called before (by a library such as libnss_ldap)
-*/
-
-static bool sasl_clt_init = false;
-
-static int
-init_sasl_client()
-{
- int result;
-
- if (sasl_clt_init)
- return SASL_OK;
- result = sasl_client_init(callbacks);
-
- /* should we retry later again or just remember that it failed? */
- if (result == SASL_OK)
- sasl_clt_init = true;
- return result;
-}
-/*
-** STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** checks/sets sasl_clt_init.
-*/
-
-void
-stop_sasl_client()
-{
- if (!sasl_clt_init)
- return;
- sasl_clt_init = false;
- sasl_done();
-}
-/*
-** GETSASLDATA -- process the challenges from the SASL protocol
-**
-** This gets the relevant sasl response data out of the reply
-** from the server.
-**
-** 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 (unused).
-**
-** Returns:
-** none.
-*/
-
-static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
-
-static void
-getsasldata(line, firstline, m, mci, e)
- char *line;
- bool firstline;
- MAILER *m;
- register MCI *mci;
- ENVELOPE *e;
-{
- int len;
- int result;
-# if SASL < 20000
- char *out;
-# endif /* SASL < 20000 */
-
- /* if not a continue we don't care about it */
- len = strlen(line);
- if ((len <= 4) ||
- (line[0] != '3') ||
- !isascii(line[1]) || !isdigit(line[1]) ||
- !isascii(line[2]) || !isdigit(line[2]))
- {
- SM_FREE_CLR(mci->mci_sasl_string);
- return;
- }
-
- /* forget about "334 " */
- line += 4;
- len -= 4;
-# if SASL >= 20000
- /* XXX put this into a macro/function? It's duplicated below */
- if (mci->mci_sasl_string != NULL)
- {
- if (mci->mci_sasl_string_len <= len)
- {
- sm_free(mci->mci_sasl_string); /* XXX */
- mci->mci_sasl_string = xalloc(len + 1);
- }
- }
- else
- mci->mci_sasl_string = xalloc(len + 1);
-
- result = sasl_decode64(line, len, mci->mci_sasl_string, len + 1,
- (unsigned int *) &mci->mci_sasl_string_len);
- if (result != SASL_OK)
- {
- mci->mci_sasl_string_len = 0;
- *mci->mci_sasl_string = '\0';
- }
-# else /* SASL >= 20000 */
- out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
- result = sasl_decode64(line, len, out, (unsigned int *) &len);
- if (result != SASL_OK)
- {
- len = 0;
- *out = '\0';
- }
-
- /*
- ** mci_sasl_string is "shared" with Cyrus-SASL library; hence
- ** it can't be in an rpool unless we use the same memory
- ** management mechanism (with same rpool!) for Cyrus SASL.
- */
-
- if (mci->mci_sasl_string != NULL)
- {
- if (mci->mci_sasl_string_len <= len)
- {
- sm_free(mci->mci_sasl_string); /* XXX */
- mci->mci_sasl_string = xalloc(len + 1);
- }
- }
- else
- mci->mci_sasl_string = xalloc(len + 1);
-
- memcpy(mci->mci_sasl_string, out, len);
- mci->mci_sasl_string[len] = '\0';
- mci->mci_sasl_string_len = len;
-# endif /* SASL >= 20000 */
- return;
-}
-/*
-** READAUTH -- read auth values from a file
-**
-** Parameters:
-** filename -- name of file to read.
-** safe -- if set, this is a safe read.
-** sai -- where to store auth_info.
-** rpool -- resource pool for sai.
-**
-** Returns:
-** EX_OK -- data succesfully read.
-** EX_UNAVAILABLE -- no valid filename.
-** EX_TEMPFAIL -- temporary failure.
-*/
-
-static char *sasl_info_name[] =
-{
- "user id",
- "authentication id",
- "password",
- "realm",
- "mechlist"
-};
-static int
-readauth(filename, safe, sai, rpool)
- char *filename;
- bool safe;
- SASL_AI_T *sai;
- SM_RPOOL_T *rpool;
-{
- SM_FILE_T *f;
- long sff;
- pid_t pid;
- int lc;
- char *s;
- char buf[MAXLINE];
-
- if (filename == NULL || filename[0] == '\0')
- return EX_UNAVAILABLE;
-
-#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 = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
- (void *) &fd, SM_IO_RDONLY, NULL);
- }
- else
-#endif /* !_FFR_ALLOW_SASLINFO */
- {
- pid = -1;
- sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
- |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
-# if _FFR_GROUPREADABLEAUTHINFOFILE
- if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
-# endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
- sff |= SFF_NOGRFILES;
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
-
-#if _FFR_ALLOW_SASLINFO
- /*
- ** XXX: make sure we don't read or open files that are not
- ** accesible to the user who specified a different authinfo
- ** file.
- */
-
- sff |= SFF_MUSTOWN;
-#else /* _FFR_ALLOW_SASLINFO */
- if (safe)
- sff |= SFF_OPENASROOT;
-#endif /* _FFR_ALLOW_SASLINFO */
-
- f = safefopen(filename, O_RDONLY, 0, sff);
- }
- if (f == NULL)
- {
- if (LogLevel > 5)
- sm_syslog(LOG_ERR, NOQID,
- "AUTH=client, error: can't open %s: %s",
- filename, sm_errstring(errno));
- return EX_TEMPFAIL;
- }
-
- lc = 0;
- while (lc <= SASL_MECHLIST &&
- sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
- {
- if (buf[0] != '#')
- {
- (*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
- if ((s = strchr((*sai)[lc], '\n')) != NULL)
- *s = '\0';
- lc++;
- }
- }
-
- (void) sm_io_close(f, SM_TIME_DEFAULT);
- if (pid > 0)
- (void) waitfor(pid);
- if (lc < SASL_PASSWORD)
- {
- if (LogLevel > 8)
- sm_syslog(LOG_ERR, NOQID,
- "AUTH=client, error: can't read %s from %s",
- sasl_info_name[lc + 1], filename);
- return EX_TEMPFAIL;
- }
- return EX_OK;
-}
-
-/*
-** GETAUTH -- get authinfo from ruleset call
-**
-** {server_name}, {server_addr} must be set
-**
-** Parameters:
-** mci -- the mailer connection structure.
-** e -- the envelope (including the sender to specify).
-** sai -- pointer to authinfo (result).
-**
-** Returns:
-** EX_OK -- ruleset was succesfully called, data may not
-** be available, sai must be checked.
-** EX_UNAVAILABLE -- ruleset unavailable (or failed).
-** EX_TEMPFAIL -- temporary failure (from ruleset).
-**
-** Side Effects:
-** Fills in sai if successful.
-*/
-
-static int
-getauth(mci, e, sai)
- MCI *mci;
- ENVELOPE *e;
- SASL_AI_T *sai;
-{
- int i, r, l, got, ret;
- char **pvp;
- char pvpbuf[PSBUFSIZE];
-
- r = rscap("authinfo", macvalue(macid("{server_name}"), e),
- macvalue(macid("{server_addr}"), e), e,
- &pvp, pvpbuf, sizeof(pvpbuf));
-
- if (r != EX_OK)
- return EX_UNAVAILABLE;
-
- /* other than expected return value: ok (i.e., no auth) */
- if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
- return EX_OK;
- if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
- return EX_TEMPFAIL;
-
- /*
- ** parse the data, put it into sai
- ** format: "TDstring" (including the '"' !)
- ** where T is a tag: 'U', ...
- ** D is a delimiter: ':' or '='
- */
-
- ret = EX_OK; /* default return value */
- i = 0;
- got = 0;
- while (i < SASL_ENTRIES)
- {
- if (pvp[i + 1] == NULL)
- break;
- if (pvp[i + 1][0] != '"')
- break;
- switch (pvp[i + 1][1])
- {
- case 'U':
- case 'u':
- r = SASL_USER;
- break;
- case 'I':
- case 'i':
- r = SASL_AUTHID;
- break;
- case 'P':
- case 'p':
- r = SASL_PASSWORD;
- break;
- case 'R':
- case 'r':
- r = SASL_DEFREALM;
- break;
- case 'M':
- case 'm':
- r = SASL_MECHLIST;
- break;
- default:
- goto fail;
- }
- l = strlen(pvp[i + 1]);
-
- /* check syntax */
- if (l <= 3 || pvp[i + 1][l - 1] != '"')
- goto fail;
-
- /* remove closing quote */
- pvp[i + 1][l - 1] = '\0';
-
- /* remove "TD and " */
- l -= 4;
- (*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
- if ((*sai)[r] == NULL)
- goto tempfail;
- if (pvp[i + 1][2] == ':')
- {
- /* ':text' (just copy) */
- (void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
- got |= 1 << r;
- }
- else if (pvp[i + 1][2] == '=')
- {
- unsigned int len;
-
- /* '=base64' (decode) */
-# if SASL >= 20000
- ret = sasl_decode64(pvp[i + 1] + 3,
- (unsigned int) l, (*sai)[r],
- (unsigned int) l + 1, &len);
-# else /* SASL >= 20000 */
- ret = sasl_decode64(pvp[i + 1] + 3,
- (unsigned int) l, (*sai)[r], &len);
-# endif /* SASL >= 20000 */
- if (ret != SASL_OK)
- goto fail;
- got |= 1 << r;
- }
- else
- goto fail;
- if (tTd(95, 5))
- sm_syslog(LOG_DEBUG, NOQID, "getauth %s=%s",
- sasl_info_name[r], (*sai)[r]);
- ++i;
- }
-
- /* did we get the expected data? */
- /* XXX: EXTERNAL mechanism only requires (and only uses) SASL_USER */
- if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
- bitset(SASL_PASSWORD_BIT, got)))
- goto fail;
-
- /* no authid? copy uid */
- if (!bitset(SASL_AUTHID_BIT, got))
- {
- l = strlen((*sai)[SASL_USER]) + 1;
- (*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
- l + 1);
- if ((*sai)[SASL_AUTHID] == NULL)
- goto tempfail;
- (void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
- }
-
- /* no uid? copy authid */
- if (!bitset(SASL_USER_BIT, got))
- {
- l = strlen((*sai)[SASL_AUTHID]) + 1;
- (*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
- l + 1);
- if ((*sai)[SASL_USER] == NULL)
- goto tempfail;
- (void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
- }
- return EX_OK;
-
- tempfail:
- ret = EX_TEMPFAIL;
- fail:
- if (LogLevel > 8)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
- macvalue(macid("{server_name}"), e),
- macvalue(macid("{server_addr}"), e),
- ret == EX_TEMPFAIL ? "temp" : "");
- for (i = 0; i <= SASL_MECHLIST; i++)
- (*sai)[i] = NULL; /* just clear; rpool */
- return ret;
-}
-
-# if SASL >= 20000
-/*
-** GETSIMPLE -- callback to get userid or authid
-**
-** Parameters:
-** context -- sai
-** 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;
- int id;
- const char **result;
- unsigned *len;
-{
- SASL_AI_T *sai;
-
- if (result == NULL || context == NULL)
- return SASL_BADPARAM;
- sai = (SASL_AI_T *) context;
-
- switch (id)
- {
- case SASL_CB_USER:
- *result = (*sai)[SASL_USER];
- if (tTd(95, 5))
- sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
- *result);
- if (len != NULL)
- *len = *result != NULL ? strlen(*result) : 0;
- break;
-
- case SASL_CB_AUTHNAME:
- *result = (*sai)[SASL_AUTHID];
- if (tTd(95, 5))
- sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
- *result);
- if (len != NULL)
- *len = *result != NULL ? strlen(*result) : 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 -- sai
-** id -- what to do
-** psecret -- (pointer to) result
-**
-** Returns:
-** OK/failure values
-*/
-
-static int
-getsecret(conn, context, id, psecret)
- sasl_conn_t *conn;
- SM_UNUSED(void *context);
- int id;
- sasl_secret_t **psecret;
-{
- int len;
- char *authpass;
- MCI *mci;
-
- if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
- return SASL_BADPARAM;
-
- mci = (MCI *) context;
- authpass = mci->mci_sai[SASL_PASSWORD];
- len = strlen(authpass);
-
- /*
- ** use an rpool because we are responsible for free()ing the secret,
- ** but we can't free() it until after the auth completes
- */
-
- *psecret = (sasl_secret_t *) sm_rpool_malloc(mci->mci_rpool,
- sizeof(sasl_secret_t) +
- len + 1);
- if (*psecret == NULL)
- return SASL_FAIL;
- (void) sm_strlcpy((char *) (*psecret)->data, authpass, len + 1);
- (*psecret)->len = (unsigned long) len;
- return SASL_OK;
-}
-# else /* SASL >= 20000 */
-/*
-** GETSIMPLE -- callback to get userid or authid
-**
-** Parameters:
-** context -- sai
-** 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;
- int id;
- const char **result;
- unsigned *len;
-{
- char *h, *s;
-# if SASL > 10509
- bool addrealm;
-# endif /* SASL > 10509 */
- size_t l;
- SASL_AI_T *sai;
- char *authid = NULL;
-
- if (result == NULL || context == NULL)
- return SASL_BADPARAM;
- sai = (SASL_AI_T *) context;
-
- /*
- ** Unfortunately it is not clear whether this routine should
- ** return a copy of a string or just a pointer to a string.
- ** The Cyrus-SASL plugins treat these return values differently, e.g.,
- ** plugins/cram.c free()s authid, plugings/digestmd5.c does not.
- ** The best solution to this problem is to fix Cyrus-SASL, but it
- ** seems there is nobody who creates patches... Hello CMU!?
- ** The second best solution is to have flags that tell this routine
- ** whether to return an malloc()ed copy.
- ** The next best solution is to always return an malloc()ed copy,
- ** and suffer from some memory leak, which is ugly for persistent
- ** queue runners.
- ** For now we go with the last solution...
- ** We can't use rpools (which would avoid this particular problem)
- ** as explained in sasl.c.
- */
-
- switch (id)
- {
- case SASL_CB_USER:
- l = strlen((*sai)[SASL_USER]) + 1;
- s = sm_sasl_malloc(l);
- if (s == NULL)
- {
- if (len != NULL)
- *len = 0;
- *result = NULL;
- return SASL_NOMEM;
- }
- (void) sm_strlcpy(s, (*sai)[SASL_USER], l);
- *result = s;
- if (tTd(95, 5))
- sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
- *result);
- if (len != NULL)
- *len = *result != NULL ? strlen(*result) : 0;
- break;
-
- case SASL_CB_AUTHNAME:
- h = (*sai)[SASL_AUTHID];
-# if SASL > 10509
- /* XXX maybe other mechanisms too?! */
- addrealm = (*sai)[SASL_MECH] != NULL &&
- sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0;
-
- /*
- ** Add realm to authentication id unless authid contains
- ** '@' (i.e., a realm) or the default realm is empty.
- */
-
- if (addrealm && h != NULL && strchr(h, '@') == NULL)
- {
- /* has this been done before? */
- if ((*sai)[SASL_ID_REALM] == NULL)
- {
- char *realm;
-
- realm = (*sai)[SASL_DEFREALM];
-
- /* do not add an empty realm */
- if (*realm == '\0')
- {
- authid = h;
- (*sai)[SASL_ID_REALM] = NULL;
- }
- else
- {
- l = strlen(h) + strlen(realm) + 2;
-
- /* should use rpool, but from where? */
- authid = sm_sasl_malloc(l);
- if (authid != NULL)
- {
- (void) sm_snprintf(authid, l,
- "%s@%s",
- h, realm);
- (*sai)[SASL_ID_REALM] = authid;
- }
- else
- {
- authid = h;
- (*sai)[SASL_ID_REALM] = NULL;
- }
- }
- }
- else
- authid = (*sai)[SASL_ID_REALM];
- }
- else
-# endif /* SASL > 10509 */
- authid = h;
- l = strlen(authid) + 1;
- s = sm_sasl_malloc(l);
- if (s == NULL)
- {
- if (len != NULL)
- *len = 0;
- *result = NULL;
- return SASL_NOMEM;
- }
- (void) sm_strlcpy(s, authid, l);
- *result = s;
- if (tTd(95, 5))
- sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
- *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 -- sai
-** id -- what to do
-** psecret -- (pointer to) result
-**
-** Returns:
-** OK/failure values
-*/
-
-static int
-getsecret(conn, context, id, psecret)
- sasl_conn_t *conn;
- SM_UNUSED(void *context);
- int id;
- sasl_secret_t **psecret;
-{
- int len;
- char *authpass;
- SASL_AI_T *sai;
-
- if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
- return SASL_BADPARAM;
-
- sai = (SASL_AI_T *) context;
- authpass = (*sai)[SASL_PASSWORD];
- len = strlen(authpass);
- *psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
- len + 1);
- if (*psecret == NULL)
- return SASL_FAIL;
- (void) sm_strlcpy((*psecret)->data, authpass, len + 1);
- (*psecret)->len = (unsigned long) len;
- return SASL_OK;
-}
-# endif /* SASL >= 20000 */
-
-/*
-** 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;
-# if SASL >= 20000
- const char *file;
-# else /* SASL >= 20000 */
- char *file;
-# endif /* SASL >= 20000 */
-#if SASL > 10515
-# if SASL >= 20000
- sasl_verify_type_t type;
-# else /* SASL >= 20000 */
- int type;
-# endif /* SASL >= 20000 */
-#endif /* SASL > 10515 */
-{
- long sff;
- int r;
-#if SASL <= 10515
- size_t len;
-#endif /* SASL <= 10515 */
- char *p;
-
- if (file == NULL || *file == '\0')
- return SASL_OK;
- sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
-#if SASL <= 10515
- if ((p = strrchr(file, '/')) == NULL)
- p = file;
- else
- ++p;
-
- /* everything beside libs and .conf files must not be readable */
- len = strlen(p);
- if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
- (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
- {
- if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
- sff |= SFF_NORFILES;
- if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
- sff |= SFF_NOGWFILES;
- }
-#else /* SASL <= 10515 */
- /* files containing passwords should be not readable */
- if (type == SASL_VRFY_PASSWD)
- {
- if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
- sff |= SFF_NOWRFILES;
- else
- sff |= SFF_NORFILES;
- if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
- sff |= SFF_NOGWFILES;
- }
-#endif /* SASL <= 10515 */
-
- p = (char *) file;
- if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
- S_IRUSR, NULL)) == 0)
- return SASL_OK;
- if (LogLevel > (r != ENOENT ? 8 : 10))
- sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
- p, sm_errstring(r));
- return SASL_CONTINUE;
-}
-
-/*
-** SASLGETREALM -- return the realm for SASL
-**
-** return the realm for the client
-**
-** Parameters:
-** context -- context shared between invocations
-** 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;
-{
- char *r;
- SASL_AI_T *sai;
-
- sai = (SASL_AI_T *) context;
- if (sai == NULL)
- return SASL_FAIL;
- r = (*sai)[SASL_DEFREALM];
-
- if (LogLevel > 12)
- sm_syslog(LOG_INFO, NOQID,
- "AUTH=client, realm=%s, available realms=%s",
- r == NULL ? "<No Realm>" : r,
- (availrealms == NULL || *availrealms == NULL)
- ? "<No Realms>" : *availrealms);
-
- /* check whether context is in list */
- if (availrealms != NULL && *availrealms != NULL)
- {
- if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
- NULL)
- {
- if (LogLevel > 8)
- sm_syslog(LOG_ERR, NOQID,
- "AUTH=client, realm=%s not in list=%s",
- r, *availrealms);
- return SASL_FAIL;
- }
- }
- *result = r;
- 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 (sm_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
-** rpool -- resource pool from which result is allocated.
-**
-** Returns:
-** pointer to new list (NULL in case of error).
-*/
-
-static char *
-removemech(rem, list, rpool)
- char *rem;
- char *list;
- SM_RPOOL_T *rpool;
-{
- 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 = (char *) sm_rpool_malloc_x(rpool, 1);
- *ret = '\0';
- return ret;
- }
- ret = (char *) sm_rpool_malloc_x(rpool, len);
- 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;
-}
-/*
-** ATTEMPTAUTH -- try to AUTHenticate using one mechanism
-**
-** Parameters:
-** m -- the mailer.
-** mci -- the mailer connection structure.
-** e -- the envelope (including the sender to specify).
-** sai - sasl authinfo
-**
-** Returns:
-** EX_OK -- authentication was successful.
-** EX_NOPERM -- authentication failed.
-** EX_IOERR -- authentication dialogue failed (I/O problem?).
-** EX_TEMPFAIL -- temporary failure.
-**
-*/
-
-static int
-attemptauth(m, mci, e, sai)
- MAILER *m;
- MCI *mci;
- ENVELOPE *e;
- SASL_AI_T *sai;
-{
- int saslresult, smtpresult;
-# if SASL >= 20000
- sasl_ssf_t ssf;
- const char *auth_id;
- const char *out;
-# else /* SASL >= 20000 */
- sasl_external_properties_t ssf;
- char *out;
-# endif /* SASL >= 20000 */
- unsigned int outlen;
- sasl_interact_t *client_interact = NULL;
- char *mechusing;
- sasl_security_properties_t ssp;
- char in64[MAXOUTLEN];
-#if NETINET || (NETINET6 && SASL >= 20000)
- extern SOCKADDR CurHostAddr;
-#endif /* NETINET || (NETINET6 && SASL >= 20000) */
-
- /* no mechanism selected (yet) */
- (*sai)[SASL_MECH] = NULL;
-
- /* dispose old connection */
- if (mci->mci_conn != NULL)
- sasl_dispose(&(mci->mci_conn));
-
- /* make a new client sasl connection */
-# if SASL >= 20000
- /*
- ** We provide the callbacks again because global callbacks in
- ** sasl_client_init() are ignored if SASL has been initialized
- ** before, for example, by a library such as libnss-ldap.
- */
-
- saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
- : "smtp",
- CurHostName, NULL, NULL, callbacks, 0,
- &mci->mci_conn);
-# else /* SASL >= 20000 */
- saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
- : "smtp",
- CurHostName, NULL, 0, &mci->mci_conn);
-# endif /* SASL >= 20000 */
- if (saslresult != SASL_OK)
- return EX_TEMPFAIL;
-
- /* set properties */
- (void) memset(&ssp, '\0', sizeof(ssp));
-
- /* XXX should these be options settable via .cf ? */
- {
- ssp.max_ssf = MaxSLBits;
- ssp.maxbufsize = MAXOUTLEN;
-# if 0
- ssp.security_flags = SASL_SEC_NOPLAINTEXT;
-# endif /* 0 */
- }
- saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
- if (saslresult != SASL_OK)
- return EX_TEMPFAIL;
-
-# if SASL >= 20000
- /* external security strength factor, authentication id */
- ssf = 0;
- auth_id = NULL;
-# if STARTTLS
- out = macvalue(macid("{cert_subject}"), e);
- if (out != NULL && *out != '\0')
- auth_id = out;
- out = macvalue(macid("{cipher_bits}"), e);
- if (out != NULL && *out != '\0')
- ssf = atoi(out);
-# endif /* STARTTLS */
- saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
- if (saslresult != SASL_OK)
- return EX_TEMPFAIL;
- saslresult = sasl_setprop(mci->mci_conn, SASL_AUTH_EXTERNAL, auth_id);
- if (saslresult != SASL_OK)
- return EX_TEMPFAIL;
-
-# if NETINET || NETINET6
- /* set local/remote ipv4 addresses */
- if (mci->mci_out != NULL && (
-# if NETINET6
- CurHostAddr.sa.sa_family == AF_INET6 ||
-# endif /* NETINET6 */
- CurHostAddr.sa.sa_family == AF_INET))
- {
- SOCKADDR_LEN_T addrsize;
- SOCKADDR saddr_l;
- char localip[60], remoteip[60];
-
- switch (CurHostAddr.sa.sa_family)
- {
- case AF_INET:
- addrsize = sizeof(struct sockaddr_in);
- break;
-# if NETINET6
- case AF_INET6:
- addrsize = sizeof(struct sockaddr_in6);
- break;
-# endif /* NETINET6 */
- default:
- break;
- }
- if (iptostring(&CurHostAddr, addrsize,
- remoteip, sizeof(remoteip)))
- {
- if (sasl_setprop(mci->mci_conn, SASL_IPREMOTEPORT,
- remoteip) != SASL_OK)
- return EX_TEMPFAIL;
- }
- addrsize = sizeof(saddr_l);
- if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *) &saddr_l, &addrsize) == 0)
- {
- if (iptostring(&saddr_l, addrsize,
- localip, sizeof(localip)))
- {
- if (sasl_setprop(mci->mci_conn,
- SASL_IPLOCALPORT,
- localip) != SASL_OK)
- return EX_TEMPFAIL;
- }
- }
- }
-# endif /* NETINET || NETINET6 */
-
- /* start client side of sasl */
- saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
- &client_interact,
- &out, &outlen,
- (const char **) &mechusing);
-# else /* SASL >= 20000 */
- /* external security strength factor, authentication id */
- ssf.ssf = 0;
- ssf.auth_id = NULL;
-# if STARTTLS
- out = macvalue(macid("{cert_subject}"), e);
- if (out != NULL && *out != '\0')
- ssf.auth_id = out;
- out = macvalue(macid("{cipher_bits}"), e);
- if (out != NULL && *out != '\0')
- ssf.ssf = atoi(out);
-# endif /* STARTTLS */
- 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(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *) &saddr_l, &addrsize) == 0)
- {
- if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
- &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);
-# endif /* SASL >= 20000 */
-
- if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
- {
- if (saslresult == SASL_NOMECH && LogLevel > 8)
- {
- sm_syslog(LOG_NOTICE, e->e_id,
- "AUTH=client, available mechanisms do not fulfill requirements");
- }
- return EX_TEMPFAIL;
- }
-
- /* just point current mechanism to the data in the sasl library */
- (*sai)[SASL_MECH] = mechusing;
-
- /* send the info across the wire */
- if (out == NULL
- /* login and digest-md5 up to 1.5.28 set out="" */
- || (outlen == 0 &&
- (sm_strcasecmp(mechusing, "LOGIN") == 0 ||
- sm_strcasecmp(mechusing, "DIGEST-MD5") == 0))
- )
- {
- /* no initial response */
- smtpmessage("AUTH %s", m, mci, mechusing);
- }
- else if (outlen == 0)
- {
- /*
- ** zero-length initial response, per RFC 2554 4.:
- ** "Unlike a zero-length client answer to a 334 reply, a zero-
- ** length initial response is sent as a single equals sign"
- */
-
- smtpmessage("AUTH %s =", m, mci, mechusing);
- }
- else
- {
- 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);
- }
-# if SASL < 20000
- sm_sasl_free(out); /* XXX only if no rpool is used */
-# endif /* SASL < 20000 */
-
- /* get the reply */
- smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL,
- XS_AUTH);
-
- for (;;)
- {
- /* check return code from server */
- if (smtpresult == 235)
- {
- macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
- mechusing);
- return EX_OK;
- }
- if (smtpresult == -1)
- return EX_IOERR;
- if (REPLYTYPE(smtpresult) == 5)
- return EX_NOPERM; /* ugly, but ... */
- if (REPLYTYPE(smtpresult) != 3)
- {
- /* should we fail deliberately, see RFC 2554 4. ? */
- /* smtpmessage("*", m, mci); */
- return EX_TEMPFAIL;
- }
-
- saslresult = sasl_client_step(mci->mci_conn,
- mci->mci_sasl_string,
- mci->mci_sasl_string_len,
- &client_interact,
- &out, &outlen);
-
- if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
- {
- if (tTd(95, 5))
- sm_dprintf("AUTH FAIL=%s (%d)\n",
- sasl_errstring(saslresult, NULL, NULL),
- saslresult);
-
- /* fail deliberately, see RFC 2554 4. */
- smtpmessage("*", m, mci);
-
- /*
- ** but we should only fail for this authentication
- ** mechanism; how to do that?
- */
-
- smtpresult = reply(m, mci, e, TimeOuts.to_auth,
- getsasldata, NULL, XS_AUTH);
- return EX_NOPERM;
- }
-
- 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';
-# if SASL < 20000
- sm_sasl_free(out); /* XXX only if no rpool is used */
-# endif /* SASL < 20000 */
- smtpmessage("%s", m, mci, in64);
- smtpresult = reply(m, mci, e, TimeOuts.to_auth,
- getsasldata, NULL, XS_AUTH);
- }
- /* 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 -- authentication was successful
-** EX_UNAVAILABLE -- authentication not possible, e.g.,
-** no data available.
-** EX_NOPERM -- authentication failed.
-** EX_TEMPFAIL -- temporary failure.
-**
-** Notice: AuthInfo is used for all connections, hence we must
-** return EX_TEMPFAIL only if we really want to retry, i.e.,
-** iff getauth() tempfailed or getauth() was used and
-** authentication tempfailed.
-*/
-
-int
-smtpauth(m, mci, e)
- MAILER *m;
- MCI *mci;
- ENVELOPE *e;
-{
- int result;
- int i;
- bool usedgetauth;
-
- mci->mci_sasl_auth = false;
- for (i = 0; i < SASL_MECH ; i++)
- mci->mci_sai[i] = NULL;
-
- result = getauth(mci, e, &(mci->mci_sai));
- if (result == EX_TEMPFAIL)
- return result;
- usedgetauth = true;
-
- /* no data available: don't try to authenticate */
- if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
- return result;
- if (result != EX_OK)
- {
- if (SASLInfo == NULL)
- return EX_UNAVAILABLE;
-
- /* read authinfo from file */
- result = readauth(SASLInfo, true, &(mci->mci_sai),
- mci->mci_rpool);
- if (result != EX_OK)
- return result;
- usedgetauth = false;
- }
-
- /* check whether sufficient data is available */
- if (mci->mci_sai[SASL_PASSWORD] == NULL ||
- *(mci->mci_sai)[SASL_PASSWORD] == '\0')
- return EX_UNAVAILABLE;
- if ((mci->mci_sai[SASL_AUTHID] == NULL ||
- *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
- (mci->mci_sai[SASL_USER] == NULL ||
- *(mci->mci_sai)[SASL_USER] == '\0'))
- return EX_UNAVAILABLE;
-
- /* set the context for the callback function to sai */
-# if SASL >= 20000
- callbacks[CB_PASS_IDX].context = (void *) mci;
-# else /* SASL >= 20000 */
- callbacks[CB_PASS_IDX].context = (void *) &mci->mci_sai;
-# endif /* SASL >= 20000 */
- callbacks[CB_USER_IDX].context = (void *) &mci->mci_sai;
- callbacks[CB_AUTHNAME_IDX].context = (void *) &mci->mci_sai;
- callbacks[CB_GETREALM_IDX].context = (void *) &mci->mci_sai;
-#if 0
- callbacks[CB_SAFESASL_IDX].context = (void *) &mci->mci_sai;
-#endif /* 0 */
-
- /* set default value for realm */
- if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
- (mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
- macvalue('j', CurEnv));
-
- /* set default value for list of mechanism to use */
- if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
- *(mci->mci_sai)[SASL_MECHLIST] == '\0')
- (mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
-
- /* create list of mechanisms to try */
- mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
- mci->mci_saslcap, mci->mci_rpool);
-
- /* initialize sasl client library */
- result = init_sasl_client();
- if (result != SASL_OK)
- return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
- do
- {
- result = attemptauth(m, mci, e, &(mci->mci_sai));
- if (result == EX_OK)
- mci->mci_sasl_auth = true;
- else if (result == EX_TEMPFAIL || result == EX_NOPERM)
- {
- mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
- mci->mci_saslcap,
- mci->mci_rpool);
- if (mci->mci_saslcap == NULL ||
- *(mci->mci_saslcap) == '\0')
- return usedgetauth ? result
- : EX_UNAVAILABLE;
- }
- else
- return result;
- } while (result != EX_OK);
- return result;
-}
-#endif /* SASL */
-
-/*
-** SMTPMAILFROM -- send MAIL command
-**
-** Parameters:
-** m -- the mailer.
-** mci -- the mailer connection structure.
-** e -- the envelope (including the sender to specify).
-*/
-
-int
-smtpmailfrom(m, mci, e)
- MAILER *m;
- MCI *mci;
- ENVELOPE *e;
-{
- int r;
- char *bufp;
- char *bodytype;
- char *enhsc;
- char buf[MAXNAME + 1];
- char optbuf[MAXLINE];
-
- if (tTd(18, 2))
- sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
- enhsc = NULL;
-
- /*
- ** Check if connection is gone, if so
- ** it's a tempfail and we use mci_errno
- ** for the reason.
- */
-
- if (mci->mci_state == MCIS_CLOSED)
- {
- errno = mci->mci_errno;
- return EX_TEMPFAIL;
- }
-
- /* set up appropriate options to include */
- if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
- {
- (void) sm_snprintf(optbuf, sizeof(optbuf), " SIZE=%ld",
- e->e_msgsize);
- bufp = &optbuf[strlen(optbuf)];
- }
- else
- {
- optbuf[0] = '\0';
- bufp = optbuf;
- }
-
- bodytype = e->e_bodytype;
- if (bitset(MCIF_8BITMIME, mci->mci_flags))
- {
- if (bodytype == NULL &&
- bitset(MM_MIME8BIT, MimeMode) &&
- bitset(EF_HAS8BIT, e->e_flags) &&
- !bitset(EF_DONT_MIME, e->e_flags) &&
- !bitnset(M_8BITS, m->m_flags))
- bodytype = "8BITMIME";
- if (bodytype != NULL &&
- SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
- {
- (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
- " BODY=%s", bodytype);
- bufp += strlen(bufp);
- }
- }
- else if (bitnset(M_8BITS, m->m_flags) ||
- !bitset(EF_HAS8BIT, e->e_flags) ||
- bitset(MCIF_8BITOK, mci->mci_flags))
- {
- /* EMPTY */
- /* just pass it through */
- }
-#if MIME8TO7
- else if (bitset(MM_CVTMIME, MimeMode) &&
- !bitset(EF_DONT_MIME, e->e_flags) &&
- (!bitset(MM_PASS8BIT, MimeMode) ||
- bitset(EF_IS_MIME, e->e_flags)))
- {
- /* must convert from 8bit MIME format to 7bit encoded */
- mci->mci_flags |= MCIF_CVT8TO7;
- }
-#endif /* MIME8TO7 */
- else if (!bitset(MM_PASS8BIT, MimeMode))
- {
- /* cannot just send a 8-bit version */
- extern char MsgBuf[];
-
- usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
- mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
- return EX_DATAERR;
- }
-
- if (bitset(MCIF_DSN, mci->mci_flags))
- {
- if (e->e_envid != NULL &&
- SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
- {
- (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
- " ENVID=%s", e->e_envid);
- bufp += strlen(bufp);
- }
-
- /* RET= parameter */
- if (bitset(EF_RET_PARAM, e->e_flags) &&
- SPACELEFT(optbuf, bufp) > 9)
- {
- (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
- " RET=%s",
- bitset(EF_NO_BODY_RETN, e->e_flags) ?
- "HDRS" : "FULL");
- bufp += strlen(bufp);
- }
- }
-
- 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 */
- )
- {
- (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
- " AUTH=%s", e->e_auth_param);
- bufp += strlen(bufp);
- }
-
- /*
- ** 17 is the max length required, we could use log() to compute
- ** the exact length (and check IS_DLVR_TRACE())
- */
-
- if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
- IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
- {
- long dby;
-
- /*
- ** Avoid problems with delays (for R) since the check
- ** in deliver() whether min-deliver-time is sufficient.
- ** Alternatively we could pass the computed time to this
- ** function.
- */
-
- dby = e->e_deliver_by - (curtime() - e->e_ctime);
- if (dby <= 0 && IS_DLVR_RETURN(e))
- dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
- (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
- " BY=%ld;%c%s",
- dby,
- IS_DLVR_RETURN(e) ? 'R' : 'N',
- IS_DLVR_TRACE(e) ? "T" : "");
- bufp += strlen(bufp);
- }
-
- /*
- ** Send the MAIL command.
- ** Designates the sender.
- */
-
- mci->mci_state = MCIS_MAIL;
-
- if (bitset(EF_RESPONSE, e->e_flags) &&
- !bitnset(M_NO_NULL_FROM, m->m_flags))
- buf[0] = '\0';
- else
- expand("\201g", buf, sizeof(buf), e);
- if (buf[0] == '<')
- {
- /* strip off <angle brackets> (put back on below) */
- bufp = &buf[strlen(buf) - 1];
- if (*bufp == '>')
- *bufp = '\0';
- bufp = &buf[1];
- }
- else
- bufp = buf;
- if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
- !bitnset(M_FROMPATH, m->m_flags))
- {
- smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
- }
- else
- {
- smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
- *bufp == '@' ? ',' : ':', bufp, optbuf);
- }
- SmtpPhase = mci->mci_phase = "client MAIL";
- sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
- CurHostName, mci->mci_phase);
- r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc, XS_DEFAULT);
- if (r < 0)
- {
- /* communications failure */
- mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
- return EX_TEMPFAIL;
- }
- else if (r == SMTPCLOSING)
- {
- /* service shutting down: handled by reply() */
- return EX_TEMPFAIL;
- }
- else if (REPLYTYPE(r) == 4)
- {
- mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
- SmtpReplyBuffer);
- return EX_TEMPFAIL;
- }
- else if (REPLYTYPE(r) == 2)
- {
- return EX_OK;
- }
- else if (r == 501)
- {
- /* syntax error in arguments */
- 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, ENHSCN(enhsc, "5.1.3"),
- SmtpReplyBuffer);
- return EX_DATAERR;
- }
- else if (r == 552)
- {
- /* exceeded storage allocation */
- 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;
- }
- else if (REPLYTYPE(r) == 5)
- {
- /* unknown error */
- 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));
- }
-
- /* protocol error -- close up */
- mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
- SmtpReplyBuffer);
- smtpquit(m, mci, e);
- return EX_PROTOCOL;
-}
-/*
-** SMTPRCPT -- designate recipient.
-**
-** Parameters:
-** to -- address of recipient.
-** m -- the mailer we are sending to.
-** mci -- the connection info for this transaction.
-** e -- the envelope for this transaction.
-**
-** Returns:
-** exit status corresponding to recipient status.
-**
-** Side Effects:
-** Sends the mail via SMTP.
-*/
-
-int
-smtprcpt(to, m, mci, e, ctladdr, xstart)
- ADDRESS *to;
- register MAILER *m;
- MCI *mci;
- ENVELOPE *e;
- ADDRESS *ctladdr;
- time_t xstart;
-{
- char *bufp;
- char optbuf[MAXLINE];
-
-#if PIPELINING
- /*
- ** If there is status waiting from the other end, read it.
- ** This should normally happen because of SMTP pipelining.
- */
-
- while (mci->mci_nextaddr != NULL &&
- sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
- {
- int r;
-
- r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
- if (r != EX_OK)
- {
- markfailure(e, mci->mci_nextaddr, mci, r, false);
- giveresponse(r, mci->mci_nextaddr->q_status, m, mci,
- ctladdr, xstart, e, to);
- }
- mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
- }
-#endif /* PIPELINING */
-
- /*
- ** Check if connection is gone, if so
- ** it's a tempfail and we use mci_errno
- ** for the reason.
- */
-
- if (mci->mci_state == MCIS_CLOSED)
- {
- errno = mci->mci_errno;
- return EX_TEMPFAIL;
- }
-
- optbuf[0] = '\0';
- bufp = optbuf;
-
- /*
- ** Warning: in the following it is assumed that the free space
- ** in bufp is sizeof(optbuf)
- */
-
- if (bitset(MCIF_DSN, mci->mci_flags))
- {
- if (IS_DLVR_NOTIFY(e) &&
- !bitset(MCIF_DLVR_BY, mci->mci_flags))
- {
- /* RFC 2852: 4.1.4.2 */
- if (!bitset(QHASNOTIFY, to->q_flags))
- to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
- else if (bitset(QPINGONSUCCESS, to->q_flags) ||
- bitset(QPINGONFAILURE, to->q_flags) ||
- bitset(QPINGONDELAY, to->q_flags))
- to->q_flags |= QPINGONDELAY;
- }
-
- /* NOTIFY= parameter */
- if (bitset(QHASNOTIFY, to->q_flags) &&
- bitset(QPRIMARY, to->q_flags) &&
- !bitnset(M_LOCALMAILER, m->m_flags))
- {
- bool firstone = true;
-
- (void) sm_strlcat(bufp, " NOTIFY=", sizeof(optbuf));
- if (bitset(QPINGONSUCCESS, to->q_flags))
- {
- (void) sm_strlcat(bufp, "SUCCESS", sizeof(optbuf));
- firstone = false;
- }
- if (bitset(QPINGONFAILURE, to->q_flags))
- {
- if (!firstone)
- (void) sm_strlcat(bufp, ",",
- sizeof(optbuf));
- (void) sm_strlcat(bufp, "FAILURE", sizeof(optbuf));
- firstone = false;
- }
- if (bitset(QPINGONDELAY, to->q_flags))
- {
- if (!firstone)
- (void) sm_strlcat(bufp, ",",
- sizeof(optbuf));
- (void) sm_strlcat(bufp, "DELAY", sizeof(optbuf));
- firstone = false;
- }
- if (firstone)
- (void) sm_strlcat(bufp, "NEVER", sizeof(optbuf));
- bufp += strlen(bufp);
- }
-
- /* ORCPT= parameter */
- if (to->q_orcpt != NULL &&
- SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
- {
- (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
- " ORCPT=%s", to->q_orcpt);
- bufp += strlen(bufp);
- }
- }
-
- smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
- mci->mci_state = MCIS_RCPT;
-
- SmtpPhase = mci->mci_phase = "client RCPT";
- sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
- CurHostName, mci->mci_phase);
-
-#if PIPELINING
- /*
- ** If running SMTP pipelining, we will pick up status later
- */
-
- if (bitset(MCIF_PIPELINED, mci->mci_flags))
- return EX_OK;
-#endif /* PIPELINING */
-
- return smtprcptstat(to, m, mci, e);
-}
-/*
-** SMTPRCPTSTAT -- get recipient status
-**
-** This is only called during SMTP pipelining
-**
-** Parameters:
-** to -- address of recipient.
-** m -- mailer being sent to.
-** mci -- the mailer connection information.
-** e -- the envelope for this message.
-**
-** Returns:
-** EX_* -- protocol status
-*/
-
-static int
-smtprcptstat(to, m, mci, e)
- ADDRESS *to;
- MAILER *m;
- register MCI *mci;
- register ENVELOPE *e;
-{
- int r;
- int save_errno;
- char *enhsc;
-
- /*
- ** Check if connection is gone, if so
- ** it's a tempfail and we use mci_errno
- ** for the reason.
- */
-
- if (mci->mci_state == MCIS_CLOSED)
- {
- errno = mci->mci_errno;
- return EX_TEMPFAIL;
- }
-
- enhsc = NULL;
- r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc, XS_DEFAULT);
- save_errno = errno;
- to->q_rstatus = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
- to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
- if (!bitnset(M_LMTP, m->m_flags))
- to->q_statmta = mci->mci_host;
- if (r < 0 || REPLYTYPE(r) == 4)
- {
- mci->mci_retryrcpt = true;
- errno = save_errno;
- return EX_TEMPFAIL;
- }
- else if (REPLYTYPE(r) == 2)
- {
- char *t;
-
- if ((t = mci->mci_tolist) != NULL)
- {
- char *p;
-
- *t++ = ',';
- for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
- continue;
- *t = '\0';
- mci->mci_tolist = t;
- }
-#if PIPELINING
- mci->mci_okrcpts++;
-#endif /* PIPELINING */
- return EX_OK;
- }
- else if (r == 550)
- {
- to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
- return EX_NOUSER;
- }
- else if (r == 551)
- {
- to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
- return EX_NOUSER;
- }
- else if (r == 553)
- {
- to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
- return EX_NOUSER;
- }
- else if (REPLYTYPE(r) == 5)
- {
- return EX_UNAVAILABLE;
- }
-
- if (LogLevel > 1)
- {
- sm_syslog(LOG_CRIT, e->e_id,
- "%.100s: SMTP RCPT protocol error: %s",
- CurHostName,
- shortenstring(SmtpReplyBuffer, 403));
- }
-
- mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
- SmtpReplyBuffer);
- return EX_PROTOCOL;
-}
-/*
-** SMTPDATA -- send the data and clean up the transaction.
-**
-** Parameters:
-** m -- mailer being sent to.
-** mci -- the mailer connection information.
-** e -- the envelope for this message.
-**
-** Returns:
-** exit status corresponding to DATA command.
-*/
-
-int
-smtpdata(m, mci, e, ctladdr, xstart)
- MAILER *m;
- register MCI *mci;
- register ENVELOPE *e;
- ADDRESS *ctladdr;
- time_t xstart;
-{
- register int r;
- int rstat;
- int xstat;
- int timeout;
- char *enhsc;
-
- /*
- ** Check if connection is gone, if so
- ** it's a tempfail and we use mci_errno
- ** for the reason.
- */
-
- if (mci->mci_state == MCIS_CLOSED)
- {
- errno = mci->mci_errno;
- return EX_TEMPFAIL;
- }
-
- enhsc = NULL;
-
- /*
- ** Send the data.
- ** First send the command and check that it is ok.
- ** Then send the data (if there are valid recipients).
- ** Follow it up with a dot to terminate.
- ** Finally get the results of the transaction.
- */
-
- /* send the command and check ok to proceed */
- smtpmessage("DATA", m, mci);
-
-#if PIPELINING
- if (mci->mci_nextaddr != NULL)
- {
- char *oldto = e->e_to;
-
- /* pick up any pending RCPT responses for SMTP pipelining */
- while (mci->mci_nextaddr != NULL)
- {
- int r;
-
- e->e_to = mci->mci_nextaddr->q_paddr;
- r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
- if (r != EX_OK)
- {
- markfailure(e, mci->mci_nextaddr, mci, r,
- false);
- giveresponse(r, mci->mci_nextaddr->q_status, m,
- mci, ctladdr, xstart, e,
- mci->mci_nextaddr);
- if (r == EX_TEMPFAIL)
- mci->mci_nextaddr->q_state = QS_RETRY;
- }
- mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
- }
- e->e_to = oldto;
-
- /*
- ** Connection might be closed in response to a RCPT command,
- ** i.e., the server responded with 421. In that case (at
- ** least) one RCPT has a temporary failure, hence we don't
- ** need to check mci_okrcpts (as it is done below) to figure
- ** out which error to return.
- */
-
- if (mci->mci_state == MCIS_CLOSED)
- {
- errno = mci->mci_errno;
- return EX_TEMPFAIL;
- }
- }
-#endif /* PIPELINING */
-
- /* now proceed with DATA phase */
- SmtpPhase = mci->mci_phase = "client DATA 354";
- mci->mci_state = MCIS_DATA;
- sm_setproctitle(true, e, "%s %s: %s",
- qid_printname(e), CurHostName, mci->mci_phase);
- r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc, XS_DEFAULT);
- if (r < 0 || REPLYTYPE(r) == 4)
- {
- if (r >= 0)
- smtpquit(m, mci, e);
- errno = mci->mci_errno;
- return EX_TEMPFAIL;
- }
- else if (REPLYTYPE(r) == 5)
- {
- smtprset(m, mci, e);
-#if PIPELINING
- if (mci->mci_okrcpts <= 0)
- return mci->mci_retryrcpt ? EX_TEMPFAIL
- : EX_UNAVAILABLE;
-#endif /* PIPELINING */
- return EX_UNAVAILABLE;
- }
- else if (REPLYTYPE(r) != 3)
- {
- if (LogLevel > 1)
- {
- sm_syslog(LOG_CRIT, e->e_id,
- "%.100s: SMTP DATA-1 protocol error: %s",
- CurHostName,
- shortenstring(SmtpReplyBuffer, 403));
- }
- smtprset(m, mci, e);
- mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
- SmtpReplyBuffer);
-#if PIPELINING
- if (mci->mci_okrcpts <= 0)
- return mci->mci_retryrcpt ? EX_TEMPFAIL
- : EX_PROTOCOL;
-#endif /* PIPELINING */
- return EX_PROTOCOL;
- }
-
-#if PIPELINING
- if (mci->mci_okrcpts > 0)
- {
-#endif /* PIPELINING */
-
- /*
- ** Set timeout around data writes. Make it at least large
- ** enough for DNS timeouts on all recipients plus some fudge
- ** factor. The main thing is that it should not be infinite.
- */
-
- if (tTd(18, 101))
- {
- /* simulate a DATA timeout */
- timeout = 10;
- }
- else
- timeout = DATA_PROGRESS_TIMEOUT * 1000;
- sm_io_setinfo(mci->mci_out, SM_IO_WHAT_TIMEOUT, &timeout);
-
-
- /*
- ** Output the actual message.
- */
-
- if (!(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER))
- goto writeerr;
-
- if (tTd(18, 101))
- {
- /* simulate a DATA timeout */
- (void) sleep(2);
- }
-
- if (!(*e->e_putbody)(mci, e, NULL))
- goto writeerr;
-
- /*
- ** Cleanup after sending message.
- */
-
-
-#if PIPELINING
- }
-#endif /* PIPELINING */
-
-#if _FFR_CATCH_BROKEN_MTAS
- if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
- {
- /* terminate the message */
- (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
- m->m_eol);
- if (TrafficLogFile != NULL)
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "%05d >>> .\n", (int) CurrentPid);
- if (Verbose)
- nmessage(">>> .");
-
- sm_syslog(LOG_CRIT, e->e_id,
- "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
- CurHostName);
- mci->mci_errno = EIO;
- mci->mci_state = MCIS_ERROR;
- mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
- smtpquit(m, mci, e);
- return EX_PROTOCOL;
- }
-#endif /* _FFR_CATCH_BROKEN_MTAS */
-
- if (sm_io_error(mci->mci_out))
- {
- /* error during processing -- don't send the dot */
- mci->mci_errno = EIO;
- mci->mci_state = MCIS_ERROR;
- mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
- smtpquit(m, mci, e);
- return EX_IOERR;
- }
-
- /* terminate the message */
- if (sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s.%s",
- bitset(MCIF_INLONGLINE, mci->mci_flags) ? m->m_eol : "",
- m->m_eol) == SM_IO_EOF)
- goto writeerr;
- if (TrafficLogFile != NULL)
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "%05d >>> .\n", (int) CurrentPid);
- if (Verbose)
- nmessage(">>> .");
-
- /* check for the results of the transaction */
- SmtpPhase = mci->mci_phase = "client DATA status";
- sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
- CurHostName, mci->mci_phase);
- if (bitnset(M_LMTP, m->m_flags))
- return EX_OK;
- r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DEFAULT);
- if (r < 0)
- return EX_TEMPFAIL;
- if (mci->mci_state == MCIS_DATA)
- mci->mci_state = MCIS_OPEN;
- xstat = EX_NOTSTICKY;
- if (r == 452)
- rstat = EX_TEMPFAIL;
- else if (REPLYTYPE(r) == 4)
- rstat = xstat = EX_TEMPFAIL;
- else if (REPLYTYPE(r) == 2)
- rstat = xstat = EX_OK;
- else if (REPLYCLASS(r) != 5)
- rstat = xstat = EX_PROTOCOL;
- else if (REPLYTYPE(r) == 5)
- rstat = EX_UNAVAILABLE;
- else
- rstat = EX_PROTOCOL;
- mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
- SmtpReplyBuffer);
- if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
- (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
- r += 5;
- else
- r = 4;
- e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
- SmtpPhase = mci->mci_phase = "idle";
- sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
- 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));
- }
- return rstat;
-
- writeerr:
- mci->mci_errno = errno;
- mci->mci_state = MCIS_ERROR;
- mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
-
- /*
- ** 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;
-}
-
-/*
-** SMTPGETSTAT -- get status code from DATA in LMTP
-**
-** Parameters:
-** m -- the mailer to which we are sending the message.
-** mci -- the mailer connection structure.
-** e -- the current envelope.
-**
-** Returns:
-** The exit status corresponding to the reply code.
-*/
-
-int
-smtpgetstat(m, mci, e)
- MAILER *m;
- MCI *mci;
- ENVELOPE *e;
-{
- int r;
- int off;
- int status, xstat;
- char *enhsc;
-
- enhsc = NULL;
-
- /* check for the results of the transaction */
- r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DEFAULT);
- if (r < 0)
- return EX_TEMPFAIL;
- xstat = EX_NOTSTICKY;
- if (REPLYTYPE(r) == 4)
- status = EX_TEMPFAIL;
- else if (REPLYTYPE(r) == 2)
- status = xstat = EX_OK;
- else if (REPLYCLASS(r) != 5)
- status = xstat = EX_PROTOCOL;
- else if (REPLYTYPE(r) == 5)
- status = EX_UNAVAILABLE;
- else
- status = EX_PROTOCOL;
- if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
- (off = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
- off += 5;
- else
- off = 4;
- e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[off]);
- mci_setstat(mci, xstat, 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));
- }
- return status;
-}
-/*
-** SMTPQUIT -- close the SMTP connection.
-**
-** Parameters:
-** m -- a pointer to the mailer.
-** mci -- the mailer connection information.
-** e -- the current envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** sends the final protocol and closes the connection.
-*/
-
-void
-smtpquit(m, mci, e)
- register MAILER *m;
- register MCI *mci;
- ENVELOPE *e;
-{
- bool oldSuprErrs = SuprErrs;
- int rcode;
- char *oldcurhost;
-
- if (mci->mci_state == MCIS_CLOSED)
- {
- mci_close(mci, "smtpquit:1");
- return;
- }
-
- oldcurhost = CurHostName;
- CurHostName = mci->mci_host; /* XXX UGLY XXX */
- if (CurHostName == NULL)
- CurHostName = MyHostName;
-
-#if PIPELINING
- mci->mci_okrcpts = 0;
-#endif /* PIPELINING */
-
- /*
- ** Suppress errors here -- we may be processing a different
- ** job when we do the quit connection, and we don't want the
- ** new job to be penalized for something that isn't it's
- ** problem.
- */
-
- SuprErrs = true;
-
- /* send the quit message if we haven't gotten I/O error */
- if (mci->mci_state != MCIS_ERROR &&
- mci->mci_state != MCIS_QUITING)
- {
- SmtpPhase = "client QUIT";
- mci->mci_state = MCIS_QUITING;
- smtpmessage("QUIT", m, mci);
- (void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL,
- XS_DEFAULT);
- SuprErrs = oldSuprErrs;
- if (mci->mci_state == MCIS_CLOSED)
- goto end;
- }
-
- /* now actually close the connection and pick up the zombie */
- 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",
- mailer == NULL ? "" : " ",
- mailer == NULL ? "" : mailer,
- rcode);
- }
-
- SuprErrs = oldSuprErrs;
-
- end:
- CurHostName = oldcurhost;
- return;
-}
-/*
-** SMTPRSET -- send a RSET (reset) command
-**
-** Parameters:
-** m -- a pointer to the mailer.
-** mci -- the mailer connection information.
-** e -- the current envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** closes the connection if there is no reply to RSET.
-*/
-
-void
-smtprset(m, mci, e)
- register MAILER *m;
- register MCI *mci;
- ENVELOPE *e;
-{
- int r;
-
- CurHostName = mci->mci_host; /* XXX UGLY XXX */
- if (CurHostName == NULL)
- CurHostName = MyHostName;
-
-#if PIPELINING
- mci->mci_okrcpts = 0;
-#endif /* PIPELINING */
-
- /*
- ** Check if connection is gone, if so
- ** it's a tempfail and we use mci_errno
- ** for the reason.
- */
-
- if (mci->mci_state == MCIS_CLOSED)
- {
- errno = mci->mci_errno;
- return;
- }
-
- SmtpPhase = "client RSET";
- smtpmessage("RSET", m, mci);
- r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL, XS_DEFAULT);
- if (r < 0)
- return;
-
- /*
- ** Any response is deemed to be acceptable.
- ** The standard does not state the proper action
- ** to take when a value other than 250 is received.
- **
- ** However, if 421 is returned for the RSET, leave
- ** mci_state alone (MCIS_SSD can be set in reply()
- ** and MCIS_CLOSED can be set in smtpquit() if
- ** reply() gets a 421 and calls smtpquit()).
- */
-
- if (mci->mci_state != MCIS_SSD && mci->mci_state != MCIS_CLOSED)
- mci->mci_state = MCIS_OPEN;
- else if (mci->mci_exitstat == EX_OK)
- mci_setstat(mci, EX_TEMPFAIL, "4.5.0", NULL);
-}
-/*
-** SMTPPROBE -- check the connection state
-**
-** Parameters:
-** mci -- the mailer connection information.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** closes the connection if there is no reply to RSET.
-*/
-
-int
-smtpprobe(mci)
- register MCI *mci;
-{
- int r;
- MAILER *m = mci->mci_mailer;
- ENVELOPE *e;
- extern ENVELOPE 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, NULL, XS_DEFAULT);
- if (REPLYTYPE(r) != 2)
- smtpquit(m, mci, e);
- return r;
-}
-/*
-** REPLY -- read arpanet reply
-**
-** Parameters:
-** m -- the mailer we are reading the reply from.
-** mci -- the mailer connection info structure.
-** e -- the current envelope.
-** timeout -- the timeout for reads.
-** pfunc -- processing function called on each line of response.
-** If null, no special processing is done.
-** enhstat -- optional, returns enhanced error code string (if set)
-** rtype -- type of SmtpMsgBuffer: does it contains secret data?
-**
-** Returns:
-** reply code it reads.
-**
-** Side Effects:
-** flushes the mail file.
-*/
-
-int
-reply(m, mci, e, timeout, pfunc, enhstat, rtype)
- MAILER *m;
- MCI *mci;
- ENVELOPE *e;
- time_t timeout;
- void (*pfunc) __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
- char **enhstat;
- int rtype;
-{
- register char *bufp;
- register int r;
- bool firstline = true;
- char junkbuf[MAXLINE];
- static char enhstatcode[ENHSCLEN];
- int save_errno;
-
- /*
- ** Flush the output before reading response.
- **
- ** For SMTP pipelining, it would be better if we didn't do
- ** this if there was already data waiting to be read. But
- ** to do it properly means pushing it to the I/O library,
- ** since it really needs to be done below the buffer layer.
- */
-
- if (mci->mci_out != NULL)
- (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
-
- if (tTd(18, 1))
- sm_dprintf("reply\n");
-
- /*
- ** Read the input line, being careful not to hang.
- */
-
- bufp = SmtpReplyBuffer;
- for (;;)
- {
- register char *p;
-
- /* actually do the read */
- if (e->e_xfp != NULL) /* for debugging */
- (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
-
- /* if we are in the process of closing just give the code */
- if (mci->mci_state == MCIS_CLOSED)
- return SMTPCLOSING;
-
- /* don't try to read from a non-existent fd */
- if (mci->mci_in == NULL)
- {
- if (mci->mci_errno == 0)
- mci->mci_errno = EBADF;
-
- /* errors on QUIT should be ignored */
- if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
- {
- errno = mci->mci_errno;
- mci_close(mci, "reply:1");
- return -1;
- }
- mci->mci_state = MCIS_ERROR;
- smtpquit(m, mci, e);
- errno = mci->mci_errno;
- return -1;
- }
-
- if (mci->mci_out != NULL)
- (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
-
- /* get the line from the other side */
- p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
- save_errno = errno;
- mci->mci_lastuse = curtime();
-
- if (p == NULL)
- {
- bool oldholderrs;
- extern char MsgBuf[];
-
- /* errors on QUIT should be ignored */
- if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
- {
- mci_close(mci, "reply:2");
- return -1;
- }
-
- /* if the remote end closed early, fake an error */
- errno = save_errno;
- if (errno == 0)
- {
- (void) sm_snprintf(SmtpReplyBuffer,
- sizeof(SmtpReplyBuffer),
- "421 4.4.1 Connection reset by %s",
- CURHOSTNAME);
-#ifdef ECONNRESET
- errno = ECONNRESET;
-#else /* ECONNRESET */
- errno = EPIPE;
-#endif /* ECONNRESET */
- }
-
- mci->mci_errno = errno;
- oldholderrs = HoldErrs;
- HoldErrs = true;
- usrerr("451 4.4.1 reply: read error from %s",
- CURHOSTNAME);
- mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
-
- /* if debugging, pause so we can see state */
- if (tTd(18, 100))
- (void) pause();
- mci->mci_state = MCIS_ERROR;
- smtpquit(m, mci, e);
-#if XDEBUG
- {
- char wbuf[MAXLINE];
-
- p = wbuf;
- if (e->e_to != NULL)
- {
- (void) sm_snprintf(p,
- SPACELEFT(wbuf, p),
- "%s... ",
- shortenstring(e->e_to, MAXSHORTSTR));
- p += strlen(p);
- }
- (void) sm_snprintf(p, SPACELEFT(wbuf, p),
- "reply(%.100s) during %s",
- CURHOSTNAME, SmtpPhase);
- checkfd012(wbuf);
- }
-#endif /* XDEBUG */
- HoldErrs = oldholderrs;
- errno = save_errno;
- return -1;
- }
- fixcrlf(bufp, true);
-
- /* EHLO failure is not a real error */
- if (e->e_xfp != NULL && (bufp[0] == '4' ||
- (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
- {
- /* serious error -- log the previous command */
- if (SmtpNeedIntro)
- {
- /* inform user who we are chatting with */
- (void) sm_io_fprintf(CurEnv->e_xfp,
- SM_TIME_DEFAULT,
- "... while talking to %s:\n",
- CURHOSTNAME);
- SmtpNeedIntro = false;
- }
- if (SmtpMsgBuffer[0] != '\0')
- {
- (void) sm_io_fprintf(e->e_xfp,
- SM_TIME_DEFAULT,
- ">>> %s\n",
- (rtype == XS_STARTTLS)
- ? "STARTTLS dialogue"
- : ((rtype == XS_AUTH)
- ? "AUTH dialogue"
- : SmtpMsgBuffer));
- SmtpMsgBuffer[0] = '\0';
- }
-
- /* now log the message as from the other side */
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "<<< %s\n", bufp);
- }
-
- /* display the input for verbose mode */
- if (Verbose)
- nmessage("050 %s", bufp);
-
- /* 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);
-
- firstline = false;
-
- /* decode the reply code */
- r = atoi(bufp);
-
- /* extra semantics: 0xx codes are "informational" */
- if (r < 100)
- continue;
-
- /* if no continuation lines, return this line */
- if (bufp[3] != '-')
- break;
-
- /* first line of real reply -- ignore rest */
- bufp = junkbuf;
- }
-
- /*
- ** Now look at SmtpReplyBuffer -- only care about the first
- ** line of the response from here on out.
- */
-
- /* save temporary failure messages for posterity */
- if (SmtpReplyBuffer[0] == '4')
- (void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof(SmtpError));
-
- /* reply code 421 is "Service Shutting Down" */
- if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD &&
- mci->mci_state != MCIS_QUITING)
- {
- /* send the quit protocol */
- mci->mci_state = MCIS_SSD;
- smtpquit(m, mci, e);
- }
-
- return r;
-}
-/*
-** SMTPMESSAGE -- send message to server
-**
-** Parameters:
-** f -- format
-** m -- the mailer to control formatting.
-** a, b, c -- parameters
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** writes message to mci->mci_out.
-*/
-
-/*VARARGS1*/
-void
-#ifdef __STDC__
-smtpmessage(char *f, MAILER *m, MCI *mci, ...)
-#else /* __STDC__ */
-smtpmessage(f, m, mci, va_alist)
- char *f;
- MAILER *m;
- MCI *mci;
- va_dcl
-#endif /* __STDC__ */
-{
- SM_VA_LOCAL_DECL
-
- SM_VA_START(ap, mci);
- (void) sm_vsnprintf(SmtpMsgBuffer, sizeof(SmtpMsgBuffer), f, ap);
- SM_VA_END(ap);
-
- if (tTd(18, 1) || Verbose)
- nmessage(">>> %s", SmtpMsgBuffer);
- if (TrafficLogFile != NULL)
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "%05d >>> %s\n", (int) CurrentPid,
- SmtpMsgBuffer);
- if (mci->mci_out != NULL)
- {
- (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
- SmtpMsgBuffer, m == NULL ? "\r\n"
- : m->m_eol);
- }
- else if (tTd(18, 1))
- {
- sm_dprintf("smtpmessage: NULL mci_out\n");
- }
-}
diff --git a/contrib/sendmail/src/util.c b/contrib/sendmail/src/util.c
deleted file mode 100644
index 95d2f9a..0000000
--- a/contrib/sendmail/src/util.c
+++ /dev/null
@@ -1,2853 +0,0 @@
-/*
- * Copyright (c) 1998-2007 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.
- *
- */
-
-#include <sendmail.h>
-
-SM_RCSID("@(#)$Id: util.c,v 8.413 2007/09/26 23:29:11 ca Exp $")
-
-#include <sm/sendmail.h>
-#include <sysexits.h>
-#include <sm/xtrap.h>
-
-/*
-** NEWSTR -- Create a copy of a C string
-**
-** Parameters:
-** s -- the string to copy.
-**
-** Returns:
-** pointer to newly allocated string.
-*/
-
-char *
-newstr(s)
- const char *s;
-{
- size_t l;
- char *n;
-
- l = strlen(s);
- SM_ASSERT(l + 1 > l);
- n = xalloc(l + 1);
- sm_strlcpy(n, s, l + 1);
- return n;
-}
-
-/*
-** ADDQUOTES -- Adds quotes & quote bits to a string.
-**
-** Runs through a string and adds backslashes and quote bits.
-**
-** Parameters:
-** s -- the string to modify.
-** rpool -- resource pool from which to allocate result
-**
-** Returns:
-** pointer to quoted string.
-*/
-
-char *
-addquotes(s, rpool)
- char *s;
- SM_RPOOL_T *rpool;
-{
- int len = 0;
- char c;
- char *p = s, *q, *r;
-
- if (s == NULL)
- return NULL;
-
- /* Find length of quoted string */
- while ((c = *p++) != '\0')
- {
- len++;
- if (c == '\\' || c == '"')
- len++;
- }
-
- q = r = sm_rpool_malloc_x(rpool, len + 3);
- p = s;
-
- /* add leading quote */
- *q++ = '"';
- while ((c = *p++) != '\0')
- {
- /* quote \ or " */
- if (c == '\\' || c == '"')
- *q++ = '\\';
- *q++ = c;
- }
- *q++ = '"';
- *q = '\0';
- return r;
-}
-
-/*
-** STRIPBACKSLASH -- Strip all leading backslashes from a string, provided
-** the following character is alpha-numerical.
-**
-** This is done in place.
-**
-** Parameters:
-** s -- the string to strip.
-**
-** Returns:
-** none.
-*/
-
-void
-stripbackslash(s)
- char *s;
-{
- char *p, *q, c;
-
- if (s == NULL || *s == '\0')
- return;
- p = q = s;
- while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1]))))
- p++;
- do
- {
- c = *q++ = *p++;
- } while (c != '\0');
-}
-
-/*
-** RFC822_STRING -- Checks string for proper RFC822 string quoting.
-**
-** Runs through a string and verifies RFC822 special characters
-** are only found inside comments, quoted strings, or backslash
-** escaped. Also verified balanced quotes and parenthesis.
-**
-** Parameters:
-** s -- the string to modify.
-**
-** Returns:
-** true iff the string is RFC822 compliant, false otherwise.
-*/
-
-bool
-rfc822_string(s)
- char *s;
-{
- bool quoted = false;
- int commentlev = 0;
- char *c = s;
-
- if (s == NULL)
- return false;
-
- while (*c != '\0')
- {
- /* escaped character */
- if (*c == '\\')
- {
- c++;
- if (*c == '\0')
- return false;
- }
- else if (commentlev == 0 && *c == '"')
- quoted = !quoted;
- else if (!quoted)
- {
- if (*c == ')')
- {
- /* unbalanced ')' */
- if (commentlev == 0)
- return false;
- else
- commentlev--;
- }
- else if (*c == '(')
- commentlev++;
- else if (commentlev == 0 &&
- strchr(MustQuoteChars, *c) != NULL)
- return false;
- }
- c++;
- }
-
- /* unbalanced '"' or '(' */
- return !quoted && commentlev == 0;
-}
-
-/*
-** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
-**
-** Arbitrarily shorten (in place) an RFC822 string and rebalance
-** comments and quotes.
-**
-** Parameters:
-** string -- the string to shorten
-** length -- the maximum size, 0 if no maximum
-**
-** Returns:
-** true if string is changed, false otherwise
-**
-** Side Effects:
-** Changes string in place, possibly resulting
-** in a shorter string.
-*/
-
-bool
-shorten_rfc822_string(string, length)
- char *string;
- size_t length;
-{
- bool backslash = false;
- bool modified = false;
- bool quoted = false;
- 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.
- */
-
- slen = strlen(string);
- if (length == 0 || slen < length)
- length = slen;
-
- while (*ptr != '\0')
- {
- if (backslash)
- {
- backslash = false;
- goto increment;
- }
-
- if (*ptr == '\\')
- backslash = true;
- else if (*ptr == '(')
- {
- if (!quoted)
- parencount++;
- }
- else if (*ptr == ')')
- {
- 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) <= (size_t) ((backslash ? 1 : 0) +
- parencount +
- (quoted ? 1 : 0)))
- {
- /* Not enough, backtrack */
- if (*ptr == '\\')
- backslash = false;
- else if (*ptr == '(' && !quoted)
- parencount--;
- else if (*ptr == '"' && parencount == 0)
- quoted = false;
- break;
- }
- ptr++;
- }
-
- /* Rebalance */
- while (parencount-- > 0)
- {
- if (*ptr != ')')
- {
- modified = true;
- *ptr = ')';
- }
- ptr++;
- }
- if (quoted)
- {
- if (*ptr != '"')
- {
- modified = true;
- *ptr = '"';
- }
- ptr++;
- }
- if (*ptr != '\0')
- {
- modified = true;
- *ptr = '\0';
- }
- return modified;
-}
-
-/*
-** FIND_CHARACTER -- find an unquoted character in an RFC822 string
-**
-** Find an unquoted, non-commented character in an RFC822
-** string and return a pointer to its location in the
-** string.
-**
-** Parameters:
-** string -- the string to search
-** character -- the character to find
-**
-** Returns:
-** pointer to the character, or
-** a pointer to the end of the line if character is not found
-*/
-
-char *
-find_character(string, character)
- char *string;
- int character;
-{
- bool backslash = false;
- bool quoted = false;
- int parencount = 0;
-
- while (string != NULL && *string != '\0')
- {
- if (backslash)
- {
- backslash = false;
- if (!quoted && character == '\\' && *string == '\\')
- break;
- string++;
- continue;
- }
- switch (*string)
- {
- 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)
- break;
- string++;
- }
-
- /* Return pointer to the character */
- return string;
-}
-
-/*
-** CHECK_BODYTYPE -- check bodytype parameter
-**
-** Parameters:
-** bodytype -- bodytype parameter
-**
-** Returns:
-** BODYTYPE_* according to parameter
-**
-*/
-
-int
-check_bodytype(bodytype)
- char *bodytype;
-{
- /* check body type for legality */
- if (bodytype == NULL)
- return BODYTYPE_NONE;
- if (sm_strcasecmp(bodytype, "7BIT") == 0)
- return BODYTYPE_7BIT;
- if (sm_strcasecmp(bodytype, "8BITMIME") == 0)
- return BODYTYPE_8BITMIME;
- return BODYTYPE_ILLEGAL;
-}
-
-/*
-** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
-**
-** Parameters:
-** str -- string to truncate
-** len -- maximum length (including '\0') (0 for unlimited)
-** delim -- delimiter character
-**
-** Returns:
-** None.
-*/
-
-void
-truncate_at_delim(str, len, delim)
- char *str;
- size_t len;
- int delim;
-{
- char *p;
-
- if (str == NULL || len == 0 || strlen(str) < len)
- return;
-
- *(str + len - 1) = '\0';
- while ((p = strrchr(str, delim)) != NULL)
- {
- *p = '\0';
- if (p - str + 4 < len)
- {
- *p++ = (char) delim;
- *p = '\0';
- (void) sm_strlcat(str, "...", len);
- return;
- }
- }
-
- /* Couldn't find a place to append "..." */
- if (len > 3)
- (void) sm_strlcpy(str, "...", len);
- else
- str[0] = '\0';
-}
-
-/*
-** XALLOC -- Allocate memory, raise an exception on error
-**
-** Parameters:
-** sz -- size of area to allocate.
-**
-** Returns:
-** pointer to data region.
-**
-** Exceptions:
-** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
-**
-** Side Effects:
-** Memory is allocated.
-*/
-
-char *
-#if SM_HEAP_CHECK
-xalloc_tagged(sz, file, line)
- register int sz;
- char *file;
- int line;
-#else /* SM_HEAP_CHECK */
-xalloc(sz)
- register int sz;
-#endif /* SM_HEAP_CHECK */
-{
- register char *p;
-
- SM_REQUIRE(sz >= 0);
-
- /* some systems can't handle size zero mallocs */
- if (sz <= 0)
- sz = 1;
-
- /* scaffolding for testing error handling code */
- sm_xtrap_raise_x(&SmHeapOutOfMemory);
-
- p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group());
- if (p == NULL)
- {
- sm_exc_raise_x(&SmHeapOutOfMemory);
- }
- return p;
-}
-
-/*
-** COPYPLIST -- copy list of pointers.
-**
-** This routine is the equivalent of strdup for lists of
-** pointers.
-**
-** Parameters:
-** list -- list of pointers to copy.
-** Must be NULL terminated.
-** copycont -- if true, copy the contents of the vector
-** (which must be a string) also.
-** rpool -- resource pool from which to allocate storage,
-** or NULL
-**
-** Returns:
-** a copy of 'list'.
-*/
-
-char **
-copyplist(list, copycont, rpool)
- char **list;
- bool copycont;
- SM_RPOOL_T *rpool;
-{
- register char **vp;
- register char **newvp;
-
- for (vp = list; *vp != NULL; vp++)
- continue;
-
- vp++;
-
- newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof(*vp));
- memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof(*vp));
-
- if (copycont)
- {
- for (vp = newvp; *vp != NULL; vp++)
- *vp = sm_rpool_strdup_x(rpool, *vp);
- }
-
- return newvp;
-}
-
-/*
-** COPYQUEUE -- copy address queue.
-**
-** This routine is the equivalent of strdup for address queues;
-** addresses marked as QS_IS_DEAD() aren't copied
-**
-** Parameters:
-** addr -- list of address structures to copy.
-** rpool -- resource pool from which to allocate storage
-**
-** Returns:
-** a copy of 'addr'.
-*/
-
-ADDRESS *
-copyqueue(addr, rpool)
- ADDRESS *addr;
- SM_RPOOL_T *rpool;
-{
- register ADDRESS *newaddr;
- ADDRESS *ret;
- register ADDRESS **tail = &ret;
-
- while (addr != NULL)
- {
- if (!QS_IS_DEAD(addr->q_state))
- {
- newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool,
- sizeof(*newaddr));
- STRUCTCOPY(*addr, *newaddr);
- *tail = newaddr;
- tail = &newaddr->q_next;
- }
- 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, logs command line.
-** keeps file open and locked to prevent overwrite of active file
-*/
-
-static SM_FILE_T *Pidf = NULL;
-
-void
-log_sendmail_pid(e)
- ENVELOPE *e;
-{
- long sff;
- char pidpath[MAXPATHLEN];
- extern char *CommandLineArgs;
-
- /* write the pid to the log file for posterity */
- sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT|SFF_NBLOCK;
- if (TrustedUid != 0 && RealUid == TrustedUid)
- sff |= SFF_OPENASROOT;
- expand(PidFile, pidpath, sizeof(pidpath), e);
- Pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff);
- if (Pidf == NULL)
- {
- if (errno == EWOULDBLOCK)
- sm_syslog(LOG_ERR, NOQID,
- "unable to write pid to %s: file in use by another process",
- pidpath);
- else
- sm_syslog(LOG_ERR, NOQID,
- "unable to write pid to %s: %s",
- pidpath, sm_errstring(errno));
- }
- else
- {
- PidFilePid = getpid();
-
- /* write the process id on line 1 */
- (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%ld\n",
- (long) PidFilePid);
-
- /* line 2 contains all command line flags */
- (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%s\n",
- CommandLineArgs);
-
- /* flush */
- (void) sm_io_flush(Pidf, SM_TIME_DEFAULT);
-
- /*
- ** Leave pid file open until process ends
- ** so it's not overwritten by another
- ** process.
- */
- }
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs);
-}
-
-/*
-** CLOSE_SENDMAIL_PID -- close sendmail pid file
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-*/
-
-void
-close_sendmail_pid()
-{
- if (Pidf == NULL)
- return;
-
- (void) sm_io_close(Pidf, SM_TIME_DEFAULT);
- Pidf = NULL;
-}
-
-/*
-** 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';
- macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf);
-}
-
-/*
-** SET_OP_MODE -- set and record the op mode
-**
-** Parameters:
-** mode -- op mode
-** e -- the current envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** sets {opMode} macro
-*/
-
-void
-set_op_mode(mode)
- int mode;
-{
- char buf[2];
- extern ENVELOPE BlankEnvelope;
-
- OpMode = (char) mode;
- buf[0] = (char) mode;
- buf[1] = '\0';
- macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf);
-}
-
-/*
-** PRINTAV -- print argument vector.
-**
-** Parameters:
-** fp -- output file pointer.
-** av -- argument vector.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** prints av.
-*/
-
-void
-printav(fp, av)
- SM_FILE_T *fp;
- char **av;
-{
- while (*av != NULL)
- {
- if (tTd(0, 44))
- sm_dprintf("\n\t%08lx=", (unsigned long) *av);
- else
- (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' ');
- if (tTd(0, 99))
- sm_dprintf("%s", str2prt(*av++));
- else
- xputs(fp, *av++);
- }
- (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n');
-}
-
-/*
-** XPUTS -- put string doing control escapes.
-**
-** Parameters:
-** fp -- output file pointer.
-** s -- string to put.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** output to stdout
-*/
-
-void
-xputs(fp, s)
- SM_FILE_T *fp;
- const char *s;
-{
- int c;
- struct metamac *mp;
- bool shiftout = false;
- extern struct metamac MetaMacros[];
- static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
- "@(#)$Debug: ANSI - enable reverse video in debug output $");
-
- /*
- ** TermEscape is set here, rather than in main(),
- ** because ANSI mode can be turned on or off at any time
- ** if we are in -bt rule testing mode.
- */
-
- if (sm_debug_unknown(&DebugANSI))
- {
- if (sm_debug_active(&DebugANSI, 1))
- {
- TermEscape.te_rv_on = "\033[7m";
- TermEscape.te_normal = "\033[0m";
- }
- else
- {
- TermEscape.te_rv_on = "";
- TermEscape.te_normal = "";
- }
- }
-
- if (s == NULL)
- {
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s",
- TermEscape.te_rv_on, TermEscape.te_normal);
- return;
- }
- while ((c = (*s++ & 0377)) != '\0')
- {
- if (shiftout)
- {
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
- TermEscape.te_normal);
- shiftout = false;
- }
- if (!isascii(c) && !tTd(84, 1))
- {
- if (c == MATCHREPL)
- {
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "%s$",
- TermEscape.te_rv_on);
- shiftout = true;
- if (*s == '\0')
- continue;
- c = *s++ & 0377;
- goto printchar;
- }
- if (c == MACROEXPAND || c == MACRODEXPAND)
- {
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
- "%s$",
- TermEscape.te_rv_on);
- if (c == MACRODEXPAND)
- (void) sm_io_putc(fp,
- SM_TIME_DEFAULT, '&');
- shiftout = true;
- if (*s == '\0')
- continue;
- if (strchr("=~&?", *s) != NULL)
- (void) sm_io_putc(fp,
- SM_TIME_DEFAULT,
- *s++);
- if (bitset(0200, *s))
- (void) sm_io_fprintf(fp,
- SM_TIME_DEFAULT,
- "{%s}",
- macname(bitidx(*s++)));
- else
- (void) sm_io_fprintf(fp,
- SM_TIME_DEFAULT,
- "%c",
- *s++);
- continue;
- }
- for (mp = MetaMacros; mp->metaname != '\0'; mp++)
- {
- if (bitidx(mp->metaval) == c)
- {
- (void) sm_io_fprintf(fp,
- SM_TIME_DEFAULT,
- "%s$%c",
- TermEscape.te_rv_on,
- mp->metaname);
- shiftout = true;
- break;
- }
- }
- if (c == MATCHCLASS || c == MATCHNCLASS)
- {
- if (bitset(0200, *s))
- (void) sm_io_fprintf(fp,
- SM_TIME_DEFAULT,
- "{%s}",
- macname(bitidx(*s++)));
- else if (*s != '\0')
- (void) sm_io_fprintf(fp,
- SM_TIME_DEFAULT,
- "%c",
- *s++);
- }
- if (mp->metaname != '\0')
- continue;
-
- /* unrecognized meta character */
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-",
- TermEscape.te_rv_on);
- shiftout = true;
- c &= 0177;
- }
- printchar:
- if (isprint(c))
- {
- (void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
- continue;
- }
-
- /* wasn't a meta-macro -- find another way to print it */
- switch (c)
- {
- case '\n':
- c = 'n';
- break;
-
- case '\r':
- c = 'r';
- break;
-
- case '\t':
- c = 't';
- break;
- }
- if (!shiftout)
- {
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
- TermEscape.te_rv_on);
- shiftout = true;
- }
- if (isprint(c))
- {
- (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\');
- (void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
- }
- else if (tTd(84, 2))
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %o ", c);
- else if (tTd(84, 1))
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %#x ", c);
- else if (!isascii(c) && !tTd(84, 1))
- {
- (void) sm_io_putc(fp, SM_TIME_DEFAULT, '^');
- (void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100);
- }
- }
- if (shiftout)
- (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
- TermEscape.te_normal);
- (void) sm_io_flush(fp, SM_TIME_DEFAULT);
-}
-
-/*
-** MAKELOWER -- Translate a line into lower case
-**
-** Parameters:
-** p -- the string to translate. If NULL, return is
-** immediate.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** String pointed to by p is translated to lower case.
-*/
-
-void
-makelower(p)
- register char *p;
-{
- register char c;
-
- if (p == NULL)
- return;
- for (; (c = *p) != '\0'; p++)
- if (isascii(c) && isupper(c))
- *p = tolower(c);
-}
-
-/*
-** FIXCRLF -- fix <CR><LF> in line.
-**
-** Looks for the <CR><LF> combination and turns it into the
-** UNIX canonical <NL> character. It only takes one line,
-** i.e., it is assumed that the first <NL> found is the end
-** of the line.
-**
-** Parameters:
-** line -- the line to fix.
-** stripnl -- if true, strip the newline also.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** line is changed in place.
-*/
-
-void
-fixcrlf(line, stripnl)
- char *line;
- bool stripnl;
-{
- register char *p;
-
- p = strchr(line, '\n');
- if (p == NULL)
- return;
- if (p > line && p[-1] == '\r')
- p--;
- if (!stripnl)
- *p++ = '\n';
- *p = '\0';
-}
-
-/*
-** PUTLINE -- put a line like fputs obeying SMTP conventions
-**
-** This routine always guarantees outputing a newline (or CRLF,
-** as appropriate) at the end of the string.
-**
-** Parameters:
-** l -- line to put.
-** mci -- the mailer connection information.
-**
-** Returns:
-** true iff line was written successfully
-**
-** Side Effects:
-** output of l to mci->mci_out.
-*/
-
-bool
-putline(l, mci)
- register char *l;
- register MCI *mci;
-{
- return putxline(l, strlen(l), mci, PXLF_MAPFROM);
-}
-
-/*
-** PUTXLINE -- putline with flags bits.
-**
-** This routine always guarantees outputing a newline (or CRLF,
-** as appropriate) at the end of the string.
-**
-** Parameters:
-** l -- line to put.
-** len -- the length of the line.
-** mci -- the mailer connection information.
-** pxflags -- flag bits:
-** PXLF_MAPFROM -- map From_ to >From_.
-** PXLF_STRIP8BIT -- strip 8th bit.
-** PXLF_HEADER -- map bare newline in header to newline space.
-** PXLF_NOADDEOL -- don't add an EOL if one wasn't present.
-** PXLF_STRIPMQUOTE -- strip METAQUOTE bytes.
-**
-** Returns:
-** true iff line was written successfully
-**
-** Side Effects:
-** output of l to mci->mci_out.
-*/
-
-
-#define PUTX(limit) \
- do \
- { \
- quotenext = false; \
- while (l < limit) \
- { \
- unsigned char c = (unsigned char) *l++; \
- \
- if (bitset(PXLF_STRIPMQUOTE, pxflags) && \
- !quotenext && c == METAQUOTE) \
- { \
- quotenext = true; \
- continue; \
- } \
- quotenext = false; \
- if (strip8bit) \
- c &= 0177; \
- if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, \
- c) == SM_IO_EOF) \
- { \
- dead = true; \
- break; \
- } \
- if (TrafficLogFile != NULL) \
- (void) sm_io_putc(TrafficLogFile, \
- SM_TIME_DEFAULT, \
- c); \
- } \
- } while (0)
-
-bool
-putxline(l, len, mci, pxflags)
- register char *l;
- size_t len;
- register MCI *mci;
- int pxflags;
-{
- register char *p, *end;
- int slop;
- bool dead, quotenext, strip8bit;
-
- /* strip out 0200 bits -- these can look like TELNET protocol */
- strip8bit = bitset(MCIF_7BIT, mci->mci_flags) ||
- bitset(PXLF_STRIP8BIT, pxflags);
- dead = false;
- slop = 0;
-
- end = l + len;
- do
- {
- bool noeol = false;
-
- /* find the end of the line */
- p = memchr(l, '\n', end - l);
- if (p == NULL)
- {
- p = end;
- noeol = true;
- }
-
- if (TrafficLogFile != NULL)
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "%05d >>> ", (int) CurrentPid);
-
- /* check for line overflow */
- while (mci->mci_mailer->m_linelimit > 0 &&
- (p - l + slop) > mci->mci_mailer->m_linelimit)
- {
- register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
-
- if (l[0] == '.' && slop == 0 &&
- bitnset(M_XDOT, mci->mci_mailer->m_flags))
- {
- if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
- '.') == SM_IO_EOF)
- dead = true;
- if (TrafficLogFile != NULL)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT, '.');
- }
- else if (l[0] == 'F' && slop == 0 &&
- bitset(PXLF_MAPFROM, pxflags) &&
- strncmp(l, "From ", 5) == 0 &&
- bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
- {
- if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
- '>') == SM_IO_EOF)
- dead = true;
- if (TrafficLogFile != NULL)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT,
- '>');
- }
- if (dead)
- break;
-
- PUTX(q);
- if (dead)
- break;
-
- if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
- '!') == SM_IO_EOF ||
- sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol) == SM_IO_EOF ||
- sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
- ' ') == SM_IO_EOF)
- {
- dead = true;
- break;
- }
- if (TrafficLogFile != NULL)
- {
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT,
- "!\n%05d >>> ",
- (int) CurrentPid);
- }
- slop = 1;
- }
-
- if (dead)
- break;
-
- /* output last part */
- if (l[0] == '.' && slop == 0 &&
- bitnset(M_XDOT, mci->mci_mailer->m_flags) &&
- !bitset(MCIF_INLONGLINE, mci->mci_flags))
- {
- if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') ==
- SM_IO_EOF)
- {
- dead = true;
- break;
- }
- if (TrafficLogFile != NULL)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT, '.');
- }
- else if (l[0] == 'F' && slop == 0 &&
- bitset(PXLF_MAPFROM, pxflags) &&
- strncmp(l, "From ", 5) == 0 &&
- bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
- !bitset(MCIF_INLONGLINE, mci->mci_flags))
- {
- if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') ==
- SM_IO_EOF)
- {
- dead = true;
- break;
- }
- if (TrafficLogFile != NULL)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT, '>');
- }
- PUTX(p);
- if (dead)
- break;
-
- if (TrafficLogFile != NULL)
- (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT,
- '\n');
- if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol))
- {
- mci->mci_flags &= ~MCIF_INLONGLINE;
- if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol) == SM_IO_EOF)
- {
- dead = true;
- break;
- }
- }
- else
- mci->mci_flags |= MCIF_INLONGLINE;
-
- if (l < end && *l == '\n')
- {
- if (*++l != ' ' && *l != '\t' && *l != '\0' &&
- bitset(PXLF_HEADER, pxflags))
- {
- if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
- ' ') == SM_IO_EOF)
- {
- dead = true;
- break;
- }
-
- if (TrafficLogFile != NULL)
- (void) sm_io_putc(TrafficLogFile,
- SM_TIME_DEFAULT, ' ');
- }
- }
-
- } while (l < end);
- return !dead;
-}
-
-/*
-** XUNLINK -- unlink a file, doing logging as appropriate.
-**
-** Parameters:
-** f -- name of file to unlink.
-**
-** Returns:
-** return value of unlink()
-**
-** Side Effects:
-** f is unlinked.
-*/
-
-int
-xunlink(f)
- char *f;
-{
- register int i;
- int save_errno;
-
- if (LogLevel > 98)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f);
-
- i = unlink(f);
- save_errno = errno;
- if (i < 0 && LogLevel > 97)
- sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d",
- f, errno);
- if (i >= 0)
- SYNC_DIR(f, false);
- errno = save_errno;
- return i;
-}
-
-/*
-** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
-**
-** Parameters:
-** buf -- place to put the input line.
-** siz -- size of buf.
-** fp -- file to read from.
-** timeout -- the timeout before error occurs.
-** during -- what we are trying to read (for error messages).
-**
-** Returns:
-** NULL on error (including timeout). This may also leave
-** buf containing a null string.
-** buf otherwise.
-*/
-
-
-char *
-sfgets(buf, siz, fp, timeout, during)
- char *buf;
- int siz;
- SM_FILE_T *fp;
- time_t timeout;
- char *during;
-{
- register char *p;
- int save_errno;
- int io_timeout;
-
- SM_REQUIRE(siz > 0);
- SM_REQUIRE(buf != NULL);
-
- if (fp == NULL)
- {
- buf[0] = '\0';
- errno = EBADF;
- return NULL;
- }
-
- /* try to read */
- p = NULL;
- errno = 0;
-
- /* convert the timeout to sm_io notation */
- io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000;
- while (!sm_io_eof(fp) && !sm_io_error(fp))
- {
- errno = 0;
- p = sm_io_fgets(fp, io_timeout, buf, siz);
- if (p == NULL && errno == EAGAIN)
- {
- /* The sm_io_fgets() call timedout */
- if (LogLevel > 1)
- sm_syslog(LOG_NOTICE, CurEnv->e_id,
- "timeout waiting for input from %.100s during %s",
- CURHOSTNAME,
- during);
- buf[0] = '\0';
-#if XDEBUG
- checkfd012(during);
-#endif /* XDEBUG */
- if (TrafficLogFile != NULL)
- (void) sm_io_fprintf(TrafficLogFile,
- SM_TIME_DEFAULT,
- "%05d <<< [TIMEOUT]\n",
- (int) CurrentPid);
- errno = ETIMEDOUT;
- return NULL;
- }
- if (p != NULL || errno != EINTR)
- break;
- (void) sm_io_clearerr(fp);
- }
- save_errno = errno;
-
- /* clean up the books and exit */
- LineNumber++;
- if (p == NULL)
- {
- buf[0] = '\0';
- if (TrafficLogFile != NULL)
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "%05d <<< [EOF]\n",
- (int) CurrentPid);
- errno = save_errno;
- return NULL;
- }
- if (TrafficLogFile != NULL)
- (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
- "%05d <<< %s", (int) CurrentPid, buf);
- if (SevenBitInput)
- {
- for (p = buf; *p != '\0'; p++)
- *p &= ~0200;
- }
- else if (!HasEightBits)
- {
- for (p = buf; *p != '\0'; p++)
- {
- if (bitset(0200, *p))
- {
- HasEightBits = true;
- break;
- }
- }
- }
- return buf;
-}
-
-/*
-** FGETFOLDED -- like fgets, but knows about folded lines.
-**
-** Parameters:
-** buf -- place to put result.
-** np -- pointer to bytes available; will be updated with
-** the actual buffer size (not number of bytes filled)
-** on return.
-** f -- file to read from.
-**
-** Returns:
-** input line(s) on success, NULL on error or SM_IO_EOF.
-** This will normally be buf -- unless the line is too
-** long, when it will be sm_malloc_x()ed.
-**
-** Side Effects:
-** buf gets lines from f, with continuation lines (lines
-** with leading white space) appended. CRLF's are mapped
-** into single newlines. Any trailing NL is stripped.
-*/
-
-char *
-fgetfolded(buf, np, f)
- char *buf;
- int *np;
- SM_FILE_T *f;
-{
- register char *p = buf;
- char *bp = buf;
- register int i;
- int n;
-
- SM_REQUIRE(np != NULL);
- n = *np;
- SM_REQUIRE(n > 0);
- SM_REQUIRE(buf != NULL);
- if (f == NULL)
- {
- buf[0] = '\0';
- errno = EBADF;
- return NULL;
- }
-
- n--;
- while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF)
- {
- if (i == '\r')
- {
- i = sm_io_getc(f, SM_TIME_DEFAULT);
- if (i != '\n')
- {
- if (i != SM_IO_EOF)
- (void) sm_io_ungetc(f, SM_TIME_DEFAULT,
- i);
- i = '\r';
- }
- }
- if (--n <= 0)
- {
- /* allocate new space */
- char *nbp;
- int nn;
-
- nn = (p - bp);
- if (nn < MEMCHUNKSIZE)
- nn *= 2;
- else
- nn += MEMCHUNKSIZE;
- nbp = sm_malloc_x(nn);
- memmove(nbp, bp, p - bp);
- p = &nbp[p - bp];
- if (bp != buf)
- sm_free(bp);
- bp = nbp;
- n = nn - (p - bp);
- *np = nn;
- }
- *p++ = i;
- if (i == '\n')
- {
- LineNumber++;
- i = sm_io_getc(f, SM_TIME_DEFAULT);
- if (i != SM_IO_EOF)
- (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i);
- if (i != ' ' && i != '\t')
- break;
- }
- }
- if (p == bp)
- return NULL;
- if (p[-1] == '\n')
- p--;
- *p = '\0';
- return bp;
-}
-
-/*
-** CURTIME -- return current time.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** the current time.
-*/
-
-time_t
-curtime()
-{
- auto time_t t;
-
- (void) time(&t);
- return t;
-}
-
-/*
-** ATOBOOL -- convert a string representation to boolean.
-**
-** Defaults to false
-**
-** Parameters:
-** s -- string to convert. Takes "tTyY", empty, and NULL as true,
-** others as false.
-**
-** Returns:
-** A boolean representation of the string.
-*/
-
-bool
-atobool(s)
- register char *s;
-{
- if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
- return true;
- return false;
-}
-
-/*
-** ATOOCT -- convert a string representation to octal.
-**
-** Parameters:
-** s -- string to convert.
-**
-** Returns:
-** An integer representing the string interpreted as an
-** octal number.
-*/
-
-int
-atooct(s)
- register char *s;
-{
- register int i = 0;
-
- while (*s >= '0' && *s <= '7')
- i = (i << 3) | (*s++ - '0');
- return i;
-}
-
-/*
-** BITINTERSECT -- tell if two bitmaps intersect
-**
-** Parameters:
-** a, b -- the bitmaps in question
-**
-** Returns:
-** true if they have a non-null intersection
-** false otherwise
-*/
-
-bool
-bitintersect(a, b)
- BITMAP256 a;
- BITMAP256 b;
-{
- int i;
-
- for (i = BITMAPBYTES / sizeof(int); --i >= 0; )
- {
- if ((a[i] & b[i]) != 0)
- return true;
- }
- return false;
-}
-
-/*
-** BITZEROP -- tell if a bitmap is all zero
-**
-** Parameters:
-** map -- the bit map to check
-**
-** Returns:
-** true if map is all zero.
-** false if there are any bits set in map.
-*/
-
-bool
-bitzerop(map)
- BITMAP256 map;
-{
- int i;
-
- for (i = BITMAPBYTES / sizeof(int); --i >= 0; )
- {
- if (map[i] != 0)
- return false;
- }
- return true;
-}
-
-/*
-** STRCONTAINEDIN -- tell if one string is contained in another
-**
-** Parameters:
-** icase -- ignore case?
-** a -- possible substring.
-** b -- possible superstring.
-**
-** Returns:
-** true if a is contained in b (case insensitive).
-** false otherwise.
-*/
-
-bool
-strcontainedin(icase, a, b)
- bool icase;
- register char *a;
- register char *b;
-{
- int la;
- int lb;
- int c;
-
- la = strlen(a);
- lb = strlen(b);
- c = *a;
- if (icase && isascii(c) && isupper(c))
- c = tolower(c);
- for (; lb-- >= la; b++)
- {
- if (icase)
- {
- if (*b != c &&
- isascii(*b) && isupper(*b) && tolower(*b) != c)
- continue;
- if (sm_strncasecmp(a, b, la) == 0)
- return true;
- }
- else
- {
- if (*b != c)
- continue;
- if (strncmp(a, b, la) == 0)
- return true;
- }
- }
- return false;
-}
-
-/*
-** CHECKFD012 -- check low numbered file descriptors
-**
-** File descriptors 0, 1, and 2 should be open at all times.
-** This routine verifies that, and fixes it if not true.
-**
-** Parameters:
-** where -- a tag printed if the assertion failed
-**
-** Returns:
-** none
-*/
-
-void
-checkfd012(where)
- char *where;
-{
-#if XDEBUG
- register int i;
-
- for (i = 0; i < 3; i++)
- fill_fd(i, where);
-#endif /* XDEBUG */
-}
-
-/*
-** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
-**
-** Parameters:
-** fd -- file descriptor to check.
-** where -- tag to print on failure.
-**
-** Returns:
-** none.
-*/
-
-void
-checkfdopen(fd, where)
- int fd;
- char *where;
-{
-#if XDEBUG
- struct stat st;
-
- if (fstat(fd, &st) < 0 && errno == EBADF)
- {
- syserr("checkfdopen(%d): %s not open as expected!", fd, where);
- printopenfds(true);
- }
-#endif /* XDEBUG */
-}
-
-/*
-** CHECKFDS -- check for new or missing file descriptors
-**
-** Parameters:
-** where -- tag for printing. If null, take a base line.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** If where is set, shows changes since the last call.
-*/
-
-void
-checkfds(where)
- char *where;
-{
- int maxfd;
- register int fd;
- bool printhdr = true;
- int save_errno = errno;
- static BITMAP256 baseline;
- extern int DtableSize;
-
- if (DtableSize > BITMAPBITS)
- maxfd = BITMAPBITS;
- else
- maxfd = DtableSize;
- if (where == NULL)
- clrbitmap(baseline);
-
- for (fd = 0; fd < maxfd; fd++)
- {
- struct stat stbuf;
-
- if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
- {
- if (!bitnset(fd, baseline))
- continue;
- clrbitn(fd, baseline);
- }
- else if (!bitnset(fd, baseline))
- setbitn(fd, baseline);
- else
- continue;
-
- /* file state has changed */
- if (where == NULL)
- continue;
- if (printhdr)
- {
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "%s: changed fds:",
- where);
- printhdr = false;
- }
- dumpfd(fd, true, true);
- }
- errno = save_errno;
-}
-
-/*
-** PRINTOPENFDS -- print the open file descriptors (for debugging)
-**
-** Parameters:
-** logit -- if set, send output to syslog; otherwise
-** print for debugging.
-**
-** Returns:
-** none.
-*/
-
-#if NETINET || NETINET6
-# include <arpa/inet.h>
-#endif /* NETINET || NETINET6 */
-
-void
-printopenfds(logit)
- bool logit;
-{
- register int fd;
- extern int DtableSize;
-
- for (fd = 0; fd < DtableSize; fd++)
- dumpfd(fd, false, logit);
-}
-
-/*
-** DUMPFD -- dump a file descriptor
-**
-** Parameters:
-** fd -- the file descriptor to dump.
-** printclosed -- if set, print a notification even if
-** it is closed; otherwise print nothing.
-** logit -- if set, use sm_syslog instead of sm_dprintf()
-**
-** Returns:
-** none.
-*/
-
-void
-dumpfd(fd, printclosed, logit)
- int fd;
- bool printclosed;
- bool logit;
-{
- register char *p;
- char *hp;
-#ifdef S_IFSOCK
- SOCKADDR sa;
-#endif /* S_IFSOCK */
- auto SOCKADDR_LEN_T slen;
- int i;
-#if STAT64 > 0
- struct stat64 st;
-#else /* STAT64 > 0 */
- struct stat st;
-#endif /* STAT64 > 0 */
- char buf[200];
-
- p = buf;
- (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
- p += strlen(p);
-
- if (
-#if STAT64 > 0
- fstat64(fd, &st)
-#else /* STAT64 > 0 */
- fstat(fd, &st)
-#endif /* STAT64 > 0 */
- < 0)
- {
- if (errno != EBADF)
- {
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "CANNOT STAT (%s)",
- sm_errstring(errno));
- goto printit;
- }
- else if (printclosed)
- {
- (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED");
- goto printit;
- }
- return;
- }
-
- i = fcntl(fd, F_GETFL, 0);
- if (i != -1)
- {
- (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
- p += strlen(p);
- }
-
- (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ",
- (int) st.st_mode);
- p += strlen(p);
- switch (st.st_mode & S_IFMT)
- {
-#ifdef S_IFSOCK
- case S_IFSOCK:
- (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK ");
- p += strlen(p);
- memset(&sa, '\0', sizeof(sa));
- slen = sizeof(sa);
- if (getsockname(fd, &sa.sa, &slen) < 0)
- (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
- sm_errstring(errno));
- else
- {
- hp = hostnamebyanyaddr(&sa);
- if (hp == NULL)
- {
- /* EMPTY */
- /* do nothing */
- }
-# if NETINET
- else if (sa.sa.sa_family == AF_INET)
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "%s/%d", hp, ntohs(sa.sin.sin_port));
-# endif /* NETINET */
-# if NETINET6
- else if (sa.sa.sa_family == AF_INET6)
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "%s/%d", hp, ntohs(sa.sin6.sin6_port));
-# endif /* NETINET6 */
- else
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "%s", hp);
- }
- p += strlen(p);
- (void) sm_snprintf(p, SPACELEFT(buf, p), "->");
- p += strlen(p);
- slen = sizeof(sa);
- if (getpeername(fd, &sa.sa, &slen) < 0)
- (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
- sm_errstring(errno));
- else
- {
- hp = hostnamebyanyaddr(&sa);
- if (hp == NULL)
- {
- /* EMPTY */
- /* do nothing */
- }
-# if NETINET
- else if (sa.sa.sa_family == AF_INET)
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "%s/%d", hp, ntohs(sa.sin.sin_port));
-# endif /* NETINET */
-# if NETINET6
- else if (sa.sa.sa_family == AF_INET6)
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "%s/%d", hp, ntohs(sa.sin6.sin6_port));
-# endif /* NETINET6 */
- else
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "%s", hp);
- }
- break;
-#endif /* S_IFSOCK */
-
- case S_IFCHR:
- (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: ");
- p += strlen(p);
- goto defprint;
-
-#ifdef S_IFBLK
- case S_IFBLK:
- (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: ");
- p += strlen(p);
- goto defprint;
-#endif /* S_IFBLK */
-
-#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
- case S_IFIFO:
- (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: ");
- p += strlen(p);
- goto defprint;
-#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
-
-#ifdef S_IFDIR
- case S_IFDIR:
- (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: ");
- p += strlen(p);
- goto defprint;
-#endif /* S_IFDIR */
-
-#ifdef S_IFLNK
- case S_IFLNK:
- (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: ");
- p += strlen(p);
- goto defprint;
-#endif /* S_IFLNK */
-
- default:
-defprint:
- (void) sm_snprintf(p, SPACELEFT(buf, p),
- "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ",
- major(st.st_dev), minor(st.st_dev),
- (ULONGLONG_T) st.st_ino,
- (int) st.st_nlink, (int) st.st_uid,
- (int) st.st_gid);
- p += strlen(p);
- (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu",
- (ULONGLONG_T) st.st_size);
- break;
- }
-
-printit:
- if (logit)
- sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
- "%.800s", buf);
- else
- sm_dprintf("%s\n", buf);
-}
-
-/*
-** SHORTEN_HOSTNAME -- strip local domain information off of hostname.
-**
-** Parameters:
-** host -- the host to shorten (stripped in place).
-**
-** Returns:
-** place where string was truncated, NULL if not truncated.
-*/
-
-char *
-shorten_hostname(host)
- char host[];
-{
- register char *p;
- char *mydom;
- int i;
- bool canon = false;
-
- /* strip off final dot */
- i = strlen(host);
- p = &host[(i == 0) ? 0 : i - 1];
- if (*p == '.')
- {
- *p = '\0';
- canon = true;
- }
-
- /* see if there is any domain at all -- if not, we are done */
- p = strchr(host, '.');
- if (p == NULL)
- return NULL;
-
- /* yes, we have a domain -- see if it looks like us */
- mydom = macvalue('m', CurEnv);
- if (mydom == NULL)
- mydom = "";
- i = strlen(++p);
- if ((canon ? sm_strcasecmp(p, mydom)
- : sm_strncasecmp(p, mydom, i)) == 0 &&
- (mydom[i] == '.' || mydom[i] == '\0'))
- {
- *--p = '\0';
- return p;
- }
- return NULL;
-}
-
-/*
-** PROG_OPEN -- open a program for reading
-**
-** Parameters:
-** argv -- the argument list.
-** pfd -- pointer to a place to store the file descriptor.
-** e -- the current envelope.
-**
-** Returns:
-** pid of the process -- -1 if it failed.
-*/
-
-pid_t
-prog_open(argv, pfd, e)
- char **argv;
- int *pfd;
- ENVELOPE *e;
-{
- pid_t pid;
- int save_errno;
- int sff;
- int ret;
- int fdv[2];
- char *p, *q;
- char buf[MAXPATHLEN];
- extern int DtableSize;
-
- if (pipe(fdv) < 0)
- {
- syserr("%s: cannot create pipe for stdout", argv[0]);
- return -1;
- }
- pid = fork();
- if (pid < 0)
- {
- syserr("%s: cannot fork", argv[0]);
- (void) close(fdv[0]);
- (void) close(fdv[1]);
- return -1;
- }
- if (pid > 0)
- {
- /* parent */
- (void) close(fdv[1]);
- *pfd = fdv[0];
- return pid;
- }
-
- /* Reset global flags */
- RestartRequest = NULL;
- RestartWorkGroup = false;
- ShutdownRequest = NULL;
- PendingSignal = 0;
- CurrentPid = getpid();
-
- /*
- ** Initialize exception stack and default exception
- ** handler for child process.
- */
-
- sm_exc_newthread(fatal_error);
-
- /* child -- close stdin */
- (void) close(0);
-
- /* stdout goes back to parent */
- (void) close(fdv[0]);
- if (dup2(fdv[1], 1) < 0)
- {
- syserr("%s: cannot dup2 for stdout", argv[0]);
- _exit(EX_OSERR);
- }
- (void) close(fdv[1]);
-
- /* stderr goes to transcript if available */
- if (e->e_xfp != NULL)
- {
- int xfd;
-
- xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL);
- if (xfd >= 0 && dup2(xfd, 2) < 0)
- {
- syserr("%s: cannot dup2 for stderr", argv[0]);
- _exit(EX_OSERR);
- }
- }
-
- /* this process has no right to the queue file */
- if (e->e_lockfp != NULL)
- {
- int fd;
-
- fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
- if (fd >= 0)
- (void) close(fd);
- else
- syserr("%s: lockfp does not have a fd", argv[0]);
- }
-
- /* 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();
- sm_mbdb_terminate();
-#if _FFR_MEMSTAT
- (void) sm_memstat_close();
-#endif /* _FFR_MEMSTAT */
- 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)
- p = ProgMailer->m_execdir;
- else
- p = NULL;
- for (; p != NULL; p = q)
- {
- q = strchr(p, ':');
- if (q != NULL)
- *q = '\0';
- expand(p, buf, sizeof(buf), e);
- if (q != NULL)
- *q++ = ':';
- if (buf[0] != '\0' && chdir(buf) >= 0)
- break;
- }
- if (p == NULL)
- {
- /* backup directories */
- if (chdir("/tmp") < 0)
- (void) chdir("/");
- }
-
- /* Check safety of program to be run */
- sff = SFF_ROOTOK|SFF_EXECOK;
- if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail))
- sff |= SFF_NOGWFILES|SFF_NOWWFILES;
- if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail))
- sff |= SFF_NOPATHCHECK;
- else
- sff |= SFF_SAFEDIRPATH;
- ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL);
- if (ret != 0)
- sm_syslog(LOG_INFO, e->e_id,
- "Warning: prog_open: program %s unsafe: %s",
- argv[0], sm_errstring(ret));
-
- /* arrange for all the files to be closed */
- sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
-
- /* now exec the process */
- (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
-
- /* woops! failed */
- save_errno = errno;
- syserr("%s: cannot exec", argv[0]);
- 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
-**
-** Parameters:
-** line -- the raw text line to search.
-** col -- the column number to fetch.
-** delim -- the delimiter between columns. If null,
-** use white space.
-** buf -- the output buffer.
-** buflen -- the length of buf.
-**
-** Returns:
-** buf if successful.
-** NULL otherwise.
-*/
-
-char *
-get_column(line, col, delim, buf, buflen)
- char line[];
- int col;
- int delim;
- char buf[];
- int buflen;
-{
- char *p;
- char *begin, *end;
- int i;
- char delimbuf[4];
-
- if ((char) delim == '\0')
- (void) sm_strlcpy(delimbuf, "\n\t ", sizeof(delimbuf));
- else
- {
- delimbuf[0] = (char) delim;
- delimbuf[1] = '\0';
- }
-
- p = line;
- if (*p == '\0')
- return NULL; /* line empty */
- if (*p == (char) delim && col == 0)
- return NULL; /* first column empty */
-
- begin = line;
-
- if (col == 0 && (char) delim == '\0')
- {
- while (*begin != '\0' && isascii(*begin) && isspace(*begin))
- begin++;
- }
-
- for (i = 0; i < col; i++)
- {
- if ((begin = strpbrk(begin, delimbuf)) == NULL)
- return NULL; /* no such column */
- begin++;
- if ((char) delim == '\0')
- {
- while (*begin != '\0' && isascii(*begin) && isspace(*begin))
- begin++;
- }
- }
-
- end = strpbrk(begin, delimbuf);
- if (end == NULL)
- i = strlen(begin);
- else
- i = end - begin;
- if (i >= buflen)
- i = buflen - 1;
- (void) sm_strlcpy(buf, begin, i + 1);
- return buf;
-}
-
-/*
-** CLEANSTRCPY -- copy string keeping out bogus characters
-**
-** Parameters:
-** t -- "to" string.
-** f -- "from" string.
-** l -- length of space available in "to" string.
-**
-** Returns:
-** none.
-*/
-
-void
-cleanstrcpy(t, f, l)
- register char *t;
- register char *f;
- int 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')
- {
- if (isascii(*f) &&
- (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
- {
- l--;
- *t++ = *f;
- }
- f++;
- }
- *t = '\0';
-}
-
-/*
-** DENLSTRING -- convert newlines in a string to spaces
-**
-** Parameters:
-** s -- the input string
-** strict -- if set, don't permit continuation lines.
-** logattacks -- if set, log attempted attacks.
-**
-** Returns:
-** A pointer to a version of the string with newlines
-** mapped to spaces. This should be copied.
-*/
-
-char *
-denlstring(s, strict, logattacks)
- char *s;
- bool strict;
- bool logattacks;
-{
- register char *p;
- int l;
- static char *bp = NULL;
- static int bl = 0;
-
- p = s;
- while ((p = strchr(p, '\n')) != NULL)
- if (strict || (*++p != ' ' && *p != '\t'))
- break;
- if (p == NULL)
- return s;
-
- l = strlen(s) + 1;
- if (bl < l)
- {
- /* allocate more space */
- char *nbp = sm_pmalloc_x(l);
-
- if (bp != NULL)
- sm_free(bp);
- bp = nbp;
- bl = l;
- }
- (void) sm_strlcpy(bp, s, l);
- for (p = bp; (p = strchr(p, '\n')) != NULL; )
- *p++ = ' ';
-
- if (logattacks)
- {
- sm_syslog(LOG_NOTICE, CurEnv ? CurEnv->e_id : NULL,
- "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
- RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
- shortenstring(bp, MAXSHORTSTR));
- }
-
- return bp;
-}
-
-/*
-** STRREPLNONPRT -- replace "unprintable" characters in a string with subst
-**
-** Parameters:
-** s -- string to manipulate (in place)
-** subst -- character to use as replacement
-**
-** Returns:
-** true iff string did not contain "unprintable" characters
-*/
-
-bool
-strreplnonprt(s, c)
- char *s;
- int c;
-{
- bool ok;
-
- ok = true;
- if (s == NULL)
- return ok;
- while (*s != '\0')
- {
- if (!(isascii(*s) && isprint(*s)))
- {
- *s = c;
- ok = false;
- }
- ++s;
- }
- return ok;
-}
-
-/*
-** PATH_IS_DIR -- check to see if file exists and is a directory.
-**
-** There are some additional checks for security violations in
-** here. This routine is intended to be used for the host status
-** support.
-**
-** Parameters:
-** pathname -- pathname to check for directory-ness.
-** createflag -- if set, create directory if needed.
-**
-** Returns:
-** true -- if the indicated pathname is a directory
-** false -- otherwise
-*/
-
-bool
-path_is_dir(pathname, createflag)
- char *pathname;
- bool createflag;
-{
- struct stat statbuf;
-
-#if HASLSTAT
- if (lstat(pathname, &statbuf) < 0)
-#else /* HASLSTAT */
- if (stat(pathname, &statbuf) < 0)
-#endif /* HASLSTAT */
- {
- if (errno != ENOENT || !createflag)
- return false;
- if (mkdir(pathname, 0755) < 0)
- return false;
- return true;
- }
- if (!S_ISDIR(statbuf.st_mode))
- {
- errno = ENOTDIR;
- return false;
- }
-
- /* security: don't allow writable directories */
- if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
- {
- errno = EACCES;
- return false;
- }
- return true;
-}
-
-/*
-** PROC_LIST_ADD -- add process id to list of our children
-**
-** Parameters:
-** pid -- pid to add to list.
-** task -- task of pid.
-** type -- type of process.
-** count -- number of processes.
-** other -- other information for this type.
-**
-** Returns:
-** none
-**
-** Side Effects:
-** May increase CurChildren. May grow ProcList.
-*/
-
-typedef struct procs PROCS_T;
-
-struct procs
-{
- pid_t proc_pid;
- char *proc_task;
- int proc_type;
- int proc_count;
- int proc_other;
- SOCKADDR proc_hostaddr;
-};
-
-static PROCS_T *volatile ProcListVec = NULL;
-static int ProcListSize = 0;
-
-void
-proc_list_add(pid, task, type, count, other, hostaddr)
- pid_t pid;
- char *task;
- int type;
- int count;
- int other;
- SOCKADDR *hostaddr;
-{
- int i;
-
- for (i = 0; i < ProcListSize; i++)
- {
- if (ProcListVec[i].proc_pid == NO_PID)
- break;
- }
- if (i >= ProcListSize)
- {
- /* probe the existing vector to avoid growing infinitely */
- proc_list_probe();
-
- /* now scan again */
- for (i = 0; i < ProcListSize; i++)
- {
- if (ProcListVec[i].proc_pid == NO_PID)
- break;
- }
- }
- if (i >= ProcListSize)
- {
- /* grow process list */
- int chldwasblocked;
- PROCS_T *npv;
-
- SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG);
- npv = (PROCS_T *) sm_pmalloc_x((sizeof(*npv)) *
- (ProcListSize + PROC_LIST_SEG));
-
- /* Block SIGCHLD so reapchild() doesn't mess with us */
- chldwasblocked = sm_blocksignal(SIGCHLD);
- if (ProcListSize > 0)
- {
- memmove(npv, ProcListVec,
- ProcListSize * sizeof(PROCS_T));
- sm_free(ProcListVec);
- }
-
- /* XXX just use memset() to initialize this part? */
- for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
- {
- npv[i].proc_pid = NO_PID;
- npv[i].proc_task = NULL;
- npv[i].proc_type = PROC_NONE;
- }
- i = ProcListSize;
- ProcListSize += PROC_LIST_SEG;
- ProcListVec = npv;
- if (chldwasblocked == 0)
- (void) sm_releasesignal(SIGCHLD);
- }
- ProcListVec[i].proc_pid = pid;
- PSTRSET(ProcListVec[i].proc_task, task);
- ProcListVec[i].proc_type = type;
- ProcListVec[i].proc_count = count;
- ProcListVec[i].proc_other = other;
- if (hostaddr != NULL)
- ProcListVec[i].proc_hostaddr = *hostaddr;
- else
- memset(&ProcListVec[i].proc_hostaddr, 0,
- sizeof(ProcListVec[i].proc_hostaddr));
-
- /* if process adding itself, it's not a child */
- if (pid != CurrentPid)
- {
- SM_ASSERT(CurChildren < INT_MAX);
- CurChildren++;
- }
-}
-
-/*
-** PROC_LIST_SET -- set pid task in process list
-**
-** Parameters:
-** pid -- pid to set
-** task -- task of pid
-**
-** Returns:
-** none.
-*/
-
-void
-proc_list_set(pid, task)
- pid_t pid;
- char *task;
-{
- int i;
-
- for (i = 0; i < ProcListSize; i++)
- {
- if (ProcListVec[i].proc_pid == pid)
- {
- PSTRSET(ProcListVec[i].proc_task, task);
- break;
- }
- }
-}
-
-/*
-** PROC_LIST_DROP -- drop pid from process list
-**
-** Parameters:
-** pid -- pid to drop
-** st -- process status
-** other -- storage for proc_other (return).
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** May decrease CurChildren, CurRunners, or
-** set RestartRequest or ShutdownRequest.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-void
-proc_list_drop(pid, st, other)
- pid_t pid;
- int st;
- int *other;
-{
- int i;
- int type = PROC_NONE;
-
- for (i = 0; i < ProcListSize; i++)
- {
- if (ProcListVec[i].proc_pid == pid)
- {
- ProcListVec[i].proc_pid = NO_PID;
- type = ProcListVec[i].proc_type;
- if (other != NULL)
- *other = ProcListVec[i].proc_other;
- if (CurChildren > 0)
- CurChildren--;
- break;
- }
- }
-
-
- if (type == PROC_CONTROL && WIFEXITED(st))
- {
- /* if so, see if we need to restart or shutdown */
- if (WEXITSTATUS(st) == EX_RESTART)
- RestartRequest = "control socket";
- else if (WEXITSTATUS(st) == EX_SHUTDOWN)
- ShutdownRequest = "control socket";
- }
- else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) &&
- ProcListVec[i].proc_other > -1)
- {
- /* restart this persistent runner */
- mark_work_group_restart(ProcListVec[i].proc_other, st);
- }
- else if (type == PROC_QUEUE)
- CurRunners -= ProcListVec[i].proc_count;
-}
-
-/*
-** PROC_LIST_CLEAR -- clear the process list
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Sets CurChildren to zero.
-*/
-
-void
-proc_list_clear()
-{
- int i;
-
- /* start from 1 since 0 is the daemon itself */
- for (i = 1; i < ProcListSize; i++)
- ProcListVec[i].proc_pid = NO_PID;
- CurChildren = 0;
-}
-
-/*
-** PROC_LIST_PROBE -- probe processes in the list to see if they still exist
-**
-** Parameters:
-** none
-**
-** Returns:
-** none
-**
-** Side Effects:
-** May decrease CurChildren.
-*/
-
-void
-proc_list_probe()
-{
- int i, children;
- int chldwasblocked;
- pid_t pid;
-
- children = 0;
- chldwasblocked = sm_blocksignal(SIGCHLD);
-
- /* start from 1 since 0 is the daemon itself */
- for (i = 1; i < ProcListSize; i++)
- {
- pid = ProcListVec[i].proc_pid;
- if (pid == NO_PID || pid == CurrentPid)
- continue;
- if (kill(pid, 0) < 0)
- {
- if (LogLevel > 3)
- sm_syslog(LOG_DEBUG, CurEnv->e_id,
- "proc_list_probe: lost pid %d",
- (int) ProcListVec[i].proc_pid);
- ProcListVec[i].proc_pid = NO_PID;
- SM_FREE_CLR(ProcListVec[i].proc_task);
- CurChildren--;
- }
- else
- {
- ++children;
- }
- }
- if (CurChildren < 0)
- CurChildren = 0;
- if (chldwasblocked == 0)
- (void) sm_releasesignal(SIGCHLD);
- if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid)
- {
- sm_syslog(LOG_ERR, NOQID,
- "proc_list_probe: found %d children, expected %d",
- children, CurChildren);
- }
-}
-
-/*
-** PROC_LIST_DISPLAY -- display the process list
-**
-** Parameters:
-** out -- output file pointer
-** prefix -- string to output in front of each line.
-**
-** Returns:
-** none.
-*/
-
-void
-proc_list_display(out, prefix)
- SM_FILE_T *out;
- char *prefix;
-{
- int i;
-
- for (i = 0; i < ProcListSize; i++)
- {
- if (ProcListVec[i].proc_pid == NO_PID)
- continue;
-
- (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n",
- prefix,
- (int) ProcListVec[i].proc_pid,
- ProcListVec[i].proc_task != NULL ?
- ProcListVec[i].proc_task : "(unknown)",
- (OpMode == MD_SMTP ||
- OpMode == MD_DAEMON ||
- OpMode == MD_ARPAFTP) ? "\r" : "");
- }
-}
-
-/*
-** PROC_LIST_SIGNAL -- send a signal to a type of process in the list
-**
-** Parameters:
-** type -- type of process to signal
-** signal -- the type of signal to send
-**
-** Results:
-** none.
-**
-** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
-** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
-** DOING.
-*/
-
-void
-proc_list_signal(type, signal)
- int type;
- int signal;
-{
- int chldwasblocked;
- int alrmwasblocked;
- int i;
- pid_t mypid = getpid();
-
- /* block these signals so that we may signal cleanly */
- chldwasblocked = sm_blocksignal(SIGCHLD);
- alrmwasblocked = sm_blocksignal(SIGALRM);
-
- /* Find all processes of type and send signal */
- for (i = 0; i < ProcListSize; i++)
- {
- if (ProcListVec[i].proc_pid == NO_PID ||
- ProcListVec[i].proc_pid == mypid)
- continue;
- if (ProcListVec[i].proc_type != type)
- continue;
- (void) kill(ProcListVec[i].proc_pid, signal);
- }
-
- /* restore the signals */
- if (alrmwasblocked == 0)
- (void) sm_releasesignal(SIGALRM);
- if (chldwasblocked == 0)
- (void) sm_releasesignal(SIGCHLD);
-}
-
-/*
-** COUNT_OPEN_CONNECTIONS
-**
-** Parameters:
-** hostaddr - ClientAddress
-**
-** Returns:
-** the number of open connections for this client
-**
-*/
-
-int
-count_open_connections(hostaddr)
- SOCKADDR *hostaddr;
-{
- int i, n;
-
- if (hostaddr == NULL)
- return 0;
-
- /*
- ** Initialize to 1 instead of 0 because this code gets called
- ** before proc_list_add() gets called, so we (the daemon child
- ** for this connection) don't count ourselves.
- */
-
- n = 1;
- for (i = 0; i < ProcListSize; i++)
- {
- if (ProcListVec[i].proc_pid == NO_PID)
- continue;
- if (hostaddr->sa.sa_family !=
- ProcListVec[i].proc_hostaddr.sa.sa_family)
- continue;
-#if NETINET
- if (hostaddr->sa.sa_family == AF_INET &&
- (hostaddr->sin.sin_addr.s_addr ==
- ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr))
- n++;
-#endif /* NETINET */
-#if NETINET6
- if (hostaddr->sa.sa_family == AF_INET6 &&
- IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr),
- &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr)))
- n++;
-#endif /* NETINET6 */
- }
- return n;
-}
diff --git a/contrib/sendmail/src/version.c b/contrib/sendmail/src/version.c
deleted file mode 100644
index ee09fc9..0000000
--- a/contrib/sendmail/src/version.c
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (c) 1998-2007 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.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sm/gen.h>
-
-SM_RCSID("@(#)$Id: version.c,v 8.199 2007/10/31 16:04:12 ca Exp $")
-
-char Version[] = "8.14.2";
OpenPOWER on IntegriCloud