From ea50d71feb02a78d4d5fa746a26ca7ddc6e8cb19 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 28 Aug 2008 02:25:51 +0000 Subject: Stage 1 of sendmail dist tree flattening. contrib/sendmail/contrib prevents doing this in one pass. --- contrib/sendmail/src/Makefile | 19 - contrib/sendmail/src/Makefile.m4 | 103 - contrib/sendmail/src/README | 1850 -------- contrib/sendmail/src/SECURITY | 203 - contrib/sendmail/src/TRACEFLAGS | 102 - contrib/sendmail/src/TUNING | 230 - contrib/sendmail/src/alias.c | 1015 ---- contrib/sendmail/src/aliases | 65 - contrib/sendmail/src/aliases.5 | 131 - contrib/sendmail/src/arpadate.c | 203 - contrib/sendmail/src/bf.c | 864 ---- contrib/sendmail/src/bf.h | 32 - contrib/sendmail/src/collect.c | 1101 ----- contrib/sendmail/src/conf.c | 6376 -------------------------- contrib/sendmail/src/conf.h | 232 - contrib/sendmail/src/control.c | 431 -- contrib/sendmail/src/convtime.c | 202 - contrib/sendmail/src/daemon.c | 4486 ------------------ contrib/sendmail/src/daemon.h | 62 - contrib/sendmail/src/deliver.c | 6258 ------------------------- contrib/sendmail/src/domain.c | 1166 ----- contrib/sendmail/src/envelope.c | 1293 ------ contrib/sendmail/src/err.c | 1158 ----- contrib/sendmail/src/headers.c | 2311 ---------- contrib/sendmail/src/helpfile | 137 - contrib/sendmail/src/macro.c | 730 --- contrib/sendmail/src/mailq.1 | 135 - contrib/sendmail/src/main.c | 4554 ------------------ contrib/sendmail/src/map.c | 7989 -------------------------------- contrib/sendmail/src/map.h | 86 - contrib/sendmail/src/mci.c | 1561 ------- contrib/sendmail/src/milter.c | 4736 ------------------- contrib/sendmail/src/mime.c | 1325 ------ contrib/sendmail/src/newaliases.1 | 50 - contrib/sendmail/src/parseaddr.c | 3360 -------------- contrib/sendmail/src/queue.c | 8895 ------------------------------------ contrib/sendmail/src/ratectrl.c | 534 --- contrib/sendmail/src/readcf.c | 4564 ------------------ contrib/sendmail/src/recipient.c | 2072 --------- contrib/sendmail/src/sasl.c | 287 -- contrib/sendmail/src/savemail.c | 1761 ------- contrib/sendmail/src/sendmail.8 | 748 --- contrib/sendmail/src/sendmail.h | 2656 ----------- contrib/sendmail/src/sfsasl.c | 952 ---- contrib/sendmail/src/sfsasl.h | 25 - contrib/sendmail/src/shmticklib.c | 78 - contrib/sendmail/src/sm_resolve.c | 456 -- contrib/sendmail/src/sm_resolve.h | 142 - contrib/sendmail/src/srvrsmtp.c | 4974 -------------------- contrib/sendmail/src/stab.c | 475 -- contrib/sendmail/src/stats.c | 198 - contrib/sendmail/src/statusd_shm.h | 44 - contrib/sendmail/src/sysexits.c | 167 - contrib/sendmail/src/timers.c | 231 - contrib/sendmail/src/timers.h | 33 - contrib/sendmail/src/tls.c | 1671 ------- contrib/sendmail/src/trace.c | 224 - contrib/sendmail/src/udb.c | 1314 ------ contrib/sendmail/src/usersmtp.c | 3318 -------------- contrib/sendmail/src/util.c | 2853 ------------ contrib/sendmail/src/version.c | 18 - 61 files changed, 93246 deletions(-) delete mode 100644 contrib/sendmail/src/Makefile delete mode 100644 contrib/sendmail/src/Makefile.m4 delete mode 100644 contrib/sendmail/src/README delete mode 100644 contrib/sendmail/src/SECURITY delete mode 100644 contrib/sendmail/src/TRACEFLAGS delete mode 100644 contrib/sendmail/src/TUNING delete mode 100644 contrib/sendmail/src/alias.c delete mode 100644 contrib/sendmail/src/aliases delete mode 100644 contrib/sendmail/src/aliases.5 delete mode 100644 contrib/sendmail/src/arpadate.c delete mode 100644 contrib/sendmail/src/bf.c delete mode 100644 contrib/sendmail/src/bf.h delete mode 100644 contrib/sendmail/src/collect.c delete mode 100644 contrib/sendmail/src/conf.c delete mode 100644 contrib/sendmail/src/conf.h delete mode 100644 contrib/sendmail/src/control.c delete mode 100644 contrib/sendmail/src/convtime.c delete mode 100644 contrib/sendmail/src/daemon.c delete mode 100644 contrib/sendmail/src/daemon.h delete mode 100644 contrib/sendmail/src/deliver.c delete mode 100644 contrib/sendmail/src/domain.c delete mode 100644 contrib/sendmail/src/envelope.c delete mode 100644 contrib/sendmail/src/err.c delete mode 100644 contrib/sendmail/src/headers.c delete mode 100644 contrib/sendmail/src/helpfile delete mode 100644 contrib/sendmail/src/macro.c delete mode 100644 contrib/sendmail/src/mailq.1 delete mode 100644 contrib/sendmail/src/main.c delete mode 100644 contrib/sendmail/src/map.c delete mode 100644 contrib/sendmail/src/map.h delete mode 100644 contrib/sendmail/src/mci.c delete mode 100644 contrib/sendmail/src/milter.c delete mode 100644 contrib/sendmail/src/mime.c delete mode 100644 contrib/sendmail/src/newaliases.1 delete mode 100644 contrib/sendmail/src/parseaddr.c delete mode 100644 contrib/sendmail/src/queue.c delete mode 100644 contrib/sendmail/src/ratectrl.c delete mode 100644 contrib/sendmail/src/readcf.c delete mode 100644 contrib/sendmail/src/recipient.c delete mode 100644 contrib/sendmail/src/sasl.c delete mode 100644 contrib/sendmail/src/savemail.c delete mode 100644 contrib/sendmail/src/sendmail.8 delete mode 100644 contrib/sendmail/src/sendmail.h delete mode 100644 contrib/sendmail/src/sfsasl.c delete mode 100644 contrib/sendmail/src/sfsasl.h delete mode 100644 contrib/sendmail/src/shmticklib.c delete mode 100644 contrib/sendmail/src/sm_resolve.c delete mode 100644 contrib/sendmail/src/sm_resolve.h delete mode 100644 contrib/sendmail/src/srvrsmtp.c delete mode 100644 contrib/sendmail/src/stab.c delete mode 100644 contrib/sendmail/src/stats.c delete mode 100644 contrib/sendmail/src/statusd_shm.h delete mode 100644 contrib/sendmail/src/sysexits.c delete mode 100644 contrib/sendmail/src/timers.c delete mode 100644 contrib/sendmail/src/timers.h delete mode 100644 contrib/sendmail/src/tls.c delete mode 100644 contrib/sendmail/src/trace.c delete mode 100644 contrib/sendmail/src/udb.c delete mode 100644 contrib/sendmail/src/usersmtp.c delete mode 100644 contrib/sendmail/src/util.c delete mode 100644 contrib/sendmail/src/version.c (limited to 'contrib/sendmail/src') 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 ), 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 ), - SFS_VFS (3), SFS_MOUNT (4), SFS_STATFS (5) if you have - the two-argument statfs(2) system call with includes in - , , or 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 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 - - 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 - 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 , 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 - #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" - 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 - 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 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 - #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 - 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 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" - 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 . - 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 , - 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 : - - 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 . - -Darwin/Mac OS X (10.X.X) - The linker errors produced regarding getopt() and its associated - variables can safely be ignored. - - From Mike Zimmerman : - - 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 : - - 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 : - - 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 . 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 - -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 - -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_RCSID("@(#)$Id: bf.c,v 8.62 2006/03/31 18:45:56 ca Exp $") - -#include -#include -#include -#include -#include -#include -#include -#include -#include "sendmail.h" -#include "bf.h" - -#include - -/* 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 - -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 - -SM_RCSID("@(#)$Id: conf.c,v 8.1136 2007/10/10 00:06:45 ca Exp $") - -#include -#include -#if NEWDB -# include "sm/bdb.h" -#endif /* NEWDB */ - -#include -#include "map.h" - -#ifdef DEC -# if NETINET6 -/* for the IPv6 device lookup */ -# define _SOCKADDR_LEN -# include -# endif /* NETINET6 */ -#endif /* DEC */ - -# include -# include - -#include -#if NETINET || NETINET6 -# include -#endif /* NETINET || NETINET6 */ -#if HASULIMIT && defined(HPUX11) -# include -#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 -#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 -#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 -#endif /* _AUX_SOURCE */ - -#if SHARE_V1 -# include -#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 */ -#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 - -/* _PATH_UNIX should be defined in */ -# 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 - -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 - -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 -# include - -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 -# else /* defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0 */ -# include -# 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 - -# 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 - -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 - -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 - -/* 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 -# endif /* SPT_TYPE == SPT_PSTAT */ -# if SPT_TYPE == SPT_PSSTRINGS -# include -# include -# 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 -# include -# endif /* SPT_TYPE == SPT_SYSMIPS */ - -# if SPT_TYPE == SPT_SCO -# include -# include -# include -# include -# 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 -#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 -# if _AIX4 >= 40200 -# include -# endif /* _AIX4 >= 40200 */ -# include -# 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 implementation */ -# define SFS_MOUNT 4 /* use implementation */ -# define SFS_STATFS 5 /* use implementation */ -# define SFS_STATVFS 6 /* use implementation */ - -# ifndef SFS_TYPE -# define SFS_TYPE SFS_NONE -# endif /* ! SFS_TYPE */ - -# if SFS_TYPE == SFS_USTAT -# include -# endif /* SFS_TYPE == SFS_USTAT */ -# if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS -# include -# endif /* SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS */ -# if SFS_TYPE == SFS_VFS -# include -# endif /* SFS_TYPE == SFS_VFS */ -# if SFS_TYPE == SFS_MOUNT -# include -# endif /* SFS_TYPE == SFS_MOUNT */ -# if SFS_TYPE == SFS_STATVFS -# include -# 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 -# endif /* RLIMIT_NEEDS_SYS_TIME_H */ -# include -#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 - -/* 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 -# include - -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 -# endif /* ! SUNOS403 */ -# if (_AIX4 >= 40300) && !defined(_NET_IF_H) -# undef __P -# endif /* (_AIX4 >= 40300) && !defined(_NET_IF_H) */ -# include -#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 - 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 -# include -# include -# ifndef __QNX__ -/* in QNX this grabs bogus LOCK_* manifests */ -# include -# endif /* ! __QNX__ */ -# include -# include -# include -# include -# include -# include -# include - -/* 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 - -#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 - -SM_RCSID("@(#)$Id: control.c,v 8.128 2006/08/15 23:24:56 ca Exp $") - -#include - -/* 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 - -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 -#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 -# 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 -#endif /* STARTTLS */ - -#include - -#if IP_SRCROUTE && NETINET -# include -# include -# if HAS_IN_H -# include -# ifndef IPOPTION -# define IPOPTION ip_opts -# define IP_LIST ip_opts -# define IP_DST ip_dst -# endif /* ! IPOPTION */ -# else /* HAS_IN_H */ -# include -# 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 - -#define DAEMON_C 1 -#include - -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 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 -# 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 -#include - -SM_RCSID("@(#)$Id: deliver.c,v 8.1015 2007/10/17 21:35:30 ca Exp $") - -#if HASSETUSERCONTEXT -# include -#endif /* HASSETUSERCONTEXT */ - -#if NETINET || NETINET6 -# include -#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 ? "" : dsn, - e->e_message == 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 " 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 -#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 - - -/* -** 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 - -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 - -SM_RCSID("@(#)$Id: err.c,v 8.196 2006/11/10 23:14:08 ca Exp $") - -#if LDAPMAP -# include -# include /* 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 -#include - -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 ? "" - : 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
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("\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 ? "" : 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 ? "" - : 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 (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 ". -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 [ ] -help The HELP command gives help info. -helo HELO -helo Introduce yourself. -ehlo EHLO -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: [ ] -mail Specifies the sender. Parameters are ESMTP extensions. -mail See "HELP DSN" for details. -rcpt RCPT To: [ ] -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 -vrfy Verify an address. If you want to see what it aliases -vrfy to, use EXPN instead. -expn EXPN -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: -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: -soml Send or mail. If the user is logged in, send directly, -soml otherwise mail. Not supported in this implementation. -saml SAML FROM: -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 [ | @ | \# ] -etrn Run the queue for the specified , or -etrn all hosts within a given , or a specially-named -etrn (implementation-specific). -dsn MAIL From: [ RET={ FULL | HDRS} ] [ ENVID= ] -dsn RCPT To: [ NOTIFY={NEVER,SUCCESS,FAILURE,DELAY} ] -dsn [ ORCPT= ] -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 - -SM_RCSID("@(#)$Id: macro.c,v 8.107 2007/08/06 22:29:02 ca Exp $") - -#include -#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 -#include -#include -#include - -#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 -#endif /* NETINET || NETINET6 */ - -/* for getcfname() */ -#include - -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
\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 ? "" : 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 ? "" : 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 - -SM_RCSID("@(#)$Id: map.c,v 8.699 2007/10/10 00:06:45 ca Exp $") - -#if LDAPMAP -# include -#endif /* LDAPMAP */ - -#if NDBM -# include -# 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 -# 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 -# 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 : "", - rr->rr_type, - value != NULL ? 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 : "", - rr->rr_type, - value != NULL ? 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 -# include - -# 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 ? "" : 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 - -# 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 -# define _DATUM_DEFINED -# include - -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 -# 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 - -SM_RCSID("@(#)$Id: mci.c,v 8.218 2006/08/15 23:24:57 ca Exp $") - -#if NETINET || NETINET6 -# include -#endif /* NETINET || NETINET6 */ - -#include - -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 - -SM_RCSID("@(#)$Id: milter.c,v 8.269 2007/06/06 17:26:12 ca Exp $") - -#if MILTER -# include -# include -# include - -# include -# include -# include - -# if NETINET || NETINET6 -# include -# if MILTER_NO_NAGLE -# include -# endif /* MILTER_NO_NAGLE */ -# endif /* NETINET || NETINET6 */ - -# include - -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 ? "" : h->h_value); - } - else - { - sm_dprintf("Change%s %s: from %s to %s\n", - h == sysheader ? " (default header)" : "", - field, - h->h_value == 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 ? "" : 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 ? "" : 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 -#include - -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(" "); - 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 . -** -** 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 ? "" : 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 - -SM_RCSID("@(#)$Id: parseaddr.c,v 8.401 2007/09/27 23:33:59 ca Exp $") - -#include -#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 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 ? "" : a->q_paddr, - m->m_mno, m->m_name, - a->q_host == NULL ? "" : a->q_host); - (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, - "\tuser `%s', ruser `%s'\n", - a->q_user, - a->q_ruser == 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 */ - 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 -#include - -SM_RCSID("@(#)$Id: queue.c,v 8.975 2007/06/18 20:08:40 ca Exp $") - -#include - -# 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 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 -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 -#endif /* NETINET || NETINET6 */ - -#include - -#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 -#include - -SM_RCSID("@(#)$Id: readcf.c,v 8.664 2007/07/10 17:01:22 ca Exp $") - -#if NETINET || NETINET6 -# include -#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 - ? "" : m->m_mtatype, - m->m_addrtype == NULL - ? "" : m->m_addrtype, - m->m_diagtype == NULL - ? "" : 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 ? "" : 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 ? "" : 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 - -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; - - /* #@# 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_RCSID("@(#)$Id: sasl.c,v 8.22 2006/08/15 23:24:57 ca Exp $") - -#if SASL -# include -# include -# include - -/* -** 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 - -# 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 - -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 - -#include -#include -#include -#include -#include -#include -#include -# 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef LOG -# include -#endif /* LOG */ - - - -# if NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 -# include -# endif /* NETINET || NETINET6 || NETUNIX || NETISO || NETNS || NETX25 */ -# if NETUNIX -# include -# endif /* NETUNIX */ -# if NETINET || NETINET6 -# include -# 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 -# endif /* NETISO */ -# if NETNS -# include -# endif /* NETNS */ -# if NETX25 -# include -# endif /* NETX25 */ - -# if NAMED_BIND -# include -# ifdef NOERROR -# undef NOERROR /* avoid conflict */ -# endif /* NOERROR */ -# include -# else /* NAMED_BIND */ -# undef SM_SET_H_ERRNO -# define SM_SET_H_ERRNO(err) -# endif /* NAMED_BIND */ - -# if HESIOD -# include -# 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 -# 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 -# include -# else /* SASL == 2 || SASL >= 20000 */ -# include -# include -# 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 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 - -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 */ -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_RCSID("@(#)$Id: sfsasl.c,v 8.115 2006/04/18 21:34:07 ca Exp $") -#include -#include -#include -#include - -/* 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 - -/* 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_RCSID("@(#)$Id: shmticklib.c,v 8.14 2001/09/11 04:05:16 gshapiro Exp $") - -#if _FFR_SHM_STATUS -# include -# include -# include - -# 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 -#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 */ - -# 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 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 -#if MILTER -# include -# include -#endif /* MILTER */ - -SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.967 2007/10/01 16:22:14 ca Exp $") - -#include -#include - -#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 - -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 ? "" : 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 - -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 - -SM_RCSID("@(#)$Id: stats.c,v 8.57 2006/08/15 23:24:58 ca Exp $") - -#include - -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 - -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_RCSID("@(#)$Id: timers.c,v 8.26 2006/08/15 23:24:58 ca Exp $") - -#if _FFR_TIMERS -# include -# include -# include "sendmail.h" -# include /* 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 - -SM_RCSID("@(#)$Id: tls.c,v 8.107 2006/10/12 21:35:11 ca Exp $") - -#if STARTTLS -# include -# include -# include -# ifndef HASURANDOMDEV -# include -# 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 -#include -#include - -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: -** -** ::= | "," -** ::= | "." -** ::= | "-" | -** ::= -** -** 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 -#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 -# 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 - -SM_RCSID("@(#)$Id: usersmtp.c,v 8.470 2007/10/17 21:35:30 ca Exp $") - -#include - - -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 ? "" : r, - (availrealms == NULL || *availrealms == NULL) - ? "" : *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 (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 - -SM_RCSID("@(#)$Id: util.c,v 8.413 2007/09/26 23:29:11 ca Exp $") - -#include -#include -#include - -/* -** 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%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 in line. -** -** Looks for the combination and turns it into the -** UNIX canonical character. It only takes one line, -** i.e., it is assumed that the first 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 -#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_RCSID("@(#)$Id: version.c,v 8.199 2007/10/31 16:04:12 ca Exp $") - -char Version[] = "8.14.2"; -- cgit v1.1