From 329949050501501c130d09efc3aee7c78c6d4f9c Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 3 Aug 1998 05:56:20 +0000 Subject: Import sendmail-8.9.1 (slightly trimmed) onto a fresh branch under src/contrib as per various discussions. I will copy across our changes and then point the Makefiles across once the dust has settled.. --- contrib/sendmail/src/Build | 513 ++++ contrib/sendmail/src/Makefile.m4 | 149 ++ contrib/sendmail/src/README | 1442 +++++++++++ contrib/sendmail/src/TRACEFLAGS | 79 + contrib/sendmail/src/alias.c | 894 +++++++ contrib/sendmail/src/aliases | 53 + contrib/sendmail/src/aliases.5 | 85 + contrib/sendmail/src/arpadate.c | 197 ++ contrib/sendmail/src/cdefs.h | 123 + contrib/sendmail/src/clock.c | 267 ++ contrib/sendmail/src/collect.c | 752 ++++++ contrib/sendmail/src/conf.c | 4798 +++++++++++++++++++++++++++++++++++ contrib/sendmail/src/conf.h | 2440 ++++++++++++++++++ contrib/sendmail/src/convtime.c | 183 ++ contrib/sendmail/src/daemon.c | 2073 +++++++++++++++ contrib/sendmail/src/deliver.c | 3722 +++++++++++++++++++++++++++ contrib/sendmail/src/domain.c | 913 +++++++ contrib/sendmail/src/envelope.c | 938 +++++++ contrib/sendmail/src/err.c | 767 ++++++ contrib/sendmail/src/headers.c | 1570 ++++++++++++ contrib/sendmail/src/ldap_map.h | 71 + contrib/sendmail/src/macro.c | 437 ++++ contrib/sendmail/src/mailq.1 | 67 + contrib/sendmail/src/mailstats.h | 34 + contrib/sendmail/src/main.c | 2701 ++++++++++++++++++++ contrib/sendmail/src/makesendmail | 513 ++++ contrib/sendmail/src/map.c | 5065 +++++++++++++++++++++++++++++++++++++ contrib/sendmail/src/mci.c | 1293 ++++++++++ contrib/sendmail/src/mime.c | 1171 +++++++++ contrib/sendmail/src/newaliases.1 | 47 + contrib/sendmail/src/parseaddr.c | 2547 +++++++++++++++++++ contrib/sendmail/src/pathnames.h | 32 + contrib/sendmail/src/queue.c | 2402 ++++++++++++++++++ contrib/sendmail/src/readcf.c | 2868 +++++++++++++++++++++ contrib/sendmail/src/recipient.c | 1431 +++++++++++ contrib/sendmail/src/safefile.c | 751 ++++++ contrib/sendmail/src/savemail.c | 1487 +++++++++++ contrib/sendmail/src/sendmail.8 | 577 +++++ contrib/sendmail/src/sendmail.h | 1500 +++++++++++ contrib/sendmail/src/sendmail.hf | 119 + contrib/sendmail/src/snprintf.c | 428 ++++ contrib/sendmail/src/srvrsmtp.c | 1532 +++++++++++ contrib/sendmail/src/stab.c | 219 ++ contrib/sendmail/src/stats.c | 135 + contrib/sendmail/src/sysexits.c | 162 ++ contrib/sendmail/src/trace.c | 111 + contrib/sendmail/src/udb.c | 1264 +++++++++ contrib/sendmail/src/useful.h | 58 + contrib/sendmail/src/usersmtp.c | 1166 +++++++++ contrib/sendmail/src/util.c | 2094 +++++++++++++++ contrib/sendmail/src/version.c | 17 + 51 files changed, 54257 insertions(+) create mode 100755 contrib/sendmail/src/Build create mode 100644 contrib/sendmail/src/Makefile.m4 create mode 100644 contrib/sendmail/src/README create mode 100644 contrib/sendmail/src/TRACEFLAGS create mode 100644 contrib/sendmail/src/alias.c create mode 100644 contrib/sendmail/src/aliases create mode 100644 contrib/sendmail/src/aliases.5 create mode 100644 contrib/sendmail/src/arpadate.c create mode 100644 contrib/sendmail/src/cdefs.h create mode 100644 contrib/sendmail/src/clock.c create mode 100644 contrib/sendmail/src/collect.c create mode 100644 contrib/sendmail/src/conf.c create mode 100644 contrib/sendmail/src/conf.h create mode 100644 contrib/sendmail/src/convtime.c create mode 100644 contrib/sendmail/src/daemon.c create mode 100644 contrib/sendmail/src/deliver.c create mode 100644 contrib/sendmail/src/domain.c create mode 100644 contrib/sendmail/src/envelope.c create mode 100644 contrib/sendmail/src/err.c create mode 100644 contrib/sendmail/src/headers.c create mode 100644 contrib/sendmail/src/ldap_map.h create mode 100644 contrib/sendmail/src/macro.c create mode 100644 contrib/sendmail/src/mailq.1 create mode 100644 contrib/sendmail/src/mailstats.h create mode 100644 contrib/sendmail/src/main.c create mode 100755 contrib/sendmail/src/makesendmail create mode 100644 contrib/sendmail/src/map.c create mode 100644 contrib/sendmail/src/mci.c create mode 100644 contrib/sendmail/src/mime.c create mode 100644 contrib/sendmail/src/newaliases.1 create mode 100644 contrib/sendmail/src/parseaddr.c create mode 100644 contrib/sendmail/src/pathnames.h create mode 100644 contrib/sendmail/src/queue.c create mode 100644 contrib/sendmail/src/readcf.c create mode 100644 contrib/sendmail/src/recipient.c create mode 100644 contrib/sendmail/src/safefile.c create mode 100644 contrib/sendmail/src/savemail.c create mode 100644 contrib/sendmail/src/sendmail.8 create mode 100644 contrib/sendmail/src/sendmail.h create mode 100644 contrib/sendmail/src/sendmail.hf create mode 100644 contrib/sendmail/src/snprintf.c create mode 100644 contrib/sendmail/src/srvrsmtp.c create mode 100644 contrib/sendmail/src/stab.c create mode 100644 contrib/sendmail/src/stats.c create mode 100644 contrib/sendmail/src/sysexits.c create mode 100644 contrib/sendmail/src/trace.c create mode 100644 contrib/sendmail/src/udb.c create mode 100644 contrib/sendmail/src/useful.h create mode 100644 contrib/sendmail/src/usersmtp.c create mode 100644 contrib/sendmail/src/util.c create mode 100644 contrib/sendmail/src/version.c (limited to 'contrib/sendmail/src') diff --git a/contrib/sendmail/src/Build b/contrib/sendmail/src/Build new file mode 100755 index 0000000..ab8a49d --- /dev/null +++ b/contrib/sendmail/src/Build @@ -0,0 +1,513 @@ +#!/bin/sh + +# Copyright (c) 1998 Sendmail, Inc. All rights reserved. +# Copyright (c) 1993, 1996-1997 Eric P. Allman. All rights reserved. +# Copyright (c) 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. +# +# +# @(#)Build 8.93 (Berkeley) 6/24/98 +# + +# +# A quick-and-dirty script to compile sendmail and related programs +# in the presence of multiple architectures. To use, just use +# "sh Build". +# + +trap "rm -f $obj/.settings$$; exit" 1 2 3 15 + +cflag="" +mflag="" +sflag="" +makeargs="" +libdirs="" +incdirs="" +libsrch="" +siteconfig="" +EX_USAGE=64 +EX_NOINPUT=66 +EX_UNAVAILABLE=69 + +while [ ! -z "$1" ] +do + case $1 + in + -c) # clean out existing $obj tree + cflag=1 + shift + ;; + + -m) # show Makefile name only + mflag=1 + shift + ;; + + -E*) # environment variables to pass into Build + arg=`echo $1 | sed 's/^-E//'` + if [ -z "$arg" ] + then + shift # move to argument + arg=$1 + fi + if [ -z "$arg" ] + then + echo "Empty -E flag" >&2 + exit $EX_USAGE + else + case $arg + in + *=*) # check format + eval $arg + export `echo $arg | sed 's;=.*;;'` + ;; + *) # bad format + echo "Bad format for -E argument ($arg)" >&2 + exit $EX_USAGE + ;; + esac + shift + fi + ;; + + -L*) # set up LIBDIRS + libdirs="$libdirs $1" + shift + ;; + + -I*) # set up INCDIRS + incdirs="$incdirs $1" + shift + ;; + + -f*) # select site config file + arg=`echo $1 | sed 's/^-f//'` + if [ -z "$arg" ] + then + shift # move to argument + arg=$1 + fi + if [ "$siteconfig" ] + then + echo "Only one -f flag allowed" >&2 + exit $EX_USAGE + else + siteconfig=$arg + if [ -z "$siteconfig" ] + then + echo "Missing argument for -f flag" >&2 + exit $EX_USAGE + elif [ ! -f "$siteconfig" ] + then + echo "${siteconfig}: File not found" + exit $EX_NOINPUT + else + shift # move past argument + fi + fi + ;; + + -S) # skip auto-configure + sflag="-s" + shift + ;; + + *) # pass argument to make + makeargs="$makeargs \"$1\"" + shift + ;; + esac +done + +# +# Do heuristic guesses !ONLY! for machines that do not have uname +# +if [ -d /NextApps -a ! -f /bin/uname -a ! -f /usr/bin/uname ] +then + # probably a NeXT box + arch=`hostinfo | sed -n 's/.*Processor type: \([^ ]*\).*/\1/p'` + os=NeXT + rel=`hostinfo | sed -n 's/.*NeXT Mach \([0-9\.]*\).*/\1/p'` +elif [ -f /usr/sony/bin/machine -a -f /etc/osversion ] +then + # probably a Sony NEWS 4.x + os=NEWS-OS + rel=`awk '{ print $3}' /etc/osversion` + arch=`/usr/sony/bin/machine` +elif [ -d /usr/omron -a -f /bin/luna ] +then + # probably a Omron LUNA + os=LUNA + if [ -f /bin/luna1 ] && /bin/luna1 + then + rel=unios-b + arch=luna1 + elif [ -f /bin/luna2 ] && /bin/luna2 + then + rel=Mach + arch=luna2 + elif [ -f /bin/luna88k ] && /bin/luna88k + then + rel=Mach + arch=luna88k + fi +elif [ -d /usr/apollo -a -d \`node_data ] +then + # probably a Apollo/DOMAIN + os=DomainOS + arch=$ISP + rel=`/usr/apollo/bin/bldt | grep Domain | awk '{ print $4 }' | sed -e 's/,//g'` +fi + +if [ ! "$arch" -a ! "$os" -a ! "$rel" ] +then + arch=`uname -m | sed -e 's/ //g'` + os=`uname -s | sed -e 's/\//-/g' -e 's/ //g'` + rel=`uname -r | sed -e 's/(/-/g' -e 's/)//g'` +fi + +# +# Tweak the values we have already got. PLEASE LIMIT THESE to +# tweaks that are absolutely necessary because your system uname +# routine doesn't return something sufficiently unique. Don't do +# it just because you don't like the name that is returned. You +# can combine the architecture name with the os name to create a +# unique Makefile name. +# + +# tweak machine architecture +case $arch +in + sun4*) arch=sun4;; + + 9000/*) arch=`echo $arch | sed -e 's/9000.//' -e 's/..$/xx/'`;; + + DS/907000) arch=ds90;; + + NILE*) arch=NILE + os=`uname -v`;; +esac + +# tweak operating system type and release +node=`uname -n | sed -e 's/\//-/g' -e 's/ //g'` +if [ "$os" = "$node" -a "$arch" = "i386" -a "$rel" = 3.2 -a "`uname -v`" = 2 ] +then + # old versions of SCO UNIX set uname -s the same as uname -n + os=SCO_SV +fi +if [ "$rel" = 4.0 ] +then + case $arch in + 3[34]??|3[34]??,*) + if [ -d /usr/sadm/sysadm/add-ons/WIN-TCP ] + then + os=NCR.MP-RAS.2.x + elif [ -d /usr/sadm/sysadm/add-ons/inet ] + then + os=NCR.MP-RAS.3.x + fi + ;; + esac +fi + +case $os +in + DYNIX-ptx) os=PTX;; + Paragon*) os=Paragon;; + HP-UX) rel=`echo $rel | sed -e 's/^[^.]*\.0*//'`;; + AIX) rela=$rel + rel=`uname -v` + case $rel in + 2) arch="" + ;; + 4) if [ "$rela" = "3" ] + then + arch=$rela + fi + ;; + esac + rel=$rel.$rela + ;; + BSD-386) os=BSD-OS;; + SCO_SV) os=SCO; rel=`uname -X | sed -n 's/Release = 3.2v//p'`;; + UNIX_System_V) if [ "$arch" = "ds90" ] + then + os="UXPDS" + rel=`uname -v | sed -e 's/\(V.*\)L.*/\1/'` + fi;; + SINIX-?) os=SINIX;; + DomainOS) case $rel in + 10.4*) rel=10.4;; + esac + ;; +esac + +# get "base part" of operating system release +rroot=`echo $rel | sed -e 's/\.[^.]*$//'` +rbase=`echo $rel | sed -e 's/\..*//'` +if [ "$rroot" = "$rbase" ] +then + rroot=$rel +fi + +# heuristic tweaks to clean up names -- PLEASE LIMIT THESE! +if [ "$os" = "unix" ] +then + # might be Altos System V + case $rel + in + 5.3*) os=Altos;; + esac +elif [ -r /unix -a -r /usr/lib/libseq.a -a -r /lib/cpp ] +then + # might be a DYNIX/ptx 2.x system, which has a broken uname + if strings /lib/cpp | grep _SEQUENT_ > /dev/null + then + os=PTX + fi +elif [ -d /usr/nec ] +then + # NEC machine -- what is it running? + if [ "$os" = "UNIX_System_V" ] + then + os=EWS-UX_V + elif [ "$os" = "UNIX_SV" ] + then + os=UX4800 + fi +elif [ "$arch" = "mips" ] +then + case $rel + in + 4_*) + if [ `uname -v` = "UMIPS" ] + then + os=RISCos + fi;; + esac +fi + +# see if there is a "user suffix" specified +if [ "${SENDMAIL_SUFFIX-}x" = "x" ] +then + sfx="" +else + sfx=".${SENDMAIL_SUFFIX}" +fi + +echo "Configuration: os=$os, rel=$rel, rbase=$rbase, rroot=$rroot, arch=$arch, sfx=$sfx" + + +SMROOT=${SMROOT-..} +BUILDTOOLS=${BUILDTOOLS-$SMROOT/BuildTools} +export SMROOT BUILDTOOLS + +# see if we are in a Build-able directory +if [ ! -f Makefile.m4 ]; then + echo "Makefile.m4 not found. Build can only be run from a source directory." + exit $EX_UNAVAILABLE +fi + +# now try to find a reasonable object directory +if [ -r obj.$os.$rel.$arch$sfx ]; then + obj=obj.$os.$rel.$arch$sfx +elif [ -r obj.$os.$rroot.$arch$sfx ]; then + obj=obj.$os.$rroot.$arch$sfx +elif [ -r obj.$os.$rbase.x.$arch$sfx ]; then + obj=obj.$os.$rbase.x.$arch$sfx +elif [ -r obj.$os.$rel$sfx ]; then + obj=obj.$os.$rel$sfx +elif [ -r obj.$os.$rbase.x$sfx ]; then + obj=obj.$os.$rbase.x$sfx +elif [ -r obj.$os.$arch$sfx ]; then + obj=obj.$os.$arch$sfx +elif [ -r obj.$rel.$arch$sfx ]; then + obj=obj.$rel.$arch$sfx +elif [ -r obj.$rbase.x.$arch$sfx ]; then + obj=obj.$rbase.x.$arch$sfx +elif [ -r obj.$os$sfx ]; then + obj=obj.$os$sfx +elif [ -r obj.$arch$sfx ]; then + obj=obj.$arch$sfx +elif [ -r obj.$rel$sfx ]; then + obj=obj.$rel$sfx +elif [ -r obj$sfx ]; then + obj=obj$sfx +fi +if [ -z "$obj" -o "$cflag" ] +then + if [ -n "$obj" ] + then + echo "Clearing out existing $obj tree" + rm -rf $obj + else + # no existing obj directory -- try to create one if Makefile found + obj=obj.$os.$rel.$arch$sfx + fi + if [ -r $BUILDTOOLS/OS/$os.$rel.$arch$sfx ]; then + oscf=$os.$rel.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rel.$arch ]; then + oscf=$os.$rel.$arch + elif [ -r $BUILDTOOLS/OS/$os.$rroot.$arch$sfx ]; then + oscf=$os.$rroot.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rroot.$arch ]; then + oscf=$os.$rroot.$arch + elif [ -r $BUILDTOOLS/OS/$os.$rbase.x.$arch$sfx ]; then + oscf=$os.$rbase.x.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rbase.x.$arch ]; then + oscf=$os.$rbase.x.$arch + elif [ -r $BUILDTOOLS/OS/$os.$rel$sfx ]; then + oscf=$os.$rel$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rel ]; then + oscf=$os.$rel + elif [ -r $BUILDTOOLS/OS/$os.$rroot$sfx ]; then + oscf=$os.$rroot$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rroot ]; then + oscf=$os.$rroot + elif [ -r $BUILDTOOLS/OS/$os.$rbase.x$sfx ]; then + oscf=$os.$rbase.x$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rbase.x ]; then + oscf=$os.$rbase.x + elif [ -r $BUILDTOOLS/OS/$os.$arch$sfx ]; then + oscf=$os.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$os.$arch ]; then + oscf=$os.$arch + elif [ -r $BUILDTOOLS/OS/$rel.$arch$sfx ]; then + oscf=$rel.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$rel.$arch ]; then + oscf=$rel.$arch + elif [ -r $BUILDTOOLS/OS/$rroot.$arch$sfx ]; then + oscf=$rroot.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$rroot.$arch ]; then + oscf=$rroot.$arch + elif [ -r $BUILDTOOLS/OS/$rbase.x.$arch$sfx ]; then + oscf=$rbase.x.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$rbase.x.$arch ]; then + oscf=$rbase.x.$arch + elif [ -r $BUILDTOOLS/OS/$os$sfx ]; then + oscf=$os$sfx + elif [ -r $BUILDTOOLS/OS/$os ]; then + oscf=$os + elif [ -r $BUILDTOOLS/OS/$arch$sfx ]; then + oscf=$arch$sfx + elif [ -r $BUILDTOOLS/OS/$arch ]; then + oscf=$arch + elif [ -r $BUILDTOOLS/OS/$rel$sfx ]; then + oscf=$rel$sfx + elif [ -r $BUILDTOOLS/OS/$rel ]; then + oscf=$rel + elif [ -r $BUILDTOOLS/OS/$rel$sfx ]; then + oscf=$rel$sfx + else + echo "Cannot determine how to support $arch.$os.$rel" >&2 + exit $EX_UNAVAILABLE + fi + M4=`sh $BUILDTOOLS/bin/find_m4.sh` + ret=$? + if [ $ret -ne 0 ] + then + exit $ret + fi + echo "Using M4=$M4" + export M4 + if [ "$mflag" ] + then + echo "Will run in virgin $obj using $BUILDTOOLS/OS/$oscf" + exit 0 + fi + if [ "$ABI" ] + then + echo "Using ABI $ABI" + fi + echo "Creating $obj using $BUILDTOOLS/OS/$oscf" + mkdir $obj + (cd $obj; ln -s ../*.[ch158] .) + if [ -f sendmail.hf ] + then + (cd $obj; ln -s ../sendmail.hf .) + fi + + rm -f $obj/.settings$$ + echo 'divert(-1)' > $obj/.settings$$ + cat $BUILDTOOLS/M4/header.m4 >> $obj/.settings$$ + if [ "$ABI" ] + then + echo "define(\`confABI', \`$ABI')" >> $obj/.settings$$ + fi + cat $BUILDTOOLS/OS/$oscf >> $obj/.settings$$ + + if [ -z "$siteconfig" ] + then + # none specified, use defaults + if [ -f $BUILDTOOLS/Site/site.$oscf$sfx.m4 ] + then + siteconfig=$BUILDTOOLS/Site/site.$oscf$sfx.m4 + elif [ -f $BUILDTOOLS/Site/site.$oscf.m4 ] + then + siteconfig=$BUILDTOOLS/Site/site.$oscf.m4 + fi + if [ -f $BUILDTOOLS/Site/site.config.m4 ] + then + siteconfig="$BUILDTOOLS/Site/site.config.m4 $siteconfig" + fi + fi + if [ ! -z "$siteconfig" ] + then + echo "Including $siteconfig" + cat $siteconfig >> $obj/.settings$$ + fi + if [ "$libdirs" ] + then + echo "define(\`confLIBDIRS', confLIBDIRS \`\`$libdirs'')" >> $obj/.settings$$ + fi + if [ "$incdirs" ] + then + echo "define(\`confINCDIRS', confINCDIRS \`\`$incdirs'')" >> $obj/.settings$$ + fi + echo 'divert(0)dnl' >> $obj/.settings$$ + libdirs=`(cat $obj/.settings$$; echo "_SRIDBIL_= confLIBDIRS" ) | \ + sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' | \ + ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - | \ + grep "^_SRIDBIL_=" | \ + sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' -e "s/^_SRIDBIL_=//"` + libsrch=`(cat $obj/.settings$$; echo "_HCRSBIL_= confLIBSEARCH" ) | \ + sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' | \ + ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - | \ + grep "^_HCRSBIL_=" | \ + sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' -e "s/^_HCRSBIL_=//"` + echo 'divert(-1)' >> $obj/.settings$$ + LIBDIRS="$libdirs" LIBSRCH="$libsrch" SITECONFIG="$siteconfig" sh $BUILDTOOLS/bin/configure.sh $sflag $oscf >> $obj/.settings$$ + echo 'divert(0)dnl' >> $obj/.settings$$ + sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' $obj/.settings$$ | \ + ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - Makefile.m4 | \ + sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' > $obj/Makefile + if [ $? -ne 0 -o ! -s $obj/Makefile ] + then + echo "ERROR: ${M4} failed; You may need a newer version of M4, at least as new as System V or GNU" 1>&2 + rm -rf $obj + exit $EX_UNAVAILABLE + fi + rm -f $obj/.settings$$ + echo "Making dependencies in $obj" + (cd $obj; ${MAKE-make} depend) +fi + +if [ "$mflag" ] +then + makefile=`ls -l $obj/Makefile | sed 's/.* //'` + if [ -z "$makefile" ] + then + echo "ERROR: $obj exists but has no Makefile" >&2 + exit $EX_NOINPUT + fi + echo "Will run in existing $obj using $makefile" + exit 0 +fi + +echo "Making in $obj" +cd $obj +eval exec ${MAKE-make} $makeargs diff --git a/contrib/sendmail/src/Makefile.m4 b/contrib/sendmail/src/Makefile.m4 new file mode 100644 index 0000000..f4229c2 --- /dev/null +++ b/contrib/sendmail/src/Makefile.m4 @@ -0,0 +1,149 @@ +# +# This Makefile is designed to work on any reasonably current version of +# "make" program. +# +# @(#)Makefile.m4 8.23 (Berkeley) 6/16/98 +# + +# C compiler +CC= confCC + +# Shell +SHELL= confSHELL + +# use O=-O (usual) or O=-g (debugging) +O= ifdef(`confOPTIMIZE', `confOPTIMIZE', `-O') + +# location of sendmail source directory +SRCDIR= . + +# define the database mechanisms available for map & alias lookups: +# -DNDBM -- use new DBM +# -DNEWDB -- use new Berkeley DB +# -DNIS -- include NIS support +# The really old (V7) DBM library is no longer supported. +# See README for a description of how these flags interact. +# +MAPDEF= ifdef(`confMAPDEF', `confMAPDEF') + +# environment definitions (e.g., -D_AIX3) +ENVDEF= ifdef(`confENVDEF', `confENVDEF') + +# see also conf.h for additional compilation flags + +# include directories +INCDIRS=confINCDIRS + +# loader options +LDOPTS= ifdef(`confLDOPTS', `confLDOPTS') + +# library directories +LIBDIRS=confLIBDIRS + +# libraries required on your system +# delete -l44bsd if you are not running BIND 4.9.x +LIBS= ifdef(`confLIBS', `confLIBS') + +# location of sendmail binary (usually /usr/sbin or /usr/lib) +BINDIR= ${DESTDIR}ifdef(`confMBINDIR', `confMBINDIR', `/usr/sbin') + +# location of "user" binaries (usually /usr/bin or /usr/ucb) +UBINDIR=${DESTDIR}ifdef(`confUBINDIR', `confUBINDIR', `/usr/bin') + +# location of sendmail.st file (usually /var/log or /usr/lib) +STDIR= ${DESTDIR}ifdef(`confSTDIR', `confSTDIR', `/var/log') + +# location of sendmail.hf file (usually /usr/share/misc or /usr/lib) +HFDIR= ${DESTDIR}ifdef(`confHFDIR', `confHFDIR', `/usr/share/misc') + +# additional .o files needed +OBJADD= ifdef(`confOBJADD', `confOBJADD') ifdef(`confSMOBJADD', `confSMOBJADD') + +undivert(1) + +################### end of user configuration flags ###################### + +BUILDBIN=confBUILDBIN +COPTS= -I. ${INCDIRS} ${MAPDEF} ${ENVDEF} +CFLAGS= $O ${COPTS} + +BEFORE= confBEFORE +OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \ + deliver.o domain.o envelope.o err.o headers.o macro.o main.o \ + map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \ + safefile.o savemail.o snprintf.o srvrsmtp.o stab.o stats.o \ + sysexits.o trace.o udb.o usersmtp.o util.o version.o ${OBJADD} + +LINKS= ifdef(`confLINKS', `confLINKS', + `${UBINDIR}/newaliases \ + ${UBINDIR}/mailq \ + ${UBINDIR}/hoststat \ + ${UBINDIR}/purgestat') + +NROFF= ifdef(`confNROFF', `confNROFF', `groff -Tascii') +MANDOC= ifdef(`confMANDOC', `confMANDOC', `-mandoc') + +INSTALL=ifdef(`confINSTALL', `confINSTALL', `install') +BINOWN= ifdef(`confSBINOWN', `confSBINOWN', `root') +BINGRP= ifdef(`confSBINGRP', `confSBINGRP', `kmem') +BINMODE=ifdef(`confSBINMODE', `confSBINMODE', `4555') + +MANOWN= ifdef(`confMANOWN', `confMANOWN', `bin') +MANGRP= ifdef(`confMANGRP', `confMANGRP', `bin') +MANMODE=ifdef(`confMANMODE', `confMANMODE', `444') + +MANROOT=${DESTDIR}ifdef(`confMANROOT', `confMANROOT', `/usr/share/man/cat') +MAN1= ${MANROOT}ifdef(`confMAN1', `confMAN1', `1') +MAN1EXT=ifdef(`confMAN1EXT', `confMAN1EXT', `1') +MAN1SRC=ifdef(`confMAN1SRC', `confMAN1SRC', `0') +MAN5= ${MANROOT}ifdef(`confMAN5', `confMAN5', `5') +MAN5EXT=ifdef(`confMAN5EXT', `confMAN5EXT', `5') +MAN5SRC=ifdef(`confMAN5SRC', `confMAN5SRC', `0') +MAN8= ${MANROOT}ifdef(`confMAN8', `confMAN8', `8') +MAN8EXT=ifdef(`confMAN8EXT', `confMAN8EXT', `8') +MAN8SRC=ifdef(`confMAN8SRC', `confMAN8SRC', `0') + +ALL= sendmail aliases.${MAN5SRC} mailq.${MAN1SRC} newaliases.${MAN1SRC} sendmail.${MAN8SRC} + +all: ${ALL} + +sendmail: ${BEFORE} ${OBJS} + ${CC} -o sendmail ${LDOPTS} ${LIBDIRS} ${OBJS} ${LIBS} + cp /dev/null sendmail.st + +undivert(3) + +aliases.${MAN5SRC}: aliases.5 + ${NROFF} ${MANDOC} aliases.5 > aliases.${MAN5SRC} + +mailq.${MAN1SRC}: mailq.1 + ${NROFF} ${MANDOC} mailq.1 > mailq.${MAN1SRC} + +newaliases.${MAN1SRC}: newaliases.1 + ${NROFF} ${MANDOC} newaliases.1 > newaliases.${MAN1SRC} + +sendmail.${MAN8SRC}: sendmail.8 + ${NROFF} ${MANDOC} sendmail.8 > sendmail.${MAN8SRC} + +install: install-sendmail install-docs + +install-sendmail: sendmail + ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR} + for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done + ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR} + ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 sendmail.st \ + ${STDIR}/sendmail.st + +install-docs: aliases.${MAN5SRC} mailq.${MAN1SRC} newaliases.${MAN1SRC} sendmail.${MAN8SRC} +ifdef(`confNO_MAN_INSTALL', `dnl', +` ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} sendmail.${MAN8SRC} ${MAN8}/sendmail.${MAN8EXT} + ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} aliases.${MAN5SRC} ${MAN5}/aliases.${MAN5EXT} + ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} mailq.${MAN1SRC} ${MAN1}/mailq.${MAN1EXT} + ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} newaliases.${MAN1SRC} ${MAN1}/newaliases.${MAN1EXT}') + +clean: + rm -f ${OBJS} sendmail aliases.${MAN5SRC} mailq.${MAN1SRC} newaliases.${MAN1SRC} sendmail.${MAN8SRC} + +################ Dependency scripts +include(confBUILDTOOLSDIR/M4/depend/ifdef(`confDEPEND_TYPE', `confDEPEND_TYPE', `generic').m4)dnl +################ End of dependency scripts diff --git a/contrib/sendmail/src/README b/contrib/sendmail/src/README new file mode 100644 index 0000000..a6f7dcf --- /dev/null +++ b/contrib/sendmail/src/README @@ -0,0 +1,1442 @@ +# Copyright (c) 1998 Sendmail, Inc. 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. +# +# +# @(#)README 8.206 (Berkeley) 6/30/98 +# + +This directory contains the source files for sendmail(TM). + +********************* +!! DO NOT USE MAKE !! in this directory to compile sendmail -- +********************* instead, use the "Build" script located in +the src directory. It will build an appropriate Makefile, and +create an appropriate obj.* subdirectory so that multiplatform +support works easily. + + ********************************************************** + ** 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: + + eqn ../doc/op/op.me | pic | ditroff -me + +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 +../BuildTools/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. + +Porting to a new Unix-based system should be a matter of creating +an appropriate configuration file in the BuildTools/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. +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. +LDAPMAP Lightweight Directory Lookup Protocol support. You will + have to install the UMich 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. + +>>> 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 the DBMDEF line in the +Makefile. + +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. + ++---------------+ +| 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 Buildtools/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. +HASFCHMOD Define this to one if you have the fchmod(2) system call. + This improves security. +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. +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)). +USESTRERROR Define this if you have the libc strerror function (which + should be declared in ), and it should be used + instead of sys_errlist. +NEEDGETOPT Define this if you need a reimplementation of getopt(3). + On some systems, getopt does very odd things if called + to scan the arguments twice. This flag will ask sendmail + to compile in a local version of getopt that works + properly. +NEEDSTRTOL Define this if your standard C library does not define + strtol(3). This will compile in a local version. +NEEDVPRINTF Define this if your standard C library does not define + vprintf(3). Note that the resulting fake implementation + is not very elegant and may not even work on some + architectures. +NEEDFSYNC Define this if your standard C library does not define + fsync(2). This will try to simulate the operation using + fcntl(2); if that is not available it does nothing, which + 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_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). +NEED_PERCENTQ Set this if your system doesn't support the printf + format strings %lld or %llu. If this is set, %qd and + %qu are used instead. + + + ++-----------------------+ +| 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. +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 to 30s 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. +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. +SMTP Define this to get the SMTP code. Implied by NETINET + or NETISO. +NAMED_BIND If non-zero, include DNS (name daemon) support, including + MX support. The specs say you must use this if you run + SMTP. You don't have to be running a name server daemon + 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. +QUEUE Define this to get queueing code. Implied by NETINET + or NETISO; required by SMTP. This gives you other good + stuff -- it should be on. +DAEMON Define this to get general network support. Implied by + NETINET or NETISO. Defined by default in conf.h. You + almost certainly want it on. +MATCHGECOS Permit fuzzy matching of user names against the full + name (GECOS) field in the /etc/passwd file. This should + probably be on, since you can disable it from the config + 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 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. + + ++---------------------+ +| 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! + + ++-------------------------------------+ +| OPERATING SYSTEM AND COMPILE QUIRKS | ++-------------------------------------+ + +GCC problems + ***************************************************************** + ** 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. + +GDBM GDBM does not work with sendmail 8.8 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 will use /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. + +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. + +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 + BuildTools/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). + + To the best of my knowledge, Solaris does not have the + gethostbyname problem described above. However, it does + have another one: + + From a correspondent: + + For solaris 2.2, I have + + hosts: files dns + + in /etc/nsswitch.conf and /etc/hosts has to have the fully + qualified host name. I think "files" has to be before "dns" + in /etc/nsswitch.conf during bootup. + + From another correspondent: + + When running sendmail under Solaris, the gethostbyname() + hack in conf.c which should perform proper canonicalization + of host names could fail. Result: the host name is not + canonicalized despite the hack, and you'll have to define $j + and $m in sendmail.cf somewhere. + + The reason could be that /etc/nsswitch.conf is improperly + configured (at least from sendmail's point of view). For + example, the line + + hosts: files nisplus dns + + will make gethostbyname() look in /etc/hosts first, then ask + nisplus, then dns. However, if /etc/hosts does not contain + the full canonicalized hostname, then no amount of + gethostbyname()s will work. + + Solution (or rather, a workaround): Ask nisplus first, then + dns, then local files: + + hosts: nisplus dns [NOTFOUND=return] files + + The Solaris "syslog" function is apparently limited to something + about 90 characters because of a kernel limitation. If you have + source code, you can probably up this number. You can get patches + 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 setuid 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.6 (SunOS 5.6) + If you built sendmail 8.8.1 through 8.8.4 inclusive on a Solaris 2.5 + system, that binary will not run on Solaris 2.6, due to problems with + incompatible snprintf(3s) calls. This problem is fixed in sendmail + 8.8.5. + +Solaris 2.5.1 (SunOS 5.5.1) and 2.6 (SunOS 5.6) + Apparently Solaris 2.5.1 patch 103663-01 installs a new + /usr/include/resolv.h file that defines the __P macro without + 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" + + Sun is aware of the problem (Sun bug ID 4081053) and it will be fixed + in Solaris 2.7. + +Ultrix + By default, the IDENT protocol is turned off on Ultrix. If you + are running Ultrix 4.4 or later, or if you have included patch + CXO-8919 for Ultrix 4.2 or 4.3 to fix the TCP problem, you can turn + IDENT on in the configuration file by setting the "ident" timeout + to 30 seconds. + +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. + +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 + It is important that on IRIX 6.x you give used ABI in command + line of Build, otherwise configuration script does not work + correctly, e.g., + + sh Build -E ABI=-n32 + + If you are using XFS filesystem, avoid using ABI=-32 if possible. + +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 + + (BuildTools/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. + + You may have to use -DNeXT. + +BSDI (BSD/386) 1.0, NetBSD 0.9, FreeBSD 1.0 + The "m4" from BSDI won't handle the config files properly. + I haven't had a chance to test this myself. + + 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 BuildTools/OS/FreeBSD). NetBSD-current may have + it too but it has not been verified. + + The latest version of Berkeley DB uses a different naming + scheme than the version that is supplied with your release. This + means you will be able to use the current version of Berkeley DB + with sendmail as long you use the new db.h when compiling + sendmail and link it against the new libdb.a. You should probably + keep the original db.h in /usr/include and the new db.h in + /usr/local/include. + +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 src and add + oldbind.compat.o to OBJADD in the Makefile. + +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. + ie. although SCO has _res.dnsrch defined, and is kinda BIND 4.8.3, it + does not inititialise it, nor does it understand 'search' in + /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. + +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 + + (BuildTools/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 (ie. 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. + + 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. + + Sendmail assumes that libc has snprintf, which has been true since + libc 4.7.0. If you are running an older version, you will need to + use -DHASSNPRINTF=0 in the Makefile. If may be able to use -lbsd + (which includes snprintf) instead of turning this off on versions + of libc between 4.4.4 and 4.7.0 (snprintf improves security, so + you want to use this if at all possible). + + NOTE ON LINUX & BIND: By default, the Makefile generated for Linux + includes header files in /usr/local/include and libraries in + /usr/local/lib. If you've installed BIND on your system, the header + 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. + +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. + +AIX 2.2.1 + Date: Mon Dec 4 14:14:56 CST 1995 + From: Mark Whetzel + Subject: Porting sendmail 8.7.2 to AIX V2 on the RT. + + This version of sendmail does not support MB, MG, and MR resource + records, which are supported by AIX sendmail. + + AIX V2 on the RT does not have 'paths.h'. Create a null + file in the 'obj' directory to remove this compile error. + + A patch file is needed to get the BSD 'db' library to compile + for AIX/RT. I have sent the necessary updates to the author, + but they may not be immediately available. + [NOTE: Berkeley DB version 2.X runs on AIX/RT.] + + The original AIX/RT resolver libraries are very old, and you + should get the latest BIND to replace it. The 4.8.3 version + has been tested, but 4.9.x is out and should work. + + To make the load average code work correctly requires an + external routine, as the kernel does not maintain system + load averages, similar to AIX V3.1.x. A reverse port of the + older 1.05 'monitor' load average daemon code written by + Jussi Maki that will work on AIX V2 for the RT is available + by E-mail to Mark Whetzel . + That code depends on an external daemon to collect system + load information, and the external routine 'getloadavg', + that will return that information. The 'LA_SUBR' define + will handle this for AIX V2 on the RT. + + Note: You will have to change BuildTools/OS/AIX.2 to correctly + point to the locatons of the updated BIND source tree and + the location of the 'newdb' tree and library location. + You will also have to change BuildTools/OS/AIX.2 to know + about the location of the 'getloadavg' routine if you use + the LA_SUBR define. + + + Manual pages will format correctly if given the mandoc macros + and used with nroff. I have not tried groff. + +RISC/os + RISC/os from MIPS is a merged AT&T/Berkeley system. When you + compile on that platform you will get duplicate definitions + on many files. You can ignore these. + +System V Release 4 Based Systems + There is a single BuildTools OS that is intended for all SVR4-based + systems (built from BuildTools/OS/SVR4). It defines __svr4__, + which is predefined by some compilers. If your compiler already + defines this compile variable, you can delete the definition from + the generated Makefile or create a BuildTools/Site/site.config.m4 + 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 . + +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. + +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' mailmethod. + + The fix is to include -DZMAILER in the compilation, which will + cause it to use "HELO hostname" (which Z-mail apparently requires + as well. :) + +LDAP + LDAP was provided by Booker Bense of + Stanford University. From Booker: + + - The patch attached to this message implements an Ldap map class. + Currently we are using this at stanford to support campus-wide + email addressing. More information can be found at + http://www.stanford.edu/~bbense/Inst.html. + + - Currently we are using the ldap map as follows: + + Kluser ldapx + -h"localhost borax.stanford.edu borate.stanford.edu boron.stanford.edu" + -k"mailacceptinggeneralid=%s" -v maildrop + + and in Rule set S5 + + # Now attempt to lookup in luser (ldap map) + R< $L > $+ $: < $L > $( luser $1 $) + R< $* > $+ @ $+ $: < $3 > $2 Rewrite if forward + + - The map definition supports most of the standard Map args plus most + of the command line options of ldapsearch. The software is currently + limited to only accepting the first entry returned. It expects that + the map defines an ldap filter that returns at most 1 valid entry. + It requires the ldap and lber libraries from the Umich Ldap3.2 + release. + + The software has been in production on Solaris.2.5.1 at Stanford + for over 2 years. + +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 on ftp.win.tue.nl in /pub/security; + grab tcp_wrappers_.tar.gz (where is the highest + numbered version). + + 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. + + ++--------------+ +| MANUAL PAGES | ++--------------+ + +The manual pages have been written against the -mandoc macros +instead of the -man macros. The latest version of groff has them +included. You can also get a copy from FTP.UU.NET in the directory +/systems/unix/bsd-sources/share/tmac. groff is available from +ftp.gnu.org in the /pub/gnu directory. + + ++-----------------+ +| 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: + +Makefile.m4 A template for constructing a makefile based on the + information in the BuildTools 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. +arpadate.c A subroutine which creates ARPANET standard dates. +clock.c Routines to implement real-time oriented functions + in sendmail -- e.g., timeouts. +collect.c The routine that actually reads the mail into a temp + 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. +convtime.c A routine to sanely process times. +daemon.c Routines to implement daemon mode. This version is + specifically for Berkeley 4.1 IPC. +deliver.c Routines to deliver mail. +domain.c Routines that interface with DNS (the Domain Name + System). +err.c Routines to print error messages. +envelope.c Routines to manipulate the envelope structure. +headers.c Routines to process message headers. +macro.c The macro expander. This is used internally to + insert information from the configuration file. +main.c The main routine to sendmail. This file also + contains some miscellaneous routines. +map.c Support for database maps. +mci.c Routines that handle mail connection information caching. +mime.c MIME conversion routines. +parseaddr.c The routines which do address parsing. +queue.c Routines to implement message queueing. +readcf.c The routine that reads the configuration file and + translates it to internal form. +recipient.c Routines that manipulate the recipient list. +safefile.c Routines to do careful checking of file modes and permissions + when opening or creating files. +savemail.c Routines which save the letter on processing errors. +sendmail.h Main header file for sendmail. +snprintf.c Routines to manipulate strings but prevent buffer overflows. +srvrsmtp.c Routines to implement server SMTP. +stab.c Routines to manage the symbol table. +stats.c Routines to collect and post the statistics. +sysexits.c List of error messages associated with error codes + in sysexits.h. +trace.c The trace package. These routines allow setting and + testing of trace flags with a high granularity. +udb.c The user database interface module. +usersmtp.c Routines to implement user SMTP. +util.c Some general purpose routines used by sendmail. +version.c The version number and information about this + version of sendmail. Theoretically, this gets + modified on every change. + +Eric Allman + +(Version 8.206, last update 6/30/98 22:08:36) diff --git a/contrib/sendmail/src/TRACEFLAGS b/contrib/sendmail/src/TRACEFLAGS new file mode 100644 index 0000000..588714d --- /dev/null +++ b/contrib/sendmail/src/TRACEFLAGS @@ -0,0 +1,79 @@ +# @(#)TRACEFLAGS 8.21 (Berkeley) 4/27/98 +0, 1 main.c main skip background fork +0, 4 main.c main canonical name, UUCP node name, a.k.a.s +0, 15 main.c main print configuration +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 +19 srvrsmtp.c smtp +20 parseaddr.c parseaddr +21 parseaddr.c rewrite +22 parseaddr.c prescan +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 +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 writeable +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 +60 map.c +61 conf.c sm_gethostbyname +62 multiple file descriptor checking +80 content length +81 sun remote mode +91 mci.c syslogging of MCI cache information +94 srvrsmtp.c cause commands to fail (for protocol testing) +99 main.c avoid backgrounding (no printed output) diff --git a/contrib/sendmail/src/alias.c b/contrib/sendmail/src/alias.c new file mode 100644 index 0000000..8da3317 --- /dev/null +++ b/contrib/sendmail/src/alias.c @@ -0,0 +1,894 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + */ + +# include "sendmail.h" + +#ifndef lint +static char sccsid[] = "@(#)alias.c 8.92 (Berkeley) 6/5/98"; +#endif /* not lint */ + + +MAP *AliasFileMap = NULL; /* the actual aliases.files map */ +int NAliasFileMaps; /* the number of entries in AliasFileMap */ + /* +** 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 stat = EX_OK; + char obuf[MAXNAME + 7]; + extern char *aliaslookup __P((char *, int *, ENVELOPE *)); + + if (tTd(27, 1)) + printf("alias(%s)\n", a->q_user); + + /* don't realias already aliased names */ + if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) + 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. + */ + + p = aliaslookup(a->q_user, &stat, e); + if (stat == EX_TEMPFAIL || stat == EX_UNAVAILABLE) + { + a->q_flags |= QQUEUEUP; + if (e->e_message == NULL) + e->e_message = newstr("alias database unavailable"); + return; + } + if (p == NULL) + return; + + /* + ** Match on Alias. + ** Deliver to the target list. + */ + + if (tTd(27, 1)) + printf("%s (%s, %s) aliased to %s\n", + a->q_paddr, a->q_host, a->q_user, p); + if (bitset(EF_VRFYONLY, e->e_flags)) + { + a->q_flags |= QVERIFIED; + return; + } + message("aliased to %s", shortenstring(p, MAXSHORTSTR)); + if (LogLevel > 9) + sm_syslog(LOG_INFO, e->e_id, + "alias %.100s => %s", + a->q_paddr, shortenstring(p, MAXSHORTSTR)); + a->q_flags &= ~QSELFREF; + if (tTd(27, 5)) + { + printf("alias: QDONTSEND "); + printaddr(a, FALSE); + } + a->q_flags |= QDONTSEND; + (void) sendtolist(p, a, sendq, aliaslevel + 1, e); + if (bitset(QSELFREF, a->q_flags)) + a->q_flags &= ~QDONTSEND; + + /* + ** Look for owner of alias + */ + + (void) strcpy(obuf, "owner-"); + if (strncmp(a->q_user, "owner-", 6) == 0 || + strlen(a->q_user) > (SIZE_T) sizeof obuf - 7) + (void) strcat(obuf, "owner"); + else + (void) strcat(obuf, a->q_user); + owner = aliaslookup(obuf, &stat, e); + if (owner == NULL) + return; + + /* reflect owner into envelope sender */ + if (strpbrk(owner, ",:/|\"") != NULL) + owner = obuf; + a->q_owner = newstr(owner); + + /* announce delivery to this alias; NORECEIPT bit set later */ + if (e->e_xfp != NULL) + fprintf(e->e_xfp, "Message delivered to mailing list %s\n", + a->q_paddr); + 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. +** e -- the current envelope. +** +** Returns: +** the value of name. +** NULL if unknown. +** +** Side Effects: +** none. +** +** Warnings: +** The return value will be trashed across calls. +*/ + +char * +aliaslookup(name, pstat, e) + char *name; + int *pstat; + ENVELOPE *e; +{ + static MAP *map = NULL; + + if (map == NULL) + { + STAB *s = stab("aliases", ST_MAP, ST_FIND); + + if (s == NULL) + return NULL; + map = &s->s_map; + } + if (!bitset(MF_OPEN, map->map_mflags)) + return NULL; + + /* special case POstMastER -- always use lower case */ + if (strcasecmp(name, "postmaster") == 0) + name = "postmaster"; + + return (*map->map_class->map_lookup)(map, name, NULL, pstat); +} + /* +** 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)) + printf("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) + { + strcpy(buf, "aliases.files sequence"); + AliasFileMap = makemapentry(buf); + if (AliasFileMap == NULL) + { + syserr("setalias: cannot create aliases.files map"); + return; + } + } + (void) snprintf(buf, sizeof buf, "Alias%d", NAliasFileMaps); + s = stab(buf, ST_MAP, ST_ENTER); + map = &s->s_map; + bzero(map, sizeof *map); + map->map_mname = s->s_name; + + p = strpbrk(p, " ,/:"); + if (p != NULL && *p == ':') + { + /* map name */ + *p++ = '\0'; + class = spec; + spec = p; + } + else + { + class = "implicit"; + map->map_mflags = MF_INCLNULL; + } + + /* find end of spec */ + if (p != NULL) + p = strchr(p, ','); + if (p != NULL) + *p++ = '\0'; + + if (tTd(27, 20)) + printf(" 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; + if (map->map_class->map_parse(map, spec)) + { + map->map_mflags |= MF_VALID|MF_ALIAS; + 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; + int isopen; +{ + bool attimeout = FALSE; + time_t mtime; + struct stat stb; + char buf[MAXNAME + 1]; + + if (tTd(27, 3)) + printf("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; + time_t toolong = curtime() + SafeAlias; + unsigned int sleeptime = 2; + + 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)) + printf("aliaswait: sleeping for %d seconds\n", + sleeptime); + + map->map_class->map_close(map); + map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); + 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)) + printf("aliaswait: not rebuildable\n"); + map->map_mflags &= ~MF_ALIASWAIT; + return isopen; + } + if (stat(map->map_file, &stb) < 0) + { + if (tTd(27, 3)) + printf("aliaswait: no source file\n"); + map->map_mflags &= ~MF_ALIASWAIT; + return isopen; + } + mtime = stb.st_mtime; + snprintf(buf, sizeof buf, "%s%s", + map->map_file, ext == NULL ? "" : ext); + if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout) + { + /* database is out of date */ + if (AutoRebuild && stb.st_ino != 0 && + (stb.st_uid == geteuid() || + (geteuid() == 0 && stb.st_uid == TrustedFileUid))) + { + bool oldSuprErrs; + + message("auto-rebuilding alias database %s", buf); + oldSuprErrs = SuprErrs; + SuprErrs = TRUE; + if (isopen) + { + map->map_class->map_close(map); + map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); + } + (void) rebuildaliases(map, TRUE); + isopen = map->map_class->map_open(map, O_RDONLY); + SuprErrs = oldSuprErrs; + } + else + { + if (LogLevel > 3) + sm_syslog(LOG_INFO, NOQID, + "alias database %s out of date", + buf); + message("Warning: alias database %s out of date", buf); + } + } + 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; +{ + FILE *af; + bool nolock = FALSE; + bool success = FALSE; + int sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK; + sigfunc_t oldsigint, oldsigquit; +#ifdef SIGTSTP + sigfunc_t oldsigtstp; +#endif + + if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) + return FALSE; + + if (!bitset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail)) + sff |= SFF_NOWLINK; + if (!bitset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail)) + sff |= SFF_NOGWFILES; + if (!bitset(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)) + printf("Can't open %s: %s\n", + map->map_file, errstring(saveerr)); + if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags)) + message("newaliases: cannot open %s: %s", + map->map_file, errstring(saveerr)); + errno = 0; + return FALSE; + } + nolock = TRUE; + if (tTd(27, 1) || + fstat(fileno(af), &stb) < 0 || + bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode)) + message("warning: cannot lock %s: %s", + map->map_file, errstring(errno)); + } + + /* see if someone else is rebuilding the alias file */ + if (!nolock && + !lockfile(fileno(af), 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(fileno(af), map->map_file, NULL, + LOCK_EX); + } + (void) xfclose(af, "rebuildaliases1", map->map_file); + errno = 0; + return FALSE; + } + + oldsigint = setsignal(SIGINT, SIG_IGN); + oldsigquit = setsignal(SIGQUIT, SIG_IGN); +#ifdef SIGTSTP + oldsigtstp = setsignal(SIGTSTP, SIG_IGN); +#endif + + 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; + readaliases(map, af, !automatic, TRUE); + success = TRUE; + } + else + { + if (tTd(27, 1)) + printf("Can't create database for %s: %s\n", + map->map_file, errstring(errno)); + if (!automatic) + syserr("Cannot create database for alias file %s", + map->map_file); + } + + /* close the file, thus releasing locks */ + xfclose(af, "rebuildaliases2", map->map_file); + + /* add distinguished entries and close the database */ + if (bitset(MF_OPEN, map->map_mflags)) + { + map->map_class->map_close(map); + map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); + } + + /* restore the old signals */ + (void) setsignal(SIGINT, oldsigint); + (void) setsignal(SIGQUIT, oldsigquit); +#ifdef SIGTSTP + (void) setsignal(SIGTSTP, oldsigtstp); +#endif + 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 -- anounce 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; + FILE *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 (fgets(line, sizeof (line), af) != NULL) + { + int lhssize, rhssize; + int c; + + LineNumber++; + p = strchr(line, '\n'); +#if _FFR_BACKSLASH_IN_ALIASES + while (p != NULL && p > line && p[-1] == '\\') + { + p--; + if (fgets(p, SPACELEFT(line, p), af) == NULL) + break; + LineNumber++; + p = strchr(p, '\n'); + } +#endif + if (p != NULL) + *p = '\0'; + else if (!feof(af)) + { + syserr("554 alias line too long"); + + /* flush to end of line */ + while ((c = getc(af)) != 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 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 missing colon"); + continue; + } + if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv) == NULL) + { + syserr("554 %.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[-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) == NULL) + usrerr("553 %s... bad address", p); + p = delimptr; + } + } + else + { + p = nlp; + } + + /* see if there should be a continuation line */ + c = getc(af); + if (!feof(af)) + (void) ungetc(c, af); + if (c != ' ' && c != '\t') + break; + + /* read continuation line */ + if (fgets(p, sizeof line - (p - line), af) == NULL) + break; + LineNumber++; + + /* check for line overflow */ + if (strchr(p, '\n') == NULL && !feof(af)) + { + usrerr("554 alias too long"); + while ((c = fgetc(af)) != EOF && c != '\n') + continue; + skipping = TRUE; + break; + } + } + + if (skipping) + continue; + + if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags)) + { + syserr("554 %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 (strcasecmp(al.q_user, "postmaster") == 0) + makelower(al.q_user); + + lhssize = strlen(al.q_user); + rhssize = strlen(rhs); + map->map_class->map_store(map, al.q_user, rhs); + + if (al.q_paddr != NULL) + free(al.q_paddr); + if (al.q_host != NULL) + free(al.q_host); + if (al.q_user != NULL) + free(al.q_user); + + /* statistics */ + naliases++; + bytes += lhssize + rhssize; + if (rhssize > longest) + longest = rhssize; + } + + CurEnv->e_to = NULL; + FileName = NULL; + if (Verbose || announcestats) + message("%s: %d aliases, longest %d bytes, %d bytes total", + map->map_file, naliases, longest, bytes); + if (LogLevel > 7 && logstats) + sm_syslog(LOG_INFO, NOQID, + "%s: %d aliases, longest %d bytes, %d bytes total", + 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)) + printf("forward(%s)\n", user->q_paddr); + + if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) || + bitset(QBADADDR, user->q_flags)) + return; + if (user->q_home == NULL) + { + syserr("554 forward: no home"); + user->q_home = "/no/such/directory"; + } + + /* good address -- look for .forward file in home */ + define('z', user->q_home, e); + define('u', user->q_user, e); + define('h', user->q_host, e); + if (ForwardPath == NULL) + ForwardPath = newstr("\201z/.forward"); + + got_transient = FALSE; + for (pp = ForwardPath; pp != NULL; pp = ep) + { + int err; + char buf[MAXPATHLEN+1]; + + ep = strchr(pp, ':'); + if (ep != NULL) + *ep = '\0'; + expand(pp, buf, sizeof buf, e); + if (ep != NULL) + *ep++ = ':'; + if (buf[0] == '\0') + continue; + if (tTd(27, 3)) + printf("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)) + printf("forward: transient error on %s\n", buf); + if (LogLevel > 2) + sm_syslog(LOG_ERR, e->e_id, + "forward %s: transient error: %s", + buf, errstring(err)); + } + else + { + switch (err) + { + case ENOENT: + break; + +#if _FFR_FORWARD_SYSERR + case E_SM_NOSLINK: + case E_SM_NOHLINK: + case E_SM_REGONLY: + case E_SM_ISEXEC: + case E_SM_WWDIR: + case E_SM_GWDIR: + case E_SM_WWFILE: + case E_SM_GWFILE: + syserr("forward: %s: %s", buf, errstring(err)); + break; +#endif + + default: + if (LogLevel > (RunAsUid == 0 ? 2 : 10)) + sm_syslog(LOG_WARNING, e->e_id, + "forward %s: %s", buf, + errstring(err)); + if (Verbose) + message("forward: %s: %s", + buf, + 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_flags |= QQUEUEUP; + return; + } +} diff --git a/contrib/sendmail/src/aliases b/contrib/sendmail/src/aliases new file mode 100644 index 0000000..7540eea --- /dev/null +++ b/contrib/sendmail/src/aliases @@ -0,0 +1,53 @@ +# +# @(#)aliases 8.2 (Berkeley) 3/5/94 +# +# Aliases in this file will NOT be expanded in the header from +# Mail, but WILL be visible over networks or from /bin/mail. +# +# >>>>>>>>>> The program "newaliases" must be run after +# >> NOTE >> this file is updated for any changes to +# >>>>>>>>>> show through to sendmail. +# + +# Basic system aliases -- these MUST be present. +MAILER-DAEMON: postmaster +postmaster: root + +# General redirections for pseudo accounts. +bin: root +daemon: root +games: root +ingres: root +nobody: root +system: root +toor: root +uucp: root + +# Well-known aliases. +manager: root +dumper: root +operator: root + +# trap decode to catch security attacks +decode: root + +# OFFICIAL CSRG/BUG ADDRESSES + +# Ftp maintainer. +ftp: ftp-bugs +ftp-bugs: bigbug@cs.berkeley.edu + +# Distribution office. +bsd-dist: bsd-dist@cs.berkeley.edu + +# Fortune maintainer. +fortune: fortune@cs.berkeley.edu + +# Termcap maintainer. +termcap: termcap@cs.berkeley.edu + +# General bug address. +ucb-fixes: bigbug@cs.berkeley.edu +ucb-fixes-request: bigbug@cs.berkeley.edu +bugs: bugs@cs.berkeley.edu +# END OFFICIAL BUG ADDRESSES diff --git a/contrib/sendmail/src/aliases.5 b/contrib/sendmail/src/aliases.5 new file mode 100644 index 0000000..8c73ac3 --- /dev/null +++ b/contrib/sendmail/src/aliases.5 @@ -0,0 +1,85 @@ +.\" Copyright (c) 1998 Sendmail, Inc. 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. +.\" +.\" +.\" @(#)aliases.5 8.8 (Berkeley) 5/19/98 +.\" +.Dd May 19, 1998 +.Dt ALIASES 5 +.Os BSD 4 +.Sh NAME +.Nm aliases +.Nd aliases file for sendmail +.Sh SYNOPSIS +.Nm aliases +.Sh DESCRIPTION +This file describes user +.Tn ID +aliases used by +.Pa /usr/sbin/sendmail . +The file resides in +.Pa /etc +and +is formatted as a series of lines of the form +.Bd -filled -offset indent +name: name_1, name2, name_3, . . . +.Ed +.Pp +The +.Em name +is the name to alias, and the +.Em name_n +are the aliases for that name. +Lines beginning with white space are continuation lines. +Lines beginning with +.Ql # +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 +After aliasing has been done, local and valid recipients who have a +.Dq Pa .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 +.Pa /etc/aliases.db +using the program +.Xr newaliases 1 . +A +.Xr newaliases +command should be executed each time the aliases file is changed for the +change to take effect. +.Sh SEE ALSO +.Xr newaliases 1 , +.Xr dbopen 3 , +.Xr dbm 3 , +.Xr sendmail 8 +.Rs +.%T "SENDMAIL Installation and Operation Guide" +.Re +.Rs +.%T "SENDMAIL An Internetwork Mail Router" +.Re +.Sh BUGS +If you have compiled +.Xr sendmail +with DBM support instead of NEWDB, +you may have encountered problems in +.Xr 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 +.Nm +file format appeared in +.Bx 4.0 . diff --git a/contrib/sendmail/src/arpadate.c b/contrib/sendmail/src/arpadate.c new file mode 100644 index 0000000..7a9576b --- /dev/null +++ b/contrib/sendmail/src/arpadate.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)arpadate.c 8.12 (Berkeley) 5/19/98"; +#endif /* not lint */ + +# include "sendmail.h" + +/* +** 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 whereever (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 + +/* 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. + */ + + (void) time(&t); + 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 +#if TZ_TYPE == TZ_TM_ZONE + tz = lt->tm_zone; +#endif +#if TZ_TYPE == TZ_TZNAME + { + extern char *tzname[]; + + tz = tzname[lt->tm_isdst]; + } +#endif +#if TZ_TYPE == TZ_TIMEZONE + { + extern char *timezone(); + + tz = timezone(off, lt->tm_isdst); + } +#endif + 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/cdefs.h b/contrib/sendmail/src/cdefs.h new file mode 100644 index 0000000..e586cbf --- /dev/null +++ b/contrib/sendmail/src/cdefs.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Berkeley Software Design, Inc. + * + * 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. + * + * @(#)cdefs.h 8.8 (Berkeley) 1/9/95 + */ + +#ifndef _CDEFS_H_ +#define _CDEFS_H_ + +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS }; +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + +/* + * The __CONCAT macro is used to concatenate parts of symbol names, e.g. + * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo. + * The __CONCAT macro is a bit tricky -- make sure you don't put spaces + * in between its arguments. __CONCAT can also concatenate double-quoted + * strings produced by the __STRING macro, but this only works with ANSI C. + */ +#if defined(__STDC__) || defined(__cplusplus) +#define __P(protos) protos /* full-blown ANSI C */ +#define __CONCAT(x,y) x ## y +#define __STRING(x) #x + +#define __const const /* define reserved names to standard */ +#define __signed signed +#define __volatile volatile +#if defined(__cplusplus) +#define __inline inline /* convert to C++ keyword */ +#else +#ifndef __GNUC__ +#define __inline /* delete GCC keyword */ +#endif /* !__GNUC__ */ +#endif /* !__cplusplus */ + +#else /* !(__STDC__ || __cplusplus) */ +#define __P(protos) () /* traditional C preprocessor */ +#define __CONCAT(x,y) x/**/y +#define __STRING(x) "x" + +#ifndef __GNUC__ +#define __const /* delete pseudo-ANSI C keywords */ +#define __inline +#define __signed +#define __volatile +/* + * In non-ANSI C environments, new programs will want ANSI-only C keywords + * deleted from the program and old programs will want them left alone. + * When using a compiler other than gcc, programs using the ANSI C keywords + * const, inline etc. as normal identifiers should define -DNO_ANSI_KEYWORDS. + * When using "gcc -traditional", we assume that this is the intent; if + * __GNUC__ is defined but __STDC__ is not, we leave the new keywords alone. + */ +#ifndef NO_ANSI_KEYWORDS +#define const /* delete ANSI C keywords */ +#define inline +#define signed +#define volatile +#endif +#endif /* !__GNUC__ */ +#endif /* !(__STDC__ || __cplusplus) */ + +/* + * GCC1 and some versions of GCC2 declare dead (non-returning) and + * pure (no side effects) functions using "volatile" and "const"; + * unfortunately, these then cause warnings under "-ansi -pedantic". + * GCC2 uses a new, peculiar __attribute__((attrs)) style. All of + * these work for GNU C++ (modulo a slight glitch in the C++ grammar + * in the distribution version of 2.5.5). + */ +#if !defined(__GNUC__) || __GNUC__ < 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ < 5) +#define __attribute__(x) /* delete __attribute__ if non-gcc or gcc1 */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __dead __volatile +#define __pure __const +#endif +#endif + +/* Delete pseudo-keywords wherever they are not available or needed. */ +#ifndef __dead +#define __dead +#define __pure +#endif + +#endif /* !_CDEFS_H_ */ diff --git a/contrib/sendmail/src/clock.c b/contrib/sendmail/src/clock.c new file mode 100644 index 0000000..e81c972 --- /dev/null +++ b/contrib/sendmail/src/clock.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)clock.c 8.34 (Berkeley) 6/4/98"; +#endif /* not lint */ + +# include "sendmail.h" + +# ifndef sigmask +# define sigmask(s) (1 << ((s) - 1)) +# endif + +/* +** SETEVENT -- set an event to happen at a specific time. +** +** Events are stored in a sorted list for fast processing. +** An event only applies to the process that set it. +** +** Parameters: +** intvl -- intvl until next event occurs. +** func -- function to call on event. +** arg -- argument to func on event. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +EVENT *FreeEventList; /* list of free events */ + +static SIGFUNC_DECL tick __P((int)); + +EVENT * +setevent(intvl, func, arg) + time_t intvl; + void (*func)(); + int arg; +{ + register EVENT **evp; + register EVENT *ev; + auto time_t now; + int wasblocked; + + if (intvl <= 0) + { + syserr("554 setevent: intvl=%ld\n", intvl); + return (NULL); + } + + wasblocked = blocksignal(SIGALRM); + (void) time(&now); + + /* search event queue for correct position */ + for (evp = &EventQueue; (ev = *evp) != NULL; evp = &ev->ev_link) + { + if (ev->ev_time >= now + intvl) + break; + } + + /* insert new event */ + ev = FreeEventList; + if (ev == NULL) + ev = (EVENT *) xalloc(sizeof *ev); + else + FreeEventList = ev->ev_link; + ev->ev_time = now + intvl; + ev->ev_func = func; + ev->ev_arg = arg; + ev->ev_pid = getpid(); + ev->ev_link = *evp; + *evp = ev; + + if (tTd(5, 5)) + printf("setevent: intvl=%ld, for=%ld, func=%lx, arg=%d, ev=%lx\n", + (long) intvl, (long)(now + intvl), (u_long) func, + arg, (u_long) ev); + + setsignal(SIGALRM, tick); + intvl = EventQueue->ev_time - now; + (void) alarm((unsigned) intvl < 1 ? 1 : intvl); + if (wasblocked == 0) + (void) releasesignal(SIGALRM); + return (ev); +} + /* +** CLREVENT -- remove an event from the event queue. +** +** Parameters: +** ev -- pointer to event to remove. +** +** Returns: +** none. +** +** Side Effects: +** arranges for event ev to not happen. +*/ + +void +clrevent(ev) + register EVENT *ev; +{ + register EVENT **evp; + int wasblocked; + + if (tTd(5, 5)) + printf("clrevent: ev=%lx\n", (u_long) ev); + if (ev == NULL) + return; + + /* find the parent event */ + wasblocked = blocksignal(SIGALRM); + for (evp = &EventQueue; *evp != NULL; evp = &(*evp)->ev_link) + { + if (*evp == ev) + break; + } + + /* now remove it */ + if (*evp != NULL) + { + *evp = ev->ev_link; + ev->ev_link = FreeEventList; + FreeEventList = ev; + } + + /* restore clocks and pick up anything spare */ + if (wasblocked == 0) + releasesignal(SIGALRM); + if (EventQueue != NULL) + kill(getpid(), SIGALRM); +} + /* +** TICK -- take a clock tick +** +** Called by the alarm clock. This routine runs events as needed. +** Always called as a signal handler, so we assume that SIGALRM +** has been blocked. +** +** Parameters: +** One that is ignored; for compatibility with signal handlers. +** +** Returns: +** none. +** +** Side Effects: +** calls the next function in EventQueue. +*/ + +/* ARGSUSED */ +static SIGFUNC_DECL +tick(sig) + int sig; +{ + register time_t now; + register EVENT *ev; + int mypid = getpid(); + int olderrno = errno; + + (void) alarm(0); + now = curtime(); + + if (tTd(5, 4)) + printf("tick: now=%ld\n", (long) now); + + /* reset signal in case System V semantics */ + (void) setsignal(SIGALRM, tick); + while ((ev = EventQueue) != NULL && + (ev->ev_time <= now || ev->ev_pid != mypid)) + { + void (*f)(); + int arg; + int pid; + + /* process the event on the top of the queue */ + ev = EventQueue; + EventQueue = EventQueue->ev_link; + if (tTd(5, 6)) + printf("tick: ev=%lx, func=%lx, arg=%d, pid=%d\n", + (u_long) ev, (u_long) ev->ev_func, + ev->ev_arg, ev->ev_pid); + + /* we must be careful in here because ev_func may not return */ + f = ev->ev_func; + arg = ev->ev_arg; + pid = ev->ev_pid; + ev->ev_link = FreeEventList; + FreeEventList = ev; + if (pid != getpid()) + continue; + if (EventQueue != NULL) + { + if (EventQueue->ev_time > now) + (void) alarm((unsigned) (EventQueue->ev_time - now)); + else + (void) alarm(3); + } + + /* call ev_func */ + errno = olderrno; + (*f)(arg); + (void) alarm(0); + now = curtime(); + } + if (EventQueue != NULL) + (void) alarm((unsigned) (EventQueue->ev_time - now)); + errno = olderrno; + return SIGFUNC_RETURN; +} + /* +** SLEEP -- a version of sleep that works with this stuff +** +** Because sleep uses the alarm facility, I must reimplement +** it here. +** +** Parameters: +** intvl -- time to sleep. +** +** Returns: +** none. +** +** Side Effects: +** waits for intvl time. However, other events can +** be run during that interval. +*/ + +static bool SleepDone; +static void endsleep __P((void)); + +#ifndef SLEEP_T +# define SLEEP_T unsigned int +#endif + +SLEEP_T +sleep(intvl) + unsigned int intvl; +{ + int was_held; + + if (intvl == 0) + return (SLEEP_T) 0; + SleepDone = FALSE; + (void) setevent((time_t) intvl, endsleep, 0); + was_held = releasesignal(SIGALRM); + while (!SleepDone) + pause(); + if (was_held > 0) + blocksignal(SIGALRM); + return (SLEEP_T) 0; +} + +static void +endsleep() +{ + SleepDone = TRUE; +} diff --git a/contrib/sendmail/src/collect.c b/contrib/sendmail/src/collect.c new file mode 100644 index 0000000..190e699 --- /dev/null +++ b/contrib/sendmail/src/collect.c @@ -0,0 +1,752 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)collect.c 8.89 (Berkeley) 6/4/98"; +#endif /* not lint */ + +# include +# include "sendmail.h" + +/* +** 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. +** +** Returns: +** none. +** +** Side Effects: +** Temp file is created and filled. +** The from person may be set. +*/ + +static jmp_buf CtxCollectTimeout; +static void collecttimeout __P((time_t)); +static bool CollectProgress; +static EVENT *CollectTimeout; + +/* 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 */ + +void +collect(fp, smtpmode, hdrp, e) + FILE *fp; + bool smtpmode; + HDR **hdrp; + register ENVELOPE *e; +{ + register FILE *volatile tf; + volatile bool ignrdot = smtpmode ? FALSE : IgnrDot; + volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0; + register char *volatile bp; + volatile int c = EOF; + volatile bool inputerr = FALSE; + bool headeronly; + char *volatile buf; + volatile int buflen; + volatile int istate; + volatile int mstate; + u_char *volatile pbp; + u_char peekbuf[8]; + char dfname[MAXQFNAME]; + char bufbuf[MAXLINE]; + extern bool isheader __P((char *)); + extern void eatheader __P((ENVELOPE *, bool)); + extern void tferror __P((FILE *volatile, ENVELOPE *)); + + headeronly = hdrp != NULL; + + /* + ** Create the temp file name and create the file. + */ + + if (!headeronly) + { + int tfd; + struct stat stbuf; + + strcpy(dfname, queuename(e, 'd')); + tfd = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode, SFF_ANYFILE); + if (tfd < 0 || (tf = fdopen(tfd, "w")) == NULL) + { + syserr("Cannot create %s", dfname); + e->e_flags |= EF_NO_BODY_RETN; + finis(); + } + if (fstat(fileno(tf), &stbuf) < 0) + e->e_dfino = -1; + else + { + e->e_dfdev = stbuf.st_dev; + e->e_dfino = stbuf.st_ino; + } + HasEightBits = FALSE; + e->e_msgsize = 0; + e->e_flags |= EF_HAS_DF; + } + + /* + ** Tell ARPANET to go ahead. + */ + + if (smtpmode) + message("354 Enter mail, end with \".\" on a line by itself"); + + if (tTd(30, 2)) + printf("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). + */ + + buf = bp = bufbuf; + buflen = sizeof bufbuf; + pbp = peekbuf; + istate = IS_BOL; + mstate = SaveFrom ? MS_HEADER : MS_UFROM; + CollectProgress = FALSE; + + if (dbto != 0) + { + /* handle possible input timeout */ + if (setjmp(CtxCollectTimeout) != 0) + { + if (LogLevel > 2) + sm_syslog(LOG_NOTICE, e->e_id, + "timeout waiting for input from %s during message collect", + CurHostName ? CurHostName : ""); + errno = 0; + usrerr("451 timeout waiting for input during message collect"); + goto readerr; + } + CollectTimeout = setevent(dbto, collecttimeout, dbto); + } + + for (;;) + { + extern int chompheader __P((char *, bool, HDR **, ENVELOPE *)); + + if (tTd(30, 35)) + printf("top, istate=%d, mstate=%d\n", istate, mstate); + for (;;) + { + if (pbp > peekbuf) + c = *--pbp; + else + { + while (!feof(fp) && !ferror(fp)) + { + errno = 0; + c = getc(fp); + if (errno != EINTR) + break; + clearerr(fp); + } + CollectProgress = TRUE; + if (TrafficLogFile != NULL && !headeronly) + { + if (istate == IS_BOL) + fprintf(TrafficLogFile, "%05d <<< ", + (int) getpid()); + if (c == EOF) + fprintf(TrafficLogFile, "[EOF]\n"); + else + putc(c, TrafficLogFile); + } + if (c == EOF) + goto readerr; + if (SevenBitInput) + c &= 0x7f; + else + HasEightBits |= bitset(0x80, c); + } + if (tTd(30, 94)) + printf("istate=%d, c=%c (0x%x)\n", + istate, 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 (c != '.' || + (OpMode != MD_SMTP && + OpMode != MD_DAEMON && + OpMode != MD_ARPAFTP)) + { + *pbp++ = c; + c = '.'; + } + break; + + case IS_DOTCR: + if (c == '\n' && !ignrdot) + goto readerr; + else + { + /* push back the ".\rx" */ + *pbp++ = c; + *pbp++ = '\r'; + c = '.'; + } + break; + + case IS_CR: + if (c == '\n') + istate = IS_BOL; + else + { + ungetc(c, fp); + 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) + e->e_msgsize++; + if (mstate == MS_BODY) + { + /* just put the character out */ + if (MaxMessageSize <= 0 || + e->e_msgsize <= MaxMessageSize) + putc(c, tf); + continue; + } + + /* header -- buffer up */ + if (bp >= &buf[buflen - 2]) + { + char *obuf; + + if (mstate != MS_HEADER) + break; + + /* out of space for header */ + obuf = buf; + if (buflen < MEMCHUNKSIZE) + buflen *= 2; + else + buflen += MEMCHUNKSIZE; + buf = xalloc(buflen); + bcopy(obuf, buf, bp - obuf); + bp = &buf[bp - obuf]; + if (obuf != bufbuf) + free(obuf); + } + if (c >= 0200 && c <= 0237) + { +#if 0 /* causes complaints -- figure out something for 8.9 */ + usrerr("Illegal character 0x%x in header", c); +#endif + } + else if (c != '\0') + *bp++ = c; + if (istate == IS_BOL) + break; + } + *bp = '\0'; + +nextstate: + if (tTd(30, 35)) + printf("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) + { + extern void eatfrom __P((char *volatile, ENVELOPE *)); + + bp = buf; + eatfrom(buf, e); + continue; + } +#endif + /* fall through */ + + case MS_HEADER: + if (!isheader(buf)) + { + mstate = MS_BODY; + goto nextstate; + } + + /* check for possible continuation line */ + do + { + clearerr(fp); + errno = 0; + c = getc(fp); + } while (errno == EINTR); + if (c != EOF) + ungetc(c, fp); + if (c == ' ' || c == '\t') + { + /* yep -- defer this */ + continue; + } + + /* trim off trailing CRLF or NL */ + if (*--bp != '\n' || *--bp != '\r') + bp++; + *bp = '\0'; + if (bitset(H_EOH, chompheader(buf, FALSE, hdrp, e))) + { + mstate = MS_BODY; + goto nextstate; + } + break; + + case MS_BODY: + if (tTd(30, 1)) + printf("EOH\n"); + if (headeronly) + goto readerr; + 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 (MaxMessageSize <= 0 || + e->e_msgsize <= MaxMessageSize) + { + while (*bp != '\0') + putc(*bp++, tf); + } + break; + } + bp = buf; + } + +readerr: + if ((feof(fp) && smtpmode) || ferror(fp)) + { + const char *errmsg = errstring(errno); + + if (tTd(30, 1)) + printf("collect: premature EOM: %s\n", errmsg); + if (LogLevel >= 2) + sm_syslog(LOG_WARNING, e->e_id, + "collect: premature EOM: %s", errmsg); + inputerr = TRUE; + } + + /* reset global timer */ + clrevent(CollectTimeout); + + if (headeronly) + return; + + if (tf != NULL && + (fflush(tf) != 0 || ferror(tf) || + (SuperSafe && fsync(fileno(tf)) < 0) || + fclose(tf) < 0)) + { + tferror(tf, e); + flush_errors(TRUE); + finis(); + } + + /* An EOF when running SMTP is an error */ + if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) + { + char *host; + char *problem; + + host = RealHostName; + if (host == NULL) + host = "localhost"; + + if (feof(fp)) + problem = "unexpected close"; + else if (ferror(fp)) + problem = "I/O error"; + else + problem = "read timeout"; + if (LogLevel > 0 && feof(fp)) + sm_syslog(LOG_NOTICE, e->e_id, + "collect: %s on connection from %.100s, sender=%s: %s", + problem, host, + shortenstring(e->e_from.q_paddr, MAXSHORTSTR), + errstring(errno)); + if (feof(fp)) + usrerr("451 collect: %s on connection from %s, from=%s", + problem, host, + shortenstring(e->e_from.q_paddr, MAXSHORTSTR)); + else + syserr("451 collect: %s on connection from %s, from=%s", + problem, host, + shortenstring(e->e_from.q_paddr, MAXSHORTSTR)); + + /* don't return an error indication */ + e->e_to = NULL; + e->e_flags &= ~EF_FATALERRS; + e->e_flags |= EF_CLRQUEUE; + + /* and don't try to deliver the partial message either */ + if (InChild) + ExitStat = EX_QUIT; + finis(); + } + + /* + ** Find out some information from the headers. + ** Examples are who is the from person & the date. + */ + + eatheader(e, TRUE); + + if (GrabTo && e->e_sendqueue == NULL) + usrerr("No recipient addresses found in header"); + + /* collect statistics */ + if (OpMode != MD_VERIFY) + markstats(e, (ADDRESS *) NULL, FALSE); + +#if _FFR_DSN_RRT_OPTION + /* + ** 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; + } +#endif + + /* + ** Add an Apparently-To: line if we have no recipient lines. + */ + + if (hvalue("to", e->e_header) != NULL || + hvalue("cc", e->e_header) != NULL || + hvalue("apparently-to", e->e_header) != NULL) + { + /* have a valid recipient header -- delete Bcc: headers */ + e->e_flags |= EF_DELETE_BCC; + } + else if (hvalue("bcc", e->e_header) == NULL) + { + /* no valid recipient headers */ + register ADDRESS *q; + char *hdr = NULL; + extern void addheader __P((char *, char *, HDR **)); + + /* create an Apparently-To: field */ + /* that or reject the message.... */ + switch (NoRecipientAction) + { + case NRA_ADD_APPARENTLY_TO: + hdr = "Apparently-To"; + break; + + case NRA_ADD_TO: + hdr = "To"; + break; + + case NRA_ADD_BCC: + addheader("Bcc", " ", &e->e_header); + break; + + case NRA_ADD_TO_UNDISCLOSED: + addheader("To", "undisclosed-recipients:;", &e->e_header); + break; + } + + if (hdr != NULL) + { + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (q->q_alias != NULL) + continue; + if (tTd(30, 3)) + printf("Adding %s: %s\n", + hdr, q->q_paddr); + addheader(hdr, q->q_paddr, &e->e_header); + } + } + } + + /* check for message too large */ + if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) + { + e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE; + e->e_status = "5.2.3"; + usrerr("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"; + usrerr("554 Eight bit data not allowed"); + } + } + else + { + /* if it claimed to be 8 bits, well, it lied.... */ + if (e->e_bodytype != NULL && + strcasecmp(e->e_bodytype, "8BITMIME") == 0) + e->e_bodytype = "7BIT"; + } + + if ((e->e_dfp = fopen(dfname, "r")) == NULL) + { + /* we haven't acked receipt yet, so just chuck this */ + syserr("Cannot reopen %s", dfname); + finis(); + } +} + + +static void +collecttimeout(timeout) + time_t timeout; +{ + /* if no progress was made, die now */ + if (!CollectProgress) + longjmp(CtxCollectTimeout, 1); + + /* otherwise reset the timeout */ + CollectTimeout = setevent(timeout, collecttimeout, timeout); + CollectProgress = FALSE; +} + /* +** TFERROR -- signal error on writing the temporary file. +** +** Parameters: +** tf -- the file pointer for the temporary file. +** e -- the current envelope. +** +** Returns: +** none. +** +** Side Effects: +** Gives an error message. +** Arranges for following output to go elsewhere. +*/ + +void +tferror(tf, e) + FILE *volatile tf; + register ENVELOPE *e; +{ + setstat(EX_IOERR); + if (errno == ENOSPC) + { +#if STAT64 > 0 + struct stat64 st; +#else + struct stat st; +#endif + long avail; + long bsize; + extern long freediskspace __P((char *, long *)); + + e->e_flags |= EF_NO_BODY_RETN; + + if ( +#if STAT64 > 0 + fstat64(fileno(tf), &st) +#else + fstat(fileno(tf), &st) +#endif + < 0) + st.st_size = 0; + (void) freopen(queuename(e, 'd'), "w", tf); + if (st.st_size <= 0) + fprintf(tf, "\n*** Mail could not be accepted"); + else if (sizeof st.st_size > sizeof (long)) + fprintf(tf, "\n*** Mail of at least %s bytes could not be accepted\n", + quad_to_string(st.st_size)); + else + fprintf(tf, "\n*** Mail of at least %lu bytes could not be accepted\n", + (unsigned long) st.st_size); + fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", + MyHostName); + avail = freediskspace(QueueDir, &bsize); + if (avail > 0) + { + if (bsize > 1024) + avail *= bsize / 1024; + else if (bsize < 1024) + avail /= 1024 / bsize; + fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", + avail); + } + e->e_status = "4.3.1"; + usrerr("452 Out of disk space for temp file"); + } + else + syserr("collect: Cannot write tf%s", e->e_id); + if (freopen("/dev/null", "w", tf) == NULL) + sm_syslog(LOG_ERR, e->e_id, + "tferror: freopen(\"/dev/null\") failed: %s", + 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. +** +** Returns: +** none. +** +** Side Effects: +** extracts what information it can from the header, +** such as the date. +*/ + +# ifndef NOTUNIX + +char *DowList[] = +{ + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL +}; + +char *MonthList[] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + NULL +}; + +void +eatfrom(fm, e) + char *volatile fm; + register ENVELOPE *e; +{ + register char *p; + register char **dt; + + if (tTd(30, 2)) + printf("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 (!(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; + + /* we have found a date */ + q = xalloc(25); + (void) strncpy(q, p, 25); + q[24] = '\0'; + q = arpadate(q); + define('a', newstr(q), e); + } +} + +# endif /* NOTUNIX */ diff --git a/contrib/sendmail/src/conf.c b/contrib/sendmail/src/conf.c new file mode 100644 index 0000000..838cd17 --- /dev/null +++ b/contrib/sendmail/src/conf.c @@ -0,0 +1,4798 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)conf.c 8.431 (Berkeley) 6/25/98"; +#endif /* not lint */ + +# include "sendmail.h" +# include "pathnames.h" +# include +# include +# include + +/* +** 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 }, + { "resent-from", H_FROM|H_RESENT }, + { "resent-reply-to", H_FROM|H_RESENT }, + { "sender", H_FROM }, + { "from", H_FROM }, + { "reply-to", H_FROM }, + { "errors-to", H_FROM|H_ERRORSTO }, + { "full-name", H_ACHECK }, + { "return-receipt-to", H_RECEIPTTO }, + + /* destination fields */ + { "to", H_RCPT }, + { "resent-to", H_RCPT|H_RESENT }, + { "cc", H_RCPT }, + { "resent-cc", H_RCPT|H_RESENT }, + { "bcc", H_RCPT|H_BCC }, + { "resent-bcc", H_RCPT|H_BCC|H_RESENT }, + { "apparently-to", H_RCPT }, + + /* message identification and control */ + { "message-id", 0 }, + { "resent-message-id", H_RESENT }, + { "message", H_EOH }, + { "text", H_EOH }, + + /* date fields */ + { "date", 0 }, + { "resent-date", H_RESENT }, + + /* trace fields */ + { "received", H_TRACE|H_FORCE }, + { "x400-received", H_TRACE|H_FORCE }, + { "via", H_TRACE|H_FORCE }, + { "mail-from", H_TRACE|H_FORCE }, + + /* miscellaneous fields */ + { "comments", H_FORCE|H_ENCODABLE }, + { "return-path", H_FORCE|H_ACHECK }, + { "content-transfer-encoding", H_CTE }, + { "content-type", H_CTYPE }, + { "content-length", H_ACHECK }, + { "subject", H_ENCODABLE }, + + { NULL, 0 } +}; + + + +/* +** Privacy values +*/ + +struct prival PrivacyValues[] = +{ + { "public", PRIV_PUBLIC }, + { "needmailhelo", PRIV_NEEDMAILHELO }, + { "needexpnhelo", PRIV_NEEDEXPNHELO }, + { "needvrfyhelo", PRIV_NEEDVRFYHELO }, + { "noexpn", PRIV_NOEXPN }, + { "novrfy", PRIV_NOVRFY }, + { "restrictmailq", PRIV_RESTRICTMAILQ }, + { "restrictqrun", PRIV_RESTRICTQRUN }, + { "noetrn", PRIV_NOETRN }, + { "noverb", PRIV_NOVERB }, + { "authwarnings", PRIV_AUTHWARNINGS }, + { "noreceipts", PRIV_NORECEIPTS }, + { "goaway", PRIV_GOAWAY }, + { 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 }, + { NULL, 0 } +}; + + +/* +** Miscellaneous stuff. +*/ + +int DtableSize = 50; /* max open files; reset in 4.2bsd */ + /* +** SETDEFAULTS -- set default values +** +** Because of the way freezing is done, these must be initialized +** using direct code. +** +** 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 _PATH_VARTMP +# define _PATH_VARTMP "/usr/tmp/" +#endif + +#ifndef MAXRULERECURSION +# define MAXRULERECURSION 50 /* max ruleset recursion depth */ +#endif + +void +setdefaults(e) + register ENVELOPE *e; +{ + int i; + struct passwd *pw; + char buf[MAXNAME]; + extern void inittimeouts __P((char *)); + extern void setdefuser __P((void)); + extern void setupmaps __P((void)); + extern void setupmailers __P((void)); + extern void setupheaders __P((void)); + + SpaceSub = ' '; /* option B */ + QueueLA = 8; /* option x */ + RefuseLA = 12; /* option X */ + WkRecipFact = 30000L; /* option y */ + WkClassFact = 1800L; /* option z */ + WkTimeFact = 90000L; /* option Z */ + QueueFactor = WkRecipFact * 20; /* option q */ + FileMode = (RealUid != geteuid()) ? 0644 : 0600; + /* option F */ + + if (((pw = getpwnam("mailnull")) != NULL && pw->pw_uid != 0) || + ((pw = getpwnam("sendmail")) != NULL && pw->pw_uid != 0) || + ((pw = 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(); + } + TrustedFileUid = 0; + if (tTd(37, 4)) + printf("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 */ + e->e_sendmode = SM_FORK; /* option d */ + e->e_errormode = EM_PRINT; /* option e */ + SevenBitInput = FALSE; /* option 7 */ + MaxMciCache = 1; /* option k */ + MciCacheTimeout = 5 MINUTES; /* option K */ + LogLevel = 9; /* option L */ + inittimeouts(NULL); /* option r */ + PrivacyFlags = PRIV_PUBLIC; /* option p */ + DontBlameSendmail = DBS_SAFE; /* DontBlameSendmail option */ +#if MIME8TO7 + MimeMode = MM_CVTMIME|MM_PASS8BIT; /* option 8 */ +#else + MimeMode = MM_PASS8BIT; +#endif + 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/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; + DoubleBounceAddr = "postmaster"; + snprintf(buf, sizeof buf, "%s%sdead.letter", + _PATH_VARTMP, + _PATH_VARTMP[sizeof _PATH_VARTMP - 2] == '/' ? "" : "/"); + DeadLetterDrop = newstr(buf); +#ifdef HESIOD_INIT + HesiodContext = NULL; +#endif + setupmaps(); + 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); + snprintf(defuserbuf, sizeof defuserbuf, "%s", + defpwent == NULL ? "nobody" : defpwent->pw_name); + if (tTd(37, 4)) + printf("setdefuser: DefUid=%d, DefUser=%s\n", + (int) DefUid, DefUser); +} + /* +** SETUPMAILERS -- initialize default mailers +*/ + +void +setupmailers() +{ + char buf[100]; + extern void makemailer __P((char *)); + + strcpy(buf, "prog, P=/bin/sh, F=lsoDq9, T=DNS/RFC822/X-Unix, A=sh -c \201u"); + makemailer(buf); + + strcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=DNS/RFC822/X-Unix, A=FILE \201u"); + makemailer(buf); + + strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u"); + makemailer(buf); +} + /* +** 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; \ + } + +void +setupmaps() +{ + register STAB *s; + +#ifdef NEWDB + 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 + +#ifdef NDBM + MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE, + map_parseargs, ndbm_map_open, ndbm_map_close, + ndbm_map_lookup, ndbm_map_store); +#endif + +#ifdef NIS + MAPDEF("nis", NULL, MCF_ALIASOK, + map_parseargs, nis_map_open, null_map_close, + nis_map_lookup, null_map_store); +#endif + +#ifdef NISPLUS + MAPDEF("nisplus", NULL, MCF_ALIASOK, + map_parseargs, nisplus_map_open, null_map_close, + nisplus_map_lookup, null_map_store); +#endif +#ifdef LDAPMAP + MAPDEF("ldapx", NULL, 0, + ldap_map_parseargs, ldap_map_open, ldap_map_close, + ldap_map_lookup, null_map_store); +#endif + +#ifdef HESIOD + MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY, + map_parseargs, hes_map_open, null_map_close, + hes_map_lookup, null_map_store); +#endif + +#if NETINFO + MAPDEF("netinfo", NULL, MCF_ALIASOK, + map_parseargs, ni_map_open, null_map_close, + ni_map_lookup, null_map_store); +#endif + +#if 0 + MAPDEF("dns", NULL, 0, + dns_map_init, null_map_open, null_map_close, + dns_map_lookup, null_map_store); +#endif + +#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 + + 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); + +#ifdef MAP_REGEX + MAPDEF("regex", NULL, 0, + regex_map_init, null_map_open, null_map_close, + regex_map_lookup, null_map_store); +#endif + +#if USERDB + /* user database */ + MAPDEF("userdb", ".db", 0, + map_parseargs, null_map_open, null_map_close, + udb_map_lookup, null_map_store); +#endif + + /* 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); + +#if _FFR_MAP_SYSLOG + /* 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); +#endif +} + +#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]; + + /* + ** Set up default hosts maps. + */ + +#if 0 + nmaps = switch_map_find("hosts", maptype, mapreturn); + for (i = 0; i < nmaps; i++) + { + if (strcmp(maptype[i], "files") == 0 && + stab("hosts.files", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "hosts.files text -k 0 -v 1 /etc/hosts"); + (void) makemapentry(buf); + } +#if NAMED_BIND + else if (strcmp(maptype[i], "dns") == 0 && + stab("hosts.dns", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "hosts.dns dns A"); + (void) makemapentry(buf); + } +#endif +#ifdef NISPLUS + else if (strcmp(maptype[i], "nisplus") == 0 && + stab("hosts.nisplus", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "hosts.nisplus nisplus -k name -v address -d hosts.org_dir"); + (void) makemapentry(buf); + } +#endif +#ifdef NIS + else if (strcmp(maptype[i], "nis") == 0 && + stab("hosts.nis", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "hosts.nis nis -d -k 0 -v 1 hosts.byname"); + (void) makemapentry(buf); + } +#endif +#if NETINFO + else if (strcmp(maptype[i], "netinfo") == 0) && + stab("hosts.netinfo", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "hosts.netinfo netinfo -v name /machines"); + (void) makemapentry(buf); + } +#endif + } +#endif + + /* + ** Make sure we have a host map. + */ + + if (stab("host", ST_MAP, ST_FIND) == NULL) + { + /* user didn't initialize: set up host map */ + strcpy(buf, "host host"); +#if NAMED_BIND + if (ConfigLevel >= 2) + strcat(buf, " -a."); +#endif + (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) + { + strcpy(buf, "aliases.files null"); + (void) makemapentry(buf); + } +#ifdef NISPLUS + else if (strcmp(maptype[i], "nisplus") == 0 && + stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion -d mail_aliases.org_dir"); + (void) makemapentry(buf); + } +#endif +#ifdef NIS + else if (strcmp(maptype[i], "nis") == 0 && + stab("aliases.nis", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "aliases.nis nis -d mail.aliases"); + (void) makemapentry(buf); + } +#endif +#ifdef NETINFO + else if (strcmp(maptype[i], "netinfo") == 0 && + stab("aliases.netinfo", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "aliases.netinfo netinfo -z, /aliases"); + (void) makemapentry(buf); + } +#endif +#ifdef HESIOD + else if (strcmp(maptype[i], "hesiod") == 0 && + stab("aliases.hesiod", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "aliases.hesiod hesiod aliases"); + (void) makemapentry(buf); + } +#endif + } + if (stab("aliases", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "aliases switch aliases"); + (void) makemapentry(buf); + } + +#if 0 /* "user" map class is a better choice */ + /* + ** Set up default users maps. + */ + + nmaps = switch_map_find("passwd", maptype, mapreturn); + for (i = 0; i < nmaps; i++) + { + if (strcmp(maptype[i], "files") == 0 && + stab("users.files", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "users.files text -m -z: -k0 -v6 /etc/passwd"); + (void) makemapentry(buf); + } +#ifdef NISPLUS + else if (strcmp(maptype[i], "nisplus") == 0 && + stab("users.nisplus", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "users.nisplus nisplus -m -kname -vhome -d passwd.org_dir"); + (void) makemapentry(buf); + } +#endif +#ifdef NIS + else if (strcmp(maptype[i], "nis") == 0 && + stab("users.nis", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "users.nis nis -m -d passwd.byname"); + (void) makemapentry(buf); + } +#endif +#ifdef HESIOD + else if (strcmp(maptype[i], "hesiod") == 0) && + stab("users.hesiod", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "users.hesiod hesiod"); + (void) makemapentry(buf); + } +#endif + } + if (stab("users", ST_MAP, ST_FIND) == NULL) + { + strcpy(buf, "users switch -m passwd"); + (void) makemapentry(buf); + } +#endif +} + /* +** 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. +*/ + +#if defined(SOLARIS) || (defined(sony_news) && defined(__svr4)) +# define _USE_SUN_NSSWITCH_ +#endif + +#ifdef _USE_SUN_NSSWITCH_ +# include +#endif + +#if defined(ultrix) || (defined(__osf__) && defined(__alpha)) +# define _USE_DEC_SVC_CONF_ +#endif + +#ifdef _USE_DEC_SVC_CONF_ +# include +#endif + +int +switch_map_find(service, maptype, mapreturn) + char *service; + char *maptype[MAXMAPSTACK]; + short mapreturn[MAXMAPACTIONS]; +{ + int svcno; + +#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) + { + 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; + } + return svcno; +#endif + +#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 + return -1; + for (svcno = 0; svcno < SVC_PATHSIZE; 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 + + case SVC_LAST: + return svcno; + } + } + return svcno; +#endif + +#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) + /* + ** Fall-back mechanism. + */ + + STAB *st; + time_t now = curtime(); + + for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) + mapreturn[svcno] = 0; + + if ((now - ServiceCacheTime) > (time_t) ServiceCacheMaxAge) + { + /* (re)read service switch */ + register FILE *fp; + int sff = SFF_REGONLY|SFF_OPENASROOT|SFF_NOLOCK; + + if (!bitset(DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR, DontBlameSendmail)) + sff |= SFF_NOWLINK; + + if (ConfigFileRead) + ServiceCacheTime = now; + fp = safefopen(ServiceSwitchFile, O_RDONLY, 0, sff); + if (fp != NULL) + { + char buf[MAXLINE]; + + while (fgets(buf, sizeof buf, fp) != 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; + 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) + free((void *) st->s_service[0]); + 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; + } + fclose(fp); + } + } + + /* 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; + } + return --svcno; + } +#endif + +#if !defined(_USE_SUN_NSSWITCH_) + /* if the service file doesn't work, use an absolute fallback */ +# ifdef _USE_DEC_SVC_CONF_ + punt: +# endif + for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) + mapreturn[svcno] = 0; + svcno = 0; + if (strcmp(service, "aliases") == 0) + { + maptype[svcno++] = "files"; +# ifdef AUTO_NIS_ALIASES +# ifdef NISPLUS + maptype[svcno++] = "nisplus"; +# endif +# ifdef NIS + maptype[svcno++] = "nis"; +# endif +# endif + return svcno; + } + if (strcmp(service, "hosts") == 0) + { +# if NAMED_BIND + maptype[svcno++] = "dns"; +# else +# if defined(sun) && !defined(BSD) + /* SunOS */ + maptype[svcno++] = "nis"; +# endif +# endif + maptype[svcno++] = "files"; + return svcno; + } + return -1; +#endif +} + /* +** 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 = newstr(pw->pw_name); + } + else + { + uid_t uid = RealUid; + + myname = newstr(myname); + if ((pw = sm_getpwnam(myname)) == NULL || + (uid != 0 && uid != pw->pw_uid)) + { + pw = sm_getpwuid(uid); + if (pw != NULL) + myname = newstr(pw->pw_name); + } + } + if (myname == NULL || myname[0] == '\0') + { + syserr("554 Who are you?"); + myname = "postmaster"; + } + } + + 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; +{ +# ifdef lint + if (to == NULL) + to++; +# endif /* lint */ + + if (tTd(49, 1)) + printf("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 supress body on return */ + to->q_status = "5.7.1"; + return (EX_UNAVAILABLE); + } +# endif /* EXAMPLE_CODE */ + return (EX_OK); +} + /* +** SETSIGNAL -- set a signal handler +** +** This is essentially old BSD "signal(3)". +*/ + +sigfunc_t +setsignal(sig, handler) + int sig; + sigfunc_t handler; +{ +#if defined(SYS5SIGNALS) || defined(BSD4_3) +# ifdef BSD4_3 + return signal(sig, handler); +# else + return sigset(sig, handler); +# endif +#else + struct sigaction n, o; + + bzero(&n, sizeof n); +# if USE_SA_SIGACTION + n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler; + n.sa_flags = SA_RESTART|SA_SIGINFO; +# else + n.sa_handler = handler; +# ifdef SA_RESTART + n.sa_flags = SA_RESTART; +# endif +# endif + if (sigaction(sig, &n, &o) < 0) + return SIG_ERR; + return o.sa_handler; +#endif +} + /* +** BLOCKSIGNAL -- hold a signal to prevent delivery +** +** Parameters: +** sig -- the signal to block. +** +** Returns: +** 1 signal was previously blocked +** 0 signal was not previously blocked +** -1 on failure. +*/ + +int +blocksignal(sig) + int sig; +{ +#ifdef BSD4_3 +# ifndef sigmask +# define sigmask(s) (1 << ((s) - 1)) +# endif + return (sigblock(sigmask(sig)) & sigmask(sig)) != 0; +#else +# ifdef ALTOS_SYSTEM_V + sigfunc_t handler; + + handler = sigset(sig, SIG_HOLD); + if (handler == SIG_ERR) + return -1; + else + return handler == SIG_HOLD; +# else + sigset_t sset, oset; + + sigemptyset(&sset); + sigaddset(&sset, sig); + if (sigprocmask(SIG_BLOCK, &sset, &oset) < 0) + return -1; + else + return sigismember(&oset, sig); +# endif +#endif +} + /* +** RELEASESIGNAL -- release a held signal +** +** Parameters: +** sig -- the signal to release. +** +** Returns: +** 1 signal was previously blocked +** 0 signal was not previously blocked +** -1 on failure. +*/ + +int +releasesignal(sig) + int sig; +{ +#ifdef BSD4_3 + return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0; +#else +# ifdef ALTOS_SYSTEM_V + sigfunc_t handler; + + handler = sigset(sig, SIG_HOLD); + if (sigrelse(sig) < 0) + return -1; + else + return handler == SIG_HOLD; +# else + sigset_t sset, oset; + + sigemptyset(&sset); + sigaddset(&sset, sig); + if (sigprocmask(SIG_UNBLOCK, &sset, &oset) < 0) + return -1; + else + return sigismember(&oset, sig); +# endif +#endif +} + /* +** HOLDSIGS -- arrange to hold all signals +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** Arranges that signals are held. +*/ + +void +holdsigs() +{ +} + /* +** RLSESIGS -- arrange to release all signals +** +** This undoes the effect of holdsigs. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** Arranges that signals are released. +*/ + +void +rlsesigs() +{ +} + /* +** 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 + +#if SHARE_V1 +# include +#endif + +void +init_md(argc, argv) + int argc; + char **argv; +{ +#ifdef _AUX_SOURCE + setcompat(getcompat() | COMPAT_BSDPROT); +#endif + +#ifdef SUN_EXTENSIONS + init_md_sun(); +#endif + +#if _CONVEX_SOURCE + /* keep gethostby*() from stripping the local domain name */ + set_domain_trim_off(); +#endif +#ifdef __QNX__ + /* + ** 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 +#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 +#endif + +#ifdef VENDOR_DEFAULT + VendorCode = VENDOR_DEFAULT; +#else + VendorCode = VENDOR_BERKELEY; +#endif +} + /* +** 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 */ + +/* do guesses based on general OS type */ +#ifndef LA_TYPE +# define LA_TYPE LA_ZERO +#endif + +#ifndef FSHIFT +# if defined(unixpc) +# define FSHIFT 5 +# endif + +# if defined(__alpha) || defined(IRIX) +# define FSHIFT 10 +# endif + +#endif + +#ifndef FSHIFT +# define FSHIFT 8 +#endif + +#ifndef FSCALE +# define FSCALE (1 << FSHIFT) +#endif + +#ifndef LA_AVENRUN +# ifdef SYSTEM5 +# define LA_AVENRUN "avenrun" +# else +# define LA_AVENRUN "_avenrun" +# endif +#endif + +/* _PATH_KMEM should be defined in */ +#ifndef _PATH_KMEM +# define _PATH_KMEM "/dev/kmem" +#endif + +#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT) + +#include + +/* _PATH_UNIX should be defined in */ +#ifndef _PATH_UNIX +# if defined(SYSTEM5) +# define _PATH_UNIX "/unix" +# else +# define _PATH_UNIX "/vmunix" +# endif +#endif + +#ifdef _AUX_SOURCE +struct nlist Nl[2]; +#else +struct nlist Nl[] = +{ + { LA_AVENRUN }, + { 0 }, +}; +#endif +#define X_AVENRUN 0 + +int +getla() +{ + static int kmem = -1; +#if LA_TYPE == LA_INT + long avenrun[3]; +#else +# if LA_TYPE == LA_SHORT + short avenrun[3]; +# else + double avenrun[3]; +# endif +#endif + extern int errno; + extern off_t lseek(); + + if (kmem < 0) + { +#ifdef _AUX_SOURCE + strcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN); + Nl[1].n_name[0] = '\0'; +#endif + +#if defined(_AIX3) || defined(_AIX4) + if (knlist(Nl, 1, sizeof Nl[0]) < 0) +#else + if (nlist(_PATH_UNIX, Nl) < 0) +#endif + { + if (tTd(3, 1)) + printf("getla: nlist(%s): %s\n", _PATH_UNIX, + errstring(errno)); + return (-1); + } + if (Nl[X_AVENRUN].n_value == 0) + { + if (tTd(3, 1)) + printf("getla: nlist(%s, %s) ==> 0\n", + _PATH_UNIX, LA_AVENRUN); + return (-1); + } +#ifdef NAMELISTMASK + Nl[X_AVENRUN].n_value &= NAMELISTMASK; +#endif + + kmem = open(_PATH_KMEM, 0, 0); + if (kmem < 0) + { + if (tTd(3, 1)) + printf("getla: open(/dev/kmem): %s\n", + errstring(errno)); + return (-1); + } + (void) fcntl(kmem, F_SETFD, 1); + } + if (tTd(3, 20)) + printf("getla: symbol address = %#lx\n", + (u_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)) + printf("getla: lseek or read: %s\n", errstring(errno)); + return (-1); + } +# if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) + if (tTd(3, 5)) + { +# if LA_TYPE == LA_SHORT + printf("getla: avenrun = %d", avenrun[0]); + if (tTd(3, 15)) + printf(", %d, %d", avenrun[1], avenrun[2]); +# else + printf("getla: avenrun = %ld", avenrun[0]); + if (tTd(3, 15)) + printf(", %ld, %ld", avenrun[1], avenrun[2]); +# endif + printf("\n"); + } + if (tTd(3, 1)) + printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); + return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); +# else /* LA_TYPE == LA_FLOAT */ + if (tTd(3, 5)) + { + printf("getla: avenrun = %g", avenrun[0]); + if (tTd(3, 15)) + printf(", %g, %g", avenrun[1], avenrun[2]); + printf("\n"); + } + if (tTd(3, 1)) + printf("getla: %d\n", (int) (avenrun[0] +0.5)); + return ((int) (avenrun[0] + 0.5)); +# endif +} + +#endif /* LA_TYPE == LA_INT or LA_SHORT or LA_FLOAT */ + +#if LA_TYPE == LA_READKSYM + +# include + +getla() +{ + static int kmem = -1; + long avenrun[3]; + extern int errno; + struct mioc_rksym mirk; + + if (kmem < 0) + { + kmem = open("/dev/kmem", 0, 0); + if (kmem < 0) + { + if (tTd(3, 1)) + printf("getla: open(/dev/kmem): %s\n", + errstring(errno)); + return (-1); + } + (void) fcntl(kmem, F_SETFD, 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)) + printf("getla: ioctl(MIOC_READKSYM) failed: %s\n", + errstring(errno)); + return -1; + } + if (tTd(3, 5)) + { + printf("getla: avenrun = %d", avenrun[0]); + if (tTd(3, 15)) + printf(", %d, %d", avenrun[1], avenrun[2]); + printf("\n"); + } + if (tTd(3, 1)) + printf("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)) + printf("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)) + printf("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)) + perror("getla: getloadavg failed:"); + return (-1); + } + if (tTd(3, 1)) + printf("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 +# include +#endif + +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)) + perror("getla: processor_set_default failed:"); + 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)) + perror("getla: processor_set_info failed:"); + return -1; + } + if (tTd(3, 1)) + printf("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 + +/* +** 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 + +int +getla() +{ + double avenrun; + register int result; + FILE *fp; + + fp = fopen(_PATH_LOADAVG, "r"); + if (fp == NULL) + { + if (tTd(3, 1)) + printf("getla: fopen(%s): %s\n", + _PATH_LOADAVG, errstring(errno)); + return -1; + } + result = fscanf(fp, "%lf", &avenrun); + fclose(fp); + if (result != 1) + { + if (tTd(3, 1)) + printf("getla: fscanf() = %d: %s\n", + result, errstring(errno)); + return -1; + } + + if (tTd(3, 1)) + printf("getla(): %.2f\n", avenrun); + + return ((int) (avenrun + 0.5)); +} + +#endif /* LA_TYPE == LA_PROCSTR */ + +#if LA_TYPE == LA_IRIX6 +#include + +int getla(void) +{ + static int kmem = -1; + int avenrun[3]; + + if (kmem < 0) + { + kmem = open(_PATH_KMEM, 0, 0); + if (kmem < 0) + { + if (tTd(3, 1)) + printf("getla: open(%s): %s\n", _PATH_KMEM, + errstring(errno)); + return -1; + } + (void) fcntl(kmem, F_SETFD, 1); + } + + if (lseek(kmem, (sysmp(MP_KERNADDR, MPKA_AVENRUN) & 0x7fffffff), SEEK_SET) == -1 || + read(kmem, (char *)avenrun, sizeof(avenrun)) < sizeof(avenrun)) + { + if (tTd(3, 1)) + printf("getla: lseek or read: %s\n", + errstring(errno)); + return -1; + } + if (tTd(3, 5)) + { + printf("getla: avenrun = %ld", (long int) avenrun[0]); + if (tTd(3, 15)) + printf(", %ld, %ld", + (long int) avenrun[1], (long int) avenrun[2]); + printf("\n"); + } + + if (tTd(3, 1)) + printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); + return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); + +} +#endif + +#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)) + printf("getla: kstat_open(): %s\n", + errstring(errno)); + return -1; + } + if (ksp == NULL) + ksp = kstat_lookup(kc, "unix", 0, "system_misc"); + if (ksp == NULL) + { + if (tTd(3, 1)) + printf("getla: kstat_lookup(): %s\n", + errstring(errno)); + return -1; + } + if (kstat_read(kc, ksp, NULL) < 0) + { + if (tTd(3, 1)) + printf("getla: kstat_read(): %s\n", + 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 + +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: %m", + _PATH_AVENRUN); + return -1; + } + } + + r = read(afd, &avenrun, sizeof avenrun); + + if (tTd(3, 5)) + printf("getla: avenrun = %d\n", avenrun); + loadav = (int) (avenrun + FSCALE/2) >> FSHIFT; + if (tTd(3, 1)) + printf("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)) + printf("getla: table %s\n", errstring(errno)); + return (-1); + } + + if (tTd(3, 1)) + printf("getla: scale = %d\n", tab.tl_lscale); + + if (tab.tl_lscale) + ave = (tab.tl_avenrun.l[0] + (tab.tl_lscale/2)) / tab.tl_lscale; + else + ave = (int) (tab.tl_avenrun.d[0] + 0.5); + + if (tTd(3, 1)) + printf("getla: %d\n", ave); + + return ave; +} + +#endif + +#if LA_TYPE == LA_ZERO + +int +getla() +{ + if (tTd(3, 1)) + printf("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 +static char rcsid[] = "@(#)$Id: 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 */ + /* +** SHOULDQUEUE -- should this message be queued or sent? +** +** Compares the message cost to the load average to decide. +** +** Parameters: +** pri -- the priority of the message in question. +** ctime -- the message creation time. +** +** 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. +*/ + +extern int get_num_procs_online __P((void)); + +bool +shouldqueue(pri, ctime) + long pri; + time_t ctime; +{ + bool rval; + int queuela = QueueLA * get_num_procs_online(); + + if (tTd(3, 30)) + printf("shouldqueue: CurrentLA=%d, pri=%ld: ", CurrentLA, pri); + if (CurrentLA < queuela) + { + if (tTd(3, 30)) + printf("FALSE (CurrentLA < QueueLA)\n"); + return (FALSE); + } +#if 0 /* this code is reported to cause oscillation around RefuseLA */ + if (CurrentLA >= RefuseLA && QueueLA < RefuseLA) + { + if (tTd(3, 30)) + printf("TRUE (CurrentLA >= RefuseLA)\n"); + return (TRUE); + } +#endif + rval = pri > (QueueFactor / (CurrentLA - queuela + 1)); + if (tTd(3, 30)) + printf("%s (by calculation)\n", rval ? "TRUE" : "FALSE"); + return rval; +} + /* +** REFUSECONNECTIONS -- decide if connections should be refused +** +** Parameters: +** port -- port number (for error messages only) +** +** 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(port) + int port; +{ + int refusela = RefuseLA * get_num_procs_online(); + time_t now; + static time_t lastconn = (time_t) 0; + static int conncnt = 0; + extern bool enoughdiskspace __P((long)); + +#ifdef XLA + if (!xla_smtp_ok()) + return TRUE; +#endif + + now = curtime(); + if (now != lastconn) + { + lastconn = now; + conncnt = 0; + } + else if (conncnt++ > ConnRateThrottle && ConnRateThrottle > 0) + { + /* sleep to flatten out connection load */ + setproctitle("deferring connections on port %d: %d per second", + port, ConnRateThrottle); + if (LogLevel >= 14) + sm_syslog(LOG_INFO, NOQID, + "deferring connections on port %d: %d per second", + port, ConnRateThrottle); + sleep(1); + } + + CurrentLA = getla(); + if (CurrentLA >= refusela) + { + setproctitle("rejecting connections on port %d: load average: %d", + port, CurrentLA); + if (LogLevel >= 14) + sm_syslog(LOG_INFO, NOQID, + "rejecting connections on port %d: load average: %d", + port, CurrentLA); + return TRUE; + } + + if (!enoughdiskspace(MinBlocksFree + 1)) + { + setproctitle("rejecting connections on port %d: min free: %d", + port, MinBlocksFree); + if (LogLevel >= 14) + sm_syslog(LOG_INFO, NOQID, + "rejecting connections on port %d: min free: %d", + port, MinBlocksFree); + return TRUE; + } + + if (MaxChildren > 0 && CurChildren >= MaxChildren) + { + extern void proc_list_probe __P((void)); + + proc_list_probe(); + if (CurChildren >= MaxChildren) + { + setproctitle("rejecting connections on port %d: %d children, max %d", + port, CurChildren, MaxChildren); + if (LogLevel >= 14) + sm_syslog(LOG_INFO, NOQID, + "rejecting connections on port %d: %d children, max %d", + port, CurChildren, MaxChildren); + 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 + +#if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN + +# if SPT_TYPE == SPT_PSTAT +# include +# endif +# 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 +# ifndef NKPDE /* FreeBSD 2.0 */ +# define NKPDE 63 +typedef unsigned int *pt_entry_t; +# endif +# endif +# endif + +# if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV +# define SETPROC_STATIC static +# else +# define SETPROC_STATIC +# endif + +# if SPT_TYPE == SPT_SYSMIPS +# include +# include +# endif + +# if SPT_TYPE == SPT_SCO +# include +# include +# include +# include +# if PSARGSZ > MAXLINE +# define SPT_BUFSIZE PSARGSZ +# endif +# endif + +# ifndef SPT_PADCHAR +# define SPT_PADCHAR ' ' +# endif + +# ifndef SPT_BUFSIZE +# define SPT_BUFSIZE MAXLINE +# endif + +#endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */ + +/* +** Pointers for setproctitle. +** This allows "ps" listings to give more useful information. +*/ + +char **Argv = NULL; /* pointer to argument vector */ +char *LastArgv = NULL; /* end of argv */ + +void +initsetproctitle(argc, argv, envp) + int argc; + char **argv; + char **envp; +{ + register int i, envpsize = 0; + extern char **environ; + + /* + ** Move the environment so setproctitle can use the space at + ** the top of memory. + */ + + for (i = 0; envp[i] != NULL; i++) + envpsize += strlen(envp[i]) + 1; + 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] + */ + for (i = 0; i < argc; i++) + { + if (i==0 || LastArgv + 1 == argv[i]) + LastArgv = argv[i] + strlen(argv[i]); + else + continue; + } + for (i=0; envp[i] != NULL; i++) + { + if (LastArgv + 1 == envp[i]) + LastArgv = envp[i] + strlen(envp[i]); + else + continue; + } +} + +#if SPT_TYPE != SPT_BUILTIN + + +/*VARARGS1*/ +void +# ifdef __STDC__ +setproctitle(const char *fmt, ...) +# else +setproctitle(fmt, va_alist) + const char *fmt; + va_dcl +# endif +{ +# if SPT_TYPE != SPT_NONE + register char *p; + register int i; + SETPROC_STATIC char buf[SPT_BUFSIZE]; + VA_LOCAL_DECL +# if SPT_TYPE == SPT_PSTAT + union pstun pst; +# endif +# if SPT_TYPE == SPT_SCO + off_t seek_off; + static int kmem = -1; + static int kmempid = -1; + struct user u; +# endif + + p = buf; + + /* print sendmail: heading for grep */ + (void) strcpy(p, "sendmail: "); + p += strlen(p); + + /* print the argument string */ + VA_START(fmt); + (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap); + VA_END; + + i = strlen(buf); + +# if SPT_TYPE == SPT_PSTAT + pst.pst_command = buf; + pstat(PSTAT_SETCMD, pst, i, 0, 0); +# endif +# if SPT_TYPE == SPT_PSSTRINGS + PS_STRINGS->ps_nargvstr = 1; + PS_STRINGS->ps_argvstr = buf; +# endif +# if SPT_TYPE == SPT_SYSMIPS + sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf); +# endif +# if SPT_TYPE == SPT_SCO + if (kmem < 0 || kmempid != getpid()) + { + if (kmem >= 0) + close(kmem); + kmem = open(_PATH_KMEM, O_RDWR, 0); + if (kmem < 0) + return; + (void) fcntl(kmem, F_SETFD, 1); + kmempid = getpid(); + } + 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 +# if SPT_TYPE == SPT_REUSEARGV + if (i > LastArgv - Argv[0] - 2) + { + i = LastArgv - Argv[0] - 2; + buf[i] = '\0'; + } + (void) strcpy(Argv[0], buf); + p = &Argv[0][i]; + while (p < LastArgv) + *p++ = SPT_PADCHAR; + Argv[1] = NULL; +# endif +# if SPT_TYPE == SPT_CHANGEARGV + Argv[0] = buf; + Argv[1] = 0; +# endif +# endif /* SPT_TYPE != SPT_NONE */ +} + +#endif /* SPT_TYPE != SPT_BUILTIN */ + /* +** 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; +{ +#ifdef WAITUNION + union wait st; +#else + auto int st; +#endif + pid_t i; +#if defined(ISC_UNIX) || defined(_SCO_unix_) + int savesig; +#endif + + do + { + errno = 0; +#if defined(ISC_UNIX) || defined(_SCO_unix_) + savesig = releasesignal(SIGCHLD); +#endif + i = wait(&st); +#if defined(ISC_UNIX) || defined(_SCO_unix_) + if (savesig > 0) + blocksignal(SIGCHLD); +#endif + if (i > 0) + proc_list_drop(i); + } while ((i >= 0 || errno == EINTR) && i != pid); + if (i < 0) + return -1; +#ifdef WAITUNION + return st.w_status; +#else + return st; +#endif +} + /* +** 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. +*/ + +SIGFUNC_DECL +reapchild(sig) + int sig; +{ + int olderrno = errno; + pid_t pid; +# ifdef HASWAITPID + auto int status; + int count; + + count = 0; + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + { + if (count++ > 1000) + { + if (LogLevel > 0) + sm_syslog(LOG_ALERT, NOQID, + "reapchild: waitpid loop: pid=%d, status=%x", + pid, status); + break; + } + proc_list_drop(pid); + } +# else +# ifdef WNOHANG + union wait status; + + while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0) + proc_list_drop(pid); +# 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) + proc_list_drop(pid); +# endif /* WNOHANG */ +# endif +# ifdef SYS5SIGNALS + (void) setsignal(SIGCHLD, reapchild); +# endif + errno = olderrno; + return SIGFUNC_RETURN; +} + /* +** PUTENV -- emulation of putenv() in terms of setenv() +** +** Not needed on Posix-compliant systems. +** This doesn't have full Posix semantics, but it's good enough +** for sendmail. +** +** Parameter: +** env -- the environment to put. +** +** Returns: +** none. +*/ + +#ifdef NEEDPUTENV + +# if NEEDPUTENV == 2 /* no setenv(3) call available */ + +int +putenv(str) + char *str; +{ + char **current; + int matchlen, envlen=0; + char *tmp; + char **newenv; + static int first=1; + extern char **environ; + + /* + * find out how much of str to match when searching + * for a string to replace. + */ + if ((tmp = strchr(str, '=')) == NULL || tmp == str) + matchlen = strlen(str); + else + matchlen = (int) (tmp - str); + ++matchlen; + + /* + * Search for an existing string in the environment and find the + * length of environ. If found, replace and exit. + */ + for (current=environ; *current; current++) { + ++envlen; + + if (strncmp(str, *current, matchlen) == 0) { + /* found it, now insert the new version */ + *current = (char *)str; + return(0); + } + } + + /* + * There wasn't already a slot so add space for a new slot. + * If this is our first time through, use malloc(), else realloc(). + */ + if (first) { + newenv = (char **) malloc(sizeof(char *) * (envlen + 2)); + if (newenv == NULL) + return(-1); + + first=0; + (void) memcpy(newenv, environ, sizeof(char *) * envlen); + } else { + newenv = (char **) realloc((char *)environ, sizeof(char *) * (envlen + 2)); + if (newenv == NULL) + return(-1); + } + + /* actually add in the new entry */ + environ = newenv; + environ[envlen] = (char *)str; + environ[envlen+1] = NULL; + + return(0); +} + +#else /* implement putenv() in terms of setenv() */ + +int +putenv(env) + char *env; +{ + char *p; + int l; + char nbuf[100]; + + p = strchr(env, '='); + if (p == NULL) + return 0; + l = p - env; + if (l > sizeof nbuf - 1) + l = sizeof nbuf - 1; + bcopy(env, nbuf, l); + nbuf[l] = '\0'; + return setenv(nbuf, ++p, 1); +} + +# endif +#endif + /* +** UNSETENV -- remove a variable from the environment +** +** Not needed on newer systems. +** +** Parameters: +** name -- the string name of the environment variable to be +** deleted from the current environment. +** +** Returns: +** none. +** +** Globals: +** environ -- a pointer to the current environment. +** +** Side Effects: +** Modifies environ. +*/ + +#ifndef HASUNSETENV + +void +unsetenv(name) + char *name; +{ + extern char **environ; + register char **pp; + int len = strlen(name); + + for (pp = environ; *pp != NULL; pp++) + { + if (strncmp(name, *pp, len) == 0 && + ((*pp)[len] == '=' || (*pp)[len] == '\0')) + break; + } + + for (; *pp != NULL; pp++) + *pp = pp[1]; +} + +#endif + /* +** 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 + +int +getdtsize() +{ +#ifdef RLIMIT_NOFILE + struct rlimit rl; + + if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) + return rl.rlim_cur; +#endif + +# ifdef HASGETDTABLESIZE + return getdtablesize(); +# else +# ifdef _SC_OPEN_MAX + return sysconf(_SC_OPEN_MAX); +# else + return NOFILE; +# endif +# endif +} + /* +** UNAME -- get the UUCP name of this system. +*/ + +#ifndef HASUNAME + +int +uname(name) + struct utsname *name; +{ + FILE *file; + char *n; + + name->nodename[0] = '\0'; + + /* try /etc/whoami -- one line with the node name */ + if ((file = fopen("/etc/whoami", "r")) != NULL) + { + (void) fgets(name->nodename, NODE_LENGTH + 1, file); + (void) fclose(file); + 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 = fopen("/usr/include/whoami.h", "r")) != NULL) + { + char buf[MAXLINE]; + + while (fgets(buf, MAXLINE, file) != NULL) + if (sscanf(buf, "#define sysname \"%*[^\"]\"", + NODE_LENGTH, name->nodename) > 0) + break; + (void) fclose(file); + if (name->nodename[0] != '\0') + return (0); + } + +#ifdef TRUST_POPEN + /* + ** Popen is known to have security holes. + */ + + /* try uuname -l to return local name */ + if ((file = popen("uuname -l", "r")) != NULL) + { + (void) fgets(name, NODE_LENGTH + 1, file); + (void) pclose(file); + n = strchr(name, '\n'); + if (n != NULL) + *n = '\0'; + if (name->nodename[0] != '\0') + return (0); + } +#endif + + return (-1); +} +#endif /* HASUNAME */ + /* +** INITGROUPS -- initialize groups +** +** Stub implementation for System V style systems +*/ + +#ifndef HASINITGROUPS + +initgroups(name, basegid) + char *name; + int basegid; +{ + return 0; +} + +#endif + /* +** 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 + /* +** SETSID -- set session id (for non-POSIX systems) +*/ + +#ifndef HASSETSID + +pid_t +setsid __P ((void)) +{ +#ifdef TIOCNOTTY + int fd; + + fd = open("/dev/tty", O_RDWR, 0); + if (fd >= 0) + { + (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0); + (void) close(fd); + } +#endif /* TIOCNOTTY */ +# ifdef SYS5SETPGRP + return setpgrp(); +# else + return setpgid(0, getpid()); +# endif +} + +#endif + /* +** FSYNC -- dummy fsync +*/ + +#ifdef NEEDFSYNC + +fsync(fd) + int fd; +{ +# ifdef O_SYNC + return fcntl(fd, F_SETFL, O_SYNC); +# else + /* nothing we can do */ + return 0; +# endif +} + +#endif + /* +** 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 + /* +** GETOPT -- for old systems or systems with bogus implementations +*/ + +#ifdef NEEDGETOPT + +/* + * 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 /* LIBC_SCCS and not lint */ + +#include + +/* + * get option letter from argument vector + */ +#ifdef _CONVEX_SOURCE +extern int optind, opterr, optopt; +extern char *optarg; +#else +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 + +#define BADCH (int)'?' +#define EMSG "" +#define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \ + fputc(optopt,stderr);fputc('\n',stderr);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 + /* +** VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version +*/ + +#ifdef NEEDVPRINTF + +#define MAXARG 16 + +vfprintf(fp, fmt, ap) + FILE *fp; + char *fmt; + char **ap; +{ + char *bp[MAXARG]; + int i = 0; + + while (*ap && i < MAXARG) + bp[i++] = *ap++; + fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3], + bp[4], bp[5], bp[6], bp[7], + bp[8], bp[9], bp[10], bp[11], + bp[12], bp[13], bp[14], bp[15]); +} + +vsprintf(s, fmt, ap) + char *s; + char *fmt; + char **ap; +{ + char *bp[MAXARG]; + int i = 0; + + while (*ap && i < MAXARG) + bp[i++] = *ap++; + sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3], + bp[4], bp[5], bp[6], bp[7], + bp[8], bp[9], bp[10], bp[11], + bp[12], bp[13], bp[14], bp[15]); +} + +#endif + /* +** 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 + +# if defined(_AIX3) || defined(_AIX4) +# include +# if _AIX4 >= 40200 +# include +# endif +# include +# endif + +char *DefaultUserShells[] = +{ + "/bin/sh", /* standard shell */ + "/usr/bin/sh", + "/bin/csh", /* C shell */ + "/usr/bin/csh", +#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 + "/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", +# endif +#endif +#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 +#ifdef __svr4__ + "/bin/ksh", /* Korn shell */ + "/usr/bin/ksh", +#endif +#ifdef sgi + "/sbin/sh", /* SGI's shells really live in /sbin */ + "/sbin/csh", + "/bin/ksh", /* Korn shell */ + "/sbin/ksh", + "/usr/bin/ksh", + "/bin/tcsh", /* Extended csh */ + "/usr/bin/tcsh", +#endif + NULL +}; + +#endif + +#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 +# if USEGETCONFATTR + auto char *v; +# endif + register FILE *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 + + shellf = fopen(_PATH_SHELLS, "r"); + 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, errstring(errno)); + + for (d = DefaultUserShells; *d != NULL; d++) + { + if (strcmp(shell, *d) == 0) + return TRUE; + } + return FALSE; + } + + while (fgets(buf, sizeof buf, shellf) != 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) + { + fclose(shellf); + return TRUE; + } + } + fclose(shellf); + return FALSE; +#endif +} + /* +** 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 bytes 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 + +#if SFS_TYPE == SFS_USTAT +# include +#endif +#if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS +# include +#endif +#if SFS_TYPE == SFS_VFS +# include +#endif +#if SFS_TYPE == SFS_MOUNT +# include +#endif +#if SFS_TYPE == SFS_STATVFS +# include +#endif + +long +freediskspace(dir, bsize) + char *dir; + long *bsize; +{ +#if SFS_TYPE != SFS_NONE +# if SFS_TYPE == SFS_USTAT + struct ustat fs; + struct stat statbuf; +# define FSBLOCKSIZE DEV_BSIZE +# define SFS_BAVAIL f_tfree +# else +# if defined(ultrix) + struct fs_data fs; +# define SFS_BAVAIL fd_bfreen +# define FSBLOCKSIZE 1024L +# else +# if SFS_TYPE == SFS_STATVFS + struct statvfs fs; +# define FSBLOCKSIZE fs.f_frsize +# else + struct statfs fs; +# define FSBLOCKSIZE fs.f_bsize +# endif +# endif +# endif +# ifndef SFS_BAVAIL +# define SFS_BAVAIL f_bavail +# endif + +# if SFS_TYPE == SFS_USTAT + if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0) +# else +# if SFS_TYPE == SFS_4ARGS + if (statfs(dir, &fs, sizeof fs, 0) == 0) +# else +# if SFS_TYPE == SFS_STATVFS + if (statvfs(dir, &fs) == 0) +# else +# if defined(ultrix) + if (statfs(dir, &fs) > 0) +# else + if (statfs(dir, &fs) == 0) +# endif +# endif +# endif +# endif + { + if (bsize != NULL) + *bsize = FSBLOCKSIZE; + if (fs.SFS_BAVAIL <= 0) + return 0; + else if (fs.SFS_BAVAIL > LONG_MAX) + return LONG_MAX; + else + return (long) fs.SFS_BAVAIL; + } +#endif + return (-1); +} + /* +** ENOUGHDISKSPACE -- is there enough free space on the queue fs? +** +** Only implemented if you have statfs. +** +** 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. +** +** Returns: +** TRUE if there is enough space. +** FALSE otherwise. +*/ + +bool +enoughdiskspace(msize) + long msize; +{ + long bfree, bsize; + + if (MinBlocksFree <= 0 && msize <= 0) + { + if (tTd(4, 80)) + printf("enoughdiskspace: no threshold\n"); + return TRUE; + } + + if ((bfree = freediskspace(QueueDir, &bsize)) >= 0) + { + if (tTd(4, 80)) + printf("enoughdiskspace: bavail=%ld, need=%ld\n", + bfree, msize); + + /* convert msize to block count */ + msize = msize / bsize + 1; + if (MinBlocksFree >= 0) + msize += MinBlocksFree; + + if (bfree < msize) + { + if (LogLevel > 0) + sm_syslog(LOG_ALERT, CurEnv->e_id, + "low on space (have %ld, %s needs %ld in %s)", + bfree, + CurHostName == NULL ? "SMTP-DAEMON" : CurHostName, + msize, QueueDir); + return FALSE; + } + } + else if (tTd(4, 80)) + printf("enoughdiskspace failure: min=%ld, need=%ld: %s\n", + MinBlocksFree, msize, errstring(errno)); + 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 */ +#ifdef ETIMEDOUT + case ETIMEDOUT: /* Connection timed out */ +#endif +#ifdef ESTALE + case ESTALE: /* Stale NFS file handle */ +#endif +#ifdef ENETDOWN + case ENETDOWN: /* Network is down */ +#endif +#ifdef ENETUNREACH + case ENETUNREACH: /* Network is unreachable */ +#endif +#ifdef ENETRESET + case ENETRESET: /* Network dropped connection on reset */ +#endif +#ifdef ECONNABORTED + case ECONNABORTED: /* Software caused connection abort */ +#endif +#ifdef ECONNRESET + case ECONNRESET: /* Connection reset by peer */ +#endif +#ifdef ENOBUFS + case ENOBUFS: /* No buffer space available */ +#endif +#ifdef ESHUTDOWN + case ESHUTDOWN: /* Can't send after socket shutdown */ +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: /* Connection refused */ +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: /* Host is down */ +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: /* No route to host */ +#endif +#ifdef EDQUOT + case EDQUOT: /* Disc quota exceeded */ +#endif +#ifdef EPROCLIM + case EPROCLIM: /* Too many processes */ +#endif +#ifdef EUSERS + case EUSERS: /* Too many users */ +#endif +#ifdef EDEADLK + case EDEADLK: /* Resource deadlock avoided */ +#endif +#ifdef EISCONN + case EISCONN: /* Socket already connected */ +#endif +#ifdef EINPROGRESS + case EINPROGRESS: /* Operation now in progress */ +#endif +#ifdef EALREADY + case EALREADY: /* Operation already in progress */ +#endif +#ifdef EADDRINUSE + case EADDRINUSE: /* Address already in use */ +#endif +#ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: /* Can't assign requested address */ +#endif +#ifdef ETXTBSY + case ETXTBSY: /* (Apollo) file locked */ +#endif +#if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR)) + case ENOSR: /* Out of streams resources */ +#endif + 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. +** +** 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 = ""; + + bzero(&lfd, 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)) + printf("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)) + printf("SUCCESS\n"); + return TRUE; + } + save_errno = errno; + + if (tTd(55, 60)) + printf("(%s) ", 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)) + printf("SUCCESS\n"); + return TRUE; + } + + if (!bitset(LOCK_NB, type) || (save_errno != EACCES && save_errno != EAGAIN)) + { + int omode = -1; +# ifdef F_GETFL + (void) fcntl(fd, F_GETFL, &omode); + errno = save_errno; +# endif + syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", + filename, ext, fd, type, omode, geteuid()); + dumpfd(fd, TRUE, TRUE); + } +# else + if (ext == NULL) + ext = ""; + + if (tTd(55, 60)) + printf("lockfile(%s%s, type=%o): ", filename, ext, type); + + while ((i = flock(fd, type)) < 0 && errno == EINTR) + continue; + if (i >= 0) + { + if (tTd(55, 60)) + printf("SUCCESS\n"); + return TRUE; + } + save_errno = errno; + + if (tTd(55, 60)) + printf("(%s) ", errstring(save_errno)); + + if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK) + { + int omode = -1; +# ifdef F_GETFL + (void) fcntl(fd, F_GETFL, &omode); + errno = save_errno; +# endif + syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", + filename, ext, fd, type, omode, geteuid()); + dumpfd(fd, TRUE, TRUE); + } +# endif + if (tTd(55, 60)) + printf("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 + +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 (bitset(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 + return safedir && errno == 0 && rval IS_SAFE_CHOWN; +# endif +#else + return bitset(DBS_ASSUMESAFECHOWN, DontBlameSendmail); +#endif +} + /* +** 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 +# include +#endif +#ifndef FD_SETSIZE +# define FD_SETSIZE 256 +#endif + +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 +#else +# if HASULIMIT + (void) ulimit(2, 0x3fffff); + (void) ulimit(4, FD_SETSIZE); +# endif +#endif + errno = 0; +} + /* +** GETCFNAME -- return the name of the .cf file. +** +** Some systems (e.g., NeXT) determine this dynamically. +*/ + +char * +getcfname() +{ + + if (ConfFile != NULL) + return ConfFile; +#if NETINFO + { + extern char *ni_propval __P((char *, char *, char *, char *, int)); + char *cflocation; + + cflocation = ni_propval("/locations", NULL, "sendmail", + "sendmail.cf", '\0'); + if (cflocation != NULL) + return cflocation; + } +#endif + + return _PATH_SENDMAILCF; +} + /* +** 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 (strcasecmp(vendor, "Berkeley") == 0) + { + VendorCode = VENDOR_BERKELEY; + return TRUE; + } + + /* add vendor extensions here */ + +#ifdef SUN_EXTENSIONS + if (strcasecmp(vendor, "Sun") == 0) + { + VendorCode = VENDOR_SUN; + return TRUE; + } +#endif + + return FALSE; +} + /* +** 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 + +void +vendor_pre_defaults(e) + ENVELOPE *e; +{ +#if SHARE_V1 + /* OTHERUID is defined in shares.h, do not be alarmed */ + DefShareUid = OTHERUID; +#endif +#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES) + sun_pre_defaults(e); +#endif +#ifdef apollo + /* stupid domain/os can't even open /etc/sendmail.cf without this */ + setuserenv("ISP", NULL); + setuserenv("SYSTYPE", NULL); +#endif +} + + +void +vendor_post_defaults(e) + ENVELOPE *e; +{ +#ifdef __QNX__ + char *p; + + /* Makes sure the SOCK environment variable remains */ + if (p = getextenv("SOCK")) + setuserenv("SOCK", p); +#endif +#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES) + sun_post_defaults(e); +#endif +} + /* +** VENDOR_DAEMON_SETUP -- special vendor setup needed for daemon mode +*/ + +void +vendor_daemon_setup(e) + ENVELOPE *e; +{ +#if SECUREWARE + if (getluid() != -1) + { + usrerr("Daemon cannot have LUID"); + exit(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 and auditing inforation (luid's) + ** before we loose our ``root''ness. + */ +#if SHARE_V1 + if (setupshares(uid, syserr) != 0) + syserr("Unable to set up shares"); +#endif +#if SECUREWARE + (void) setup_secure(uid); +#endif +} + /* +** 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 + +#if DAEMON +char * +validate_connection(sap, hostname, e) + SOCKADDR *sap; + char *hostname; + ENVELOPE *e; +{ +#if TCPWRAPPERS + char *host; +#endif + + if (tTd(48, 3)) + printf("validate_connection(%s, %s)\n", + hostname, anynet_ntoa(sap)); + + if (rscheck("check_relay", hostname, anynet_ntoa(sap), e) != EX_OK) + { + static char reject[BUFSIZ*2]; + extern char MsgBuf[]; + + if (tTd(48, 4)) + printf(" ... validate_connection: BAD (rscheck)\n"); + + if (strlen(MsgBuf) > 5) + { + if (isascii(MsgBuf[0]) && isdigit(MsgBuf[0]) && + isascii(MsgBuf[1]) && isdigit(MsgBuf[1]) && + isascii(MsgBuf[2]) && isdigit(MsgBuf[2])) + strcpy(reject, &MsgBuf[4]); + else + strcpy(reject, MsgBuf); + } + else + strcpy(reject, "Access denied"); + + return reject; + } + +#if TCPWRAPPERS + if (hostname[0] == '[' && hostname[strlen(hostname) - 1] == ']') + host = "unknown"; + else + host = hostname; + if (!hosts_ctl("sendmail", host, anynet_ntoa(sap), STRING_UNKNOWN)) + { + if (tTd(48, 4)) + printf(" ... validate_connection: BAD (tcpwrappers)\n"); + if (LogLevel >= 4) + sm_syslog(LOG_NOTICE, NOQID, + "tcpwrappers (%s, %s) rejection", + host, anynet_ntoa(sap)); + return "Access denied"; + } +#endif + if (tTd(48, 4)) + printf(" ... validate_connection: OK\n"); + return NULL; +} + +#endif + /* +** 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. +*/ + +#ifdef NEEDSTRTOL + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not 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 + /* +** 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. +*/ + +#ifdef 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 + /* +** 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. +*/ + +struct hostent * +sm_gethostbyname(name) + char *name; +{ + struct hostent *h; +#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)) + printf("_switch_gethostbyname_r(%s)... ", name); + h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno); +# else + extern struct hostent *__switch_gethostbyname(); + + if (tTd(61, 10)) + printf("__switch_gethostbyname(%s)... ", name); + h = __switch_gethostbyname(name); +# endif +#else + int nmaps; + char *maptype[MAXMAPSTACK]; + short mapreturn[MAXMAPACTIONS]; + char hbuf[MAXNAME]; + + if (tTd(61, 10)) + printf("gethostbyname(%s)... ", name); + h = gethostbyname(name); + if (h == NULL) + { + if (tTd(61, 10)) + printf("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) > (SIZE_T) sizeof hbuf - 1) + return NULL; + strcpy(hbuf, name); + shorten_hostname(hbuf); + + /* if it hasn't been shortened, there's no point */ + if (strcmp(hbuf, name) != 0) + { + if (tTd(61, 10)) + printf("gethostbyname(%s)... ", hbuf); + h = gethostbyname(hbuf); + } + } + } +#endif + if (tTd(61, 10)) + { + if (h == NULL) + printf("failure\n"); + else + printf("%s\n", h->h_name); + } + return h; +} + +struct hostent * +sm_gethostbyaddr(addr, len, type) + char *addr; + int len; + int type; +{ +#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) +# if SOLARIS == 20300 || SOLARIS == 203 + static struct hostent hp; + static char buf[1000]; + extern struct hostent *_switch_gethostbyaddr_r(); + + return _switch_gethostbyaddr_r(addr, len, type, &hp, buf, sizeof(buf), &h_errno); +# else + extern struct hostent *__switch_gethostbyaddr(); + + return __switch_gethostbyaddr(addr, len, type); +# endif +#else + return gethostbyaddr(addr, len, type); +#endif +} + /* +** 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 + return getpwnam(user); +#endif +} + +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 + return getpwuid(uid); +#endif +} + /* +** 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", uid); + break; + + case SSI_LOCKED: + syserr("Account has been disabled, uid = %d", uid); + break; + + case SSI_RETIRED: + syserr("Account has been retired, uid = %d", uid); + break; + + case SSI_BAD_SET_LUID: + syserr("Could not set LUID, uid = %d", uid); + break; + + case SSI_BAD_SET_PRIVS: + syserr("Could not set kernel privs, uid = %d", uid); + + default: + syserr("Unknown return code (%d) from set_secure_info(%d)", + rc, uid); + break; + } + exit(EX_NOPERM); + } +} +#endif /* SECUREWARE */ + /* +** 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 defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN +struct rtentry; +struct mbuf; +# include +# ifndef SUNOS403 +# include +# endif +# if _AIX4 >= 40300 +# undef __P +# endif +# include +#endif + +void +load_if_names() +{ +#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)) + printf("SIOCGIFNUM failed: %s\n", errstring(errno)); + numifs = -1; + } + else if (tTd(0, 42)) + printf("system has %d interfaces\n", numifs); + if (numifs < 0) +# endif + numifs = 512; + + if (numifs <= 0) + { + 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)) + printf("SIOGIFCONF failed: %s\n", errstring(errno)); + close(s); + return; + } + + /* scan the list of IP address */ + if (tTd(0, 40)) + printf("scanning for interface specific names, ifc_len=%d\n", + ifc.ifc_len); + + for (i = 0; i < ifc.ifc_len; ) + { + struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i]; + struct sockaddr *sa = &ifr->ifr_addr; + struct in_addr ia; + struct hostent *hp; +#ifdef SIOCGIFFLAGS + struct ifreq ifrf; +#endif + char ip_addr[256]; + extern char *inet_ntoa(); + +#ifdef BSD4_4_SOCKADDR + if (sa->sa_len > sizeof ifr->ifr_addr) + i += sizeof ifr->ifr_name + sa->sa_len; + else +#endif + i += sizeof *ifr; + + if (tTd(0, 20)) + printf("%s\n", anynet_ntoa((SOCKADDR *) sa)); + + if (ifr->ifr_addr.sa_family != AF_INET) + continue; + +#ifdef SIOCGIFFLAGS + bzero(&ifrf, sizeof(struct ifreq)); + strncpy(ifrf.ifr_name, ifr->ifr_name, sizeof(ifrf.ifr_name)); + ioctl(s, SIOCGIFFLAGS, (char *) &ifrf); + if (tTd(0, 41)) + printf("\tflags: %x\n", ifrf.ifr_flags); +# define IFRFREF ifrf +#else +# define IFRFREF (*ifr) +#endif + if (!bitset(IFF_UP, IFRFREF.ifr_flags)) + continue; + + /* extract IP address from the list*/ + ia = (((struct sockaddr_in *) sa)->sin_addr); + if (ia.s_addr == INADDR_ANY || ia.s_addr == INADDR_NONE) + { + message("WARNING: interface %s is UP with %s address", + ifr->ifr_name, inet_ntoa(ia)); + continue; + } + + /* save IP address in text from */ + (void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]", + sizeof ip_addr - 3, + inet_ntoa(ia)); + if (!wordinclass(ip_addr, 'w')) + { + setclass('w', ip_addr); + if (tTd(0, 4)) + printf("\ta.k.a.: %s\n", ip_addr); + } + + /* skip "loopback" interface "lo" */ + if (bitset(IFF_LOOPBACK, IFRFREF.ifr_flags)) + continue; + + /* lookup name with IP address */ + hp = sm_gethostbyaddr((char *) &ia, sizeof(ia), AF_INET); + if (hp == NULL) + { + if (LogLevel > 3) + sm_syslog(LOG_WARNING, NOQID, + "gethostbyaddr(%.100s) failed: %d\n", + inet_ntoa(ia), +#if NAMED_BIND + h_errno); +#else + -1); +#endif + continue; + } + + /* save its cname */ + if (!wordinclass((char *) hp->h_name, 'w')) + { + setclass('w', (char *) hp->h_name); + if (tTd(0, 4)) + printf("\ta.k.a.: %s\n", hp->h_name); + } + + /* save all it aliases name */ + while (*hp->h_aliases) + { + if (!wordinclass(*hp->h_aliases, 'w')) + { + setclass('w', *hp->h_aliases); + if (tTd(0, 4)) + printf("\ta.k.a.: %s\n", *hp->h_aliases); + } + hp->h_aliases++; + } + } + free(ifc.ifc_buf); + close(s); +# undef IFRFREF +#endif +} + /* +** GET_NUM_PROCS_ONLINE -- return the number of processors currently online +** +** Parameters: +** none. +** +** Returns: +** The number of processors online. +*/ + +int +get_num_procs_online() +{ + int nproc = 0; + +#if _FFR_SCALE_LA_BY_NUM_PROCS +#ifdef _SC_NPROCESSORS_ONLN + nproc = (int) sysconf(_SC_NPROCESSORS_ONLN); +#endif +#endif + if (nproc <= 0) + nproc = 1; + return nproc; +} + /* +** 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 +sm_syslog(level, id, fmt, va_alist) + int level; + const char *id; + const char *fmt; + va_dcl +#endif +{ + static char *buf = NULL; + static size_t bufsize = MAXLINE; + char *begin, *end; + int seq = 1; + int idlen; + extern int SnprfOverflow; + extern int SyslogErrno; + extern char *DoprEnd; + VA_LOCAL_DECL + extern void sm_dopr __P((char *, const char *, ...)); + + SyslogErrno = errno; + if (id == NULL) + { + id = "NOQUEUE"; + idlen = 9; + } + else if (strcmp(id, NOQID) == 0) + { + id = ""; + idlen = 0; + } + else + idlen = strlen(id + 2); +bufalloc: + if (buf == NULL) + buf = (char *) xalloc(sizeof(char) * bufsize); + + /* do a virtual vsnprintf into buf */ + VA_START(fmt); + buf[0] = 0; + DoprEnd = buf + bufsize - 1; + SnprfOverflow = 0; + sm_dopr(buf, fmt, ap); + *DoprEnd = '\0'; + VA_END; + /* end of virtual vsnprintf */ + + if (SnprfOverflow) + { + /* String too small, redo with correct size */ + bufsize += SnprfOverflow + 1; + free(buf); + buf = NULL; + goto bufalloc; + } + if ((strlen(buf) + idlen + 1) < SYSLOG_BUFSIZE) + { +#if LOG + if (*id == '\0') + syslog(level, "%s", buf); + else + syslog(level, "%s: %s", id, buf); +#else + /*XXX should do something more sensible */ + if (*id == '\0') + fprintf(stderr, "%s\n", buf); + else + fprintf(stderr, "%s: %s\n", id, buf); +#endif + return; + } + + begin = buf; + while (*begin != '\0' && + (strlen(begin) + idlen + 5) > SYSLOG_BUFSIZE) + { + char save; + + if (seq == 999) + { + /* Too many messages */ + break; + } + end = begin + SYSLOG_BUFSIZE - idlen - 12; + 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 - 12; + save = *end; + *end = 0; +#if LOG + syslog(level, "%s[%d]: %s ...", id, seq++, begin); +#else + fprintf(stderr, "%s[%d]: %s ...\n", id, seq++, begin); +#endif + *end = save; + begin = end; + } + if (seq == 999) +#if LOG + syslog(level, "%s[%d]: log terminated, too many parts", id, seq); +#else + fprintf(stderr, "%s[%d]: log terminated, too many parts\n", id, seq); +#endif + else if (*begin != '\0') +#if LOG + syslog(level, "%s[%d]: %s", id, seq, begin); +#else + fprintf(stderr, "%s[%d]: %s\n", id, seq, begin); +#endif +} + /* +** 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 +# define XCNST +# define CAST +# endif + +void +# ifdef __STDC__ +hard_syslog(int pri, XCNST char *msg, ...) +# else +hard_syslog(pri, msg, va_alist) + int pri; + XCNST char *msg; + va_dcl +# endif +{ + int i; + char buf[SYSLOG_BUFSIZE]; + VA_LOCAL_DECL; + + VA_START(msg); + vsnprintf(buf, sizeof buf, msg, ap); + VA_END; + + for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; ) + continue; +} + +# undef CAST +#endif + /* +** LOCAL_HOSTNAME_LENGTH +** +** This is required to get sendmail to compile against BIND 4.9.x +** on Ultrix. +*/ + +#if defined(ultrix) && NAMED_BIND + +# include +# if __RES >= 19931104 && __RES < 19950621 + +int +local_hostname_length(hostname) + char *hostname; +{ + int len_host, len_domain; + + if (!*_res.defdname) + res_init(); + len_host = strlen(hostname); + len_domain = strlen(_res.defdname); + if (len_host > len_domain && + (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 +#endif + /* +** Compile-Time options +*/ + +char *CompileOptions[] = +{ +#ifdef HESIOD + "HESIOD", +#endif +#if HES_GETMAILHOST + "HES_GETMAILHOST", +#endif +#ifdef LDAPMAP + "LDAPMAP", +#endif +#ifdef MAP_REGEX + "MAP_REGEX", +#endif +#if LOG + "LOG", +#endif +#if MATCHGECOS + "MATCHGECOS", +#endif +#if MIME7TO8 + "MIME7TO8", +#endif +#if MIME8TO7 + "MIME8TO7", +#endif +#if NAMED_BIND + "NAMED_BIND", +#endif +#ifdef NDBM + "NDBM", +#endif +#if NETINET + "NETINET", +#endif +#if NETINFO + "NETINFO", +#endif +#if NETISO + "NETISO", +#endif +#if NETNS + "NETNS", +#endif +#if NETUNIX + "NETUNIX", +#endif +#if NETX25 + "NETX25", +#endif +#ifdef NEWDB + "NEWDB", +#endif +#ifdef NIS + "NIS", +#endif +#ifdef NISPLUS + "NISPLUS", +#endif +#if QUEUE + "QUEUE", +#endif +#if SCANF + "SCANF", +#endif +#if SMTP + "SMTP", +#endif +#if SMTPDEBUG + "SMTPDEBUG", +#endif +#ifdef SUID_ROOT_FILES_OK + "SUID_ROOT_FILES_OK", +#endif +#if TCPWRAPPERS + "TCPWRAPPERS", +#endif +#if USERDB + "USERDB", +#endif +#if XDEBUG + "XDEBUG", +#endif +#ifdef XLA + "XLA", +#endif + NULL +}; + + +/* +** OS compile options. +*/ + +char *OsCompileOptions[] = +{ +#if BOGUS_O_EXCL + "BOGUS_O_EXCL", +#endif +#if HASFCHMOD + "HASFCHMOD", +#endif +#if HASFLOCK + "HASFLOCK", +#endif +#if HASGETDTABLESIZE + "HASGETDTABLESIZE", +#endif +#if HASGETUSERSHELL + "HASGETUSERSHELL", +#endif +#if HASINITGROUPS + "HASINITGROUPS", +#endif +#if HASLSTAT + "HASLSTAT", +#endif +#if HASSETREUID + "HASSETREUID", +#endif +#if HASSETRLIMIT + "HASSETRLIMIT", +#endif +#if HASSETSID + "HASSETSID", +#endif +#if HASSETUSERCONTEXT + "HASSETUSERCONTEXT", +#endif +#if HASSETVBUF + "HASSETVBUF", +#endif +#if HASSNPRINTF + "HASSNPRINTF", +#endif +#if HAS_ST_GEN + "HAS_ST_GEN", +#endif +#if HASSTRERROR + "HASSTRERROR", +#endif +#if HASULIMIT + "HASULIMIT", +#endif +#if HASUNAME + "HASUNAME", +#endif +#if HASUNSETENV + "HASUNSETENV", +#endif +#if HASWAITPID + "HASWAITPID", +#endif +#if IDENTPROTO + "IDENTPROTO", +#endif +#if IP_SRCROUTE + "IP_SRCROUTE", +#endif +#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL + "LOCK_ON_OPEN", +#endif +#if NEEDFSYNC + "NEEDFSYNC", +#endif +#if NOFTRUNCATE + "NOFTRUNCATE", +#endif +#if RLIMIT_NEEDS_SYS_TIME_H + "RLIMIT_NEEDS_SYS_TIME_H", +#endif +#if SAFENFSPATHCONF + "SAFENFSPATHCONF", +#endif +#if SECUREWARE + "SECUREWARE", +#endif +#if SHARE_V1 + "SHARE_V1", +#endif +#if SIOCGIFCONF_IS_BROKEN + "SIOCGIFCONF_IS_BROKEN", +#endif +#if SIOCGIFNUM_IS_BROKEN + "SIOCGIFNUM_IS_BROKEN", +#endif +#if SYS5SETPGRP + "SYS5SETPGRP", +#endif +#if SYSTEM5 + "SYSTEM5", +#endif +#if USE_SA_SIGACTION + "USE_SA_SIGACTION", +#endif +#if USE_SIGLONGJMP + "USE_SIGLONGJMP", +#endif +#if USESETEUID + "USESETEUID", +#endif + NULL +}; diff --git a/contrib/sendmail/src/conf.h b/contrib/sendmail/src/conf.h new file mode 100644 index 0000000..fd14d5a --- /dev/null +++ b/contrib/sendmail/src/conf.h @@ -0,0 +1,2440 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + * + * @(#)conf.h 8.372 (Berkeley) 6/4/98 + */ + +/* +** CONF.H -- All user-configurable parameters for sendmail +** +** Send updates to sendmail@Sendmail.ORG so they will be +** included in the next release. +*/ + +#ifdef __GNUC__ +struct rusage; /* forward declaration to get gcc to shut up in wait.h */ +#endif + +# include +# include +# include +#ifndef __QNX__ +/* in QNX this grabs bogus LOCK_* manifests */ +# include +#endif +# include +# include +# include +# include +# include +# include + +/********************************************************************** +** Table sizes, etc.... +** There shouldn't be much need to change these.... +**********************************************************************/ + +# define MAXLINE 2048 /* max line length */ +# define MAXNAME 256 /* max length of a name */ +# define MAXPV 40 /* max # of parms to mailers */ +# define MAXATOM 200 /* max atoms per address */ +# define MAXMAILERS 25 /* maximum mailers known to system */ +# 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 /* maximum SMTP line length */ +# define MAXKEY 128 /* maximum size of a database key */ +# define MEMCHUNKSIZE 1024 /* chunk size for memory allocation */ +# define MAXUSERENVIRON 100 /* max envars saved, must be >= 3 */ +# define MAXALIASDB 12 /* max # of alias databases */ +# define MAXMAPSTACK 12 /* max # of stacked or sequenced maps */ +# define MAXTOCLASS 8 /* max # of message timeout classes */ +# define MAXMIMEARGS 20 /* max args in Content-Type: */ +# define MAXMIMENESTING 20 /* max MIME multipart nesting */ +# define QUEUESEGSIZE 1000 /* increment for queue size */ +# define MAXQFNAME 20 /* max qf file name length */ +# define MACBUFSIZE 4096 /* max expanded macro buffer size */ +# define TOBUFSIZE 512 /* max buffer to hold address list */ +# define MAXSHORTSTR 203 /* max short string length */ + +/********************************************************************** +** 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 + +# ifndef NETISO +# define NETISO 0 /* do not include ISO socket support */ +# endif + +# ifndef NAMED_BIND +# define NAMED_BIND 1 /* use Berkeley Internet Domain Server */ +# endif + +# ifndef XDEBUG +# define XDEBUG 1 /* enable extended debugging */ +# endif + +# ifndef MATCHGECOS +# define MATCHGECOS 1 /* match user names from gecos field */ +# endif + +# ifndef DSN +# define DSN 1 /* include delivery status notification code */ +# endif + +# if !defined(USERDB) && (defined(NEWDB) || defined(HESIOD)) +# define USERDB 1 /* look in user database */ +# endif + +# ifndef MIME8TO7 +# define MIME8TO7 1 /* 8->7 bit MIME conversions */ +# endif + +# ifndef MIME7TO8 +# define MIME7TO8 1 /* 7->8 bit MIME conversions */ +# endif + +/********************************************************************** +** "Hard" compilation options. +** #define these if they are available; comment them out otherwise. +** These cannot be overridden from the Makefile, and should really not +** be turned off unless absolutely necessary. +**********************************************************************/ + +# define LOG 1 /* enable logging -- don't turn off */ + +/********************************************************************** +** End of site-specific configuration. +**********************************************************************/ + /* +** General "standard C" defines. +** +** These may be undone later, to cope with systems that claim to +** be Standard C but aren't. Gcc is the biggest offender -- it +** doesn't realize that the library is part of the language. +** +** Life would be much easier if we could get rid of this sort +** of bozo problems. +*/ + +#ifdef __STDC__ +# define HASSETVBUF 1 /* we have setvbuf(3) in libc */ +#endif + +/* +** Assume you have standard calls; can be #undefed below if necessary. +*/ + +# define HASLSTAT 1 /* has lstat(2) call */ + /********************************************************************** +** Operating system configuration. +** +** Unless you are porting to a new OS, you shouldn't have to +** change these. +**********************************************************************/ + +/* +** HP-UX -- tested for 8.07, 9.00, and 9.01. +** +** If V4FS is defined, compile for HP-UX 10.0. +** 11.x support from Richard Allen . +*/ + +#ifdef __hpux + /* common definitions for HP-UX 9.x and 10.x */ +# undef m_flags /* conflict between Berkeley DB 1.85 db.h & sys/sysmacros.h on HP 300 */ +# define SYSTEM5 1 /* include all the System V defines */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# define USESETEUID 1 /* has useable seteuid(2) call */ +# define BOGUS_O_EXCL 1 /* exclusive open follows symlinks */ +# define seteuid(e) setresuid(-1, e, -1) +# define IP_SRCROUTE 1 /* can check IP source routing */ +# define LA_TYPE LA_HPUX +# define SPT_TYPE SPT_PSTAT +# define SFS_TYPE SFS_VFS /* use statfs() implementation */ +# define GIDSET_T gid_t +# ifndef HASGETUSERSHELL +# define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ +# endif +# ifndef HPUX11 +# define syslog hard_syslog +# endif +# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */ + +# ifdef V4FS + /* HP-UX 10.x */ +# define _PATH_UNIX "/stand/vmunix" +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" +# endif +# ifndef IDENTPROTO +# define IDENTPROTO 1 /* TCP/IP implementation fixed in 10.0 */ +# endif + +# else + /* HP-UX 9.x */ +# define _PATH_UNIX "/hp-ux" +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* TCP/IP implementation is broken */ +# endif +# ifdef __STDC__ +extern void hard_syslog(int, char *, ...); +# else +extern void hard_syslog(); +# endif +# define FDSET_CAST (int *) /* cast for fd_set parameters to select */ +# endif + +#endif + + +/* +** IBM AIX 4.x +*/ + +#ifdef _AIX4 +# define _AIX3 1 /* pull in AIX3 stuff */ +# define USESETEUID 1 /* seteuid(2) works */ +# define TZ_TYPE TZ_NAME /* use tzname[] vector */ +# define SOCKOPT_LEN_T size_t /* arg#5 to getsockopt */ +# if _AIX4 >= 40200 +# define HASSETREUID 1 /* setreuid(2) works as of AIX 4.2 */ +# define SOCKADDR_LEN_T size_t /* e.g., arg#3 to accept, getsockname */ +# endif +# if defined(_ILS_MACROS) /* IBM versions aren't side-effect clean */ +# undef isascii +# define isascii(c) !(c & ~0177) +# undef isdigit +# define isdigit(__a) (_IS(__a,_ISDIGIT)) +# undef isspace +# define isspace(__a) (_IS(__a,_ISSPACE)) +# endif +#endif + + +/* +** IBM AIX 3.x -- actually tested for 3.2.3 +*/ + +#ifdef _AIX3 +# include +# include /* to get byte order */ +# include +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASUNAME 1 /* use System V uname(2) system call */ +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ +# define GIDSET_T gid_t +# define SFS_TYPE SFS_STATFS /* use statfs() impl */ +# define SPT_PADCHAR '\0' /* pad process title with nulls */ +# define LA_TYPE LA_INT +# define FSHIFT 16 +# define LA_AVENRUN "avenrun" +#endif + + +/* +** IBM AIX 2.2.1 -- actually tested for osupdate level 2706+1773 +** +** From Mark Whetzel . +*/ + +#ifdef AIX /* AIX/RT compiler pre-defines this */ +# include +# include /* AIX/RT resource.h does NOT include this */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASUNAME 1 /* use System V uname(2) system call */ +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# define HASFCHMOD 0 /* does not have fchmod(2) syscall */ +# define HASSETREUID 1 /* use setreuid(2) -lbsd system call */ +# define HASSETVBUF 1 /* use setvbuf(2) system call */ +# define HASSETRLIMIT 0 /* does not have setrlimit call */ +# define HASFLOCK 0 /* does not have flock call - use fcntl */ +# define HASULIMIT 1 /* use ulimit instead of setrlimit call */ +# define NEEDGETOPT 1 /* Do we need theirs or ours */ +# define SYS5SETPGRP 1 /* don't have setpgid on AIX/RT */ +# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ +# define BSD4_3 1 /* NOT bsd 4.4 or posix signals */ +# define GIDSET_T int +# define SFS_TYPE SFS_STATFS /* use statfs() impl */ +# define SPT_PADCHAR '\0' /* pad process title with nulls */ +# define LA_TYPE LA_SUBR /* use our ported loadavgd daemon */ +# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ +# define ARBPTR_T int * +# define void int +typedef int pid_t; +/* RTisms for BSD compatibility, specified in the Makefile + define BSD 1 + define BSD_INCLUDES 1 + define BSD_REMAP_SIGNAL_TO_SIGVEC + RTisms needed above */ +/* make this sendmail in a completely different place */ +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/local/newmail/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/usr/local/newmail/sendmail.pid" +# endif +#endif + + +/* +** Silicon Graphics IRIX +** +** Compiles on 4.0.1. +** +** Use IRIX64 instead of IRIX for 64-bit IRIX (6.0). +** Use IRIX5 instead of IRIX for IRIX 5.x. +** +** This version tries to be adaptive using _MIPS_SIM: +** _MIPS_SIM == _ABIO32 (= 1) Abi: -32 on IRIX 6.2 +** _MIPS_SIM == _ABIN32 (= 2) Abi: -n32 on IRIX 6.2 +** _MIPS_SIM == _ABI64 (= 3) Abi: -64 on IRIX 6.2 +** +** _MIPS_SIM is 1 also on IRIX 5.3 +** +** IRIX64 changes from Mark R. Levinson . +** IRIX5 changes from Kari E. Hurtta . +** Adaptive changes from Kari E. Hurtta . +*/ + +#if defined(__sgi) +# ifndef IRIX +# define IRIX +# endif +# if _MIPS_SIM > 0 && !defined(IRIX5) +# define IRIX5 /* IRIX5 or IRIX6 */ +# endif +# if _MIPS_SIM > 1 && !defined(IRIX6) && !defined(IRIX64) +# define IRIX6 /* IRIX6 */ +# endif + +#endif + +#ifdef IRIX +# define SYSTEM5 1 /* this is a System-V derived system */ +# define HASSETREUID 1 /* has setreuid(2) call */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# define IP_SRCROUTE 1 /* can check IP source routing */ +# define setpgid BSDsetpgrp +# define GIDSET_T gid_t +# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ +# define SFS_BAVAIL f_bfree /* alternate field name */ +# ifdef IRIX6 +# define STAT64 1 +# define QUAD_T unsigned long long +# define LA_TYPE LA_IRIX6 /* figure out at run time */ +# define SAFENFSPATHCONF 0 /* pathconf(2) lies on NFS filesystems */ +# define SYSLOG_BUFSIZE 512 +# else +# define LA_TYPE LA_INT + +# ifdef IRIX64 +# define STAT64 1 +# define QUAD_T unsigned long long +# define NAMELISTMASK 0x7fffffffffffffff /* mask for nlist() values */ +# else +# define STAT64 0 +# define NAMELISTMASK 0x7fffffff /* mask for nlist() values */ +# endif +# endif +# if defined(IRIX64) || defined(IRIX5) || defined(IRIX6) +# include +# include +# define ARGV_T char *const * +# define HASSETRLIMIT 1 /* has setrlimit(2) syscall */ +# define HASGETDTABLESIZE 1 /* has getdtablesize(2) syscall */ +# define HASSTRERROR 1 /* has strerror(3) */ +# else +# define ARGV_T const char ** +# define WAITUNION 1 /* use "union wait" as wait argument type */ +# endif +#endif + + +/* +** SunOS and Solaris +** +** Tested on SunOS 4.1.x (a.k.a. Solaris 1.1.x) and +** Solaris 2.4 (a.k.a. SunOS 5.4). +*/ + +#if defined(sun) && !defined(BSD) + +# include +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASUNAME 1 /* use System V uname(2) system call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# define IP_SRCROUTE 1 /* can check IP source routing */ +# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */ + +# ifdef SOLARIS_2_3 +# define SOLARIS 20300 /* for back compat only -- use -DSOLARIS=20300 */ +# endif + +# if defined(NOT_SENDMAIL) && !defined(SOLARIS) && defined(sun) && (defined(__svr4__) || defined(__SVR4)) +# define SOLARIS 1 /* unknown Solaris version */ +# endif + +# ifdef SOLARIS + /* Solaris 2.x (a.k.a. SunOS 5.x) */ +# ifndef __svr4__ +# define __svr4__ /* use all System V Releae 4 defines below */ +# endif +# define GIDSET_T gid_t +# define USE_SA_SIGACTION 1 /* use sa_sigaction field */ +# ifndef _PATH_UNIX +# define _PATH_UNIX "/dev/ksyms" +# endif +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" +# endif +# ifndef _PATH_HOSTS +# define _PATH_HOSTS "/etc/inet/hosts" +# endif +# ifndef SYSLOG_BUFSIZE +# define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */ +# endif +# ifndef TZ_TYPE +# define TZ_TYPE TZ_TZNAME +# endif +# if SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) +# define USESETEUID 1 /* seteuid works as of 2.3 */ +# endif +# if SOLARIS >= 20500 || (SOLARIS < 10000 && SOLARIS >= 205) +# define HASSETREUID 1 /* setreuid works as of 2.5 */ +# if SOLARIS < 207 || (SOLARIS > 10000 && SOLARIS < 20700) +# ifndef LA_TYPE +# define LA_TYPE LA_KSTAT /* use kstat(3k) -- may work in < 2.5 */ +# endif +# endif +# endif +# if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) +# define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ +# endif +# if SOLARIS >= 20700 || (SOLARIS < 10000 && SOLARIS >= 207) +# ifndef LA_TYPE +# define LA_TYPE LA_SUBR /* getloadavg(3c) appears in 2.7 */ +# endif +# define HASGETUSERSHELL 1 /* getusershell(3c) bug fixed in 2.7 */ +# endif +# ifndef HASGETUSERSHELL +# define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps pre-2.7 */ +# endif + +# else + /* SunOS 4.0.3 or 4.1.x */ +# define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */ +# define HASSETREUID 1 /* has setreuid(2) call */ +# ifndef HASFLOCK +# define HASFLOCK 1 /* has flock(2) call */ +# endif +# define SFS_TYPE SFS_VFS /* use statfs() implementation */ +# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ +# include +# include +# ifdef __GNUC__ +# define strtoul strtol /* gcc library bogosity */ +# endif + +# ifdef SUNOS403 + /* special tweaking for SunOS 4.0.3 */ +# include +# define BSD4_3 1 /* 4.3 BSD-based */ +# define NEEDSTRSTR 1 /* need emulation of strstr(3) routine */ +# define WAITUNION 1 /* use "union wait" as wait argument type */ +# undef WIFEXITED +# undef WEXITSTATUS +# undef HASUNAME +# define setpgid setpgrp +# define MODE_T int +typedef int pid_t; +extern char *getenv(); + +# else + /* 4.1.x specifics */ +# define HASSETSID 1 /* has Posix setsid(2) call */ +# define HASSETVBUF 1 /* we have setvbuf(3) in libc */ + +# endif +# endif + +# ifndef LA_TYPE +# define LA_TYPE LA_INT +# endif + +#endif /* sun && !BSD */ + +/* +** DG/UX +** +** Tested on 5.4.2 and 5.4.3. Use DGUX_5_4_2 to get the +** older support. +** 5.4.3 changes from Mark T. Robinson . +*/ + +#ifdef DGUX_5_4_2 +# define DGUX 1 +#endif + +#ifdef DGUX +# define SYSTEM5 1 +# define LA_TYPE LA_DGUX +# define HASSETREUID 1 /* has setreuid(2) call */ +# define HASUNAME 1 /* use System V uname(2) system call */ +# define HASSETSID 1 /* has Posix setsid(2) call */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define IP_SRCROUTE 0 /* does not have */ +# define HASGETUSERSHELL 0 /* does not have getusershell(3) */ +# define HASSNPRINTF 1 /* has snprintf(3) */ +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* TCP/IP implementation is broken */ +# endif +# define SPT_TYPE SPT_NONE /* don't use setproctitle */ +# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ + +/* these include files must be included early on DG/UX */ +# include +# include + +/* compiler doesn't understand const? */ +# define const + +# ifdef DGUX_5_4_2 +# define inet_addr dgux_inet_addr +extern long dgux_inet_addr(); +# endif +#endif + + +/* +** Digital Ultrix 4.2A or 4.3 +** +** Apparently, fcntl locking is broken on 4.2A, in that locks are +** not dropped when the process exits. This causes major problems, +** so flock is the only alternative. +*/ + +#ifdef ultrix +# define HASSETREUID 1 /* has setreuid(2) call */ +# define HASUNSETENV 1 /* has unsetenv(3) call */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASUNAME 1 /* use System V uname(2) system call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# ifndef HASFLOCK +# define HASFLOCK 1 /* has flock(2) call */ +# endif +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# ifndef BROKEN_RES_SEARCH +# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */ +# endif +# ifdef vax +# define LA_TYPE LA_FLOAT +# else +# define LA_TYPE LA_INT +# define LA_AVENRUN "avenrun" +# endif +# define SFS_TYPE SFS_MOUNT /* use statfs() impl */ +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* pre-4.4 TCP/IP implementation is broken */ +# endif +# define SYSLOG_BUFSIZE 256 +#endif + + +/* +** OSF/1 for KSR. +** +** Contributed by Todd C. Miller +*/ + +#ifdef __ksr__ +# define __osf__ 1 /* get OSF/1 defines below */ +# ifndef TZ_TYPE +# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ +# endif +#endif + + +/* +** OSF/1 for Intel Paragon. +** +** Contributed by Jeff A. Earickson +** of Intel Scalable Systems Divison. +*/ + +#ifdef __PARAGON__ +# define __osf__ 1 /* get OSF/1 defines below */ +# ifndef TZ_TYPE +# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ +# endif +# define GIDSET_T gid_t +# define MAXNAMLEN NAME_MAX +#endif + + +/* +** OSF/1 (tested on Alpha) -- now known as Digital UNIX. +** +** Tested for 3.2 and 4.0. +*/ + +#ifdef __osf__ +# define HASUNSETENV 1 /* has unsetenv(3) call */ +# define USESETEUID 1 /* has useable seteuid(2) call */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# define IP_SRCROUTE 1 /* can check IP source routing */ +# define HAS_ST_GEN 1 /* has st_gen field in stat struct */ +# ifndef HASFLOCK +# define HASFLOCK 1 /* has flock(2) call */ +# endif +# define LA_TYPE LA_ALPHAOSF +# define SFS_TYPE SFS_STATVFS /* use statfs() impl */ +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/var/adm/sendmail/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/var/run/sendmail.pid" +# endif +# define bcopy(s, d, l) (memmove((d), (s), (l))) +# define bzero(d, l) (memset((d), '\0', (l))) +# define bcmp(s, d, l) (memcmp((s), (d), (l))) +#endif + + +/* +** NeXTstep +*/ + +#ifdef NeXT +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define NEEDPUTENV 2 /* need putenv(3) call; no setenv(3) call */ +# ifndef HASFLOCK +# define HASFLOCK 1 /* has flock(2) call */ +# endif +# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ +# define WAITUNION 1 /* use "union wait" as wait argument type */ +# define UID_T int /* compiler gripes on uid_t */ +# define GID_T int /* ditto for gid_t */ +# define MODE_T int /* and mode_t */ +# define setpgid setpgrp +# ifndef NOT_SENDMAIL +# define sleep sleepX +# endif +# ifndef LA_TYPE +# define LA_TYPE LA_MACH +# endif +# define SFS_TYPE SFS_VFS /* use statfs() implementation */ +# ifndef _POSIX_SOURCE +typedef int pid_t; +# undef WEXITSTATUS +# undef WIFEXITED +# endif +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/etc/sendmail/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/etc/sendmail/sendmail.pid" +# endif + +# ifdef TCPWRAPPERS +# ifndef HASUNSETENV +# define HASUNSETENV 1 +# endif +# undef NEEDPUTENV +# endif + +#endif + + +/* +** 4.4 BSD +** +** See also BSD defines. +*/ + +#if defined(BSD4_4) && !defined(__bsdi__) && !defined(__GNU__) +# include +# define HASUNSETENV 1 /* has unsetenv(3) call */ +# define USESETEUID 1 /* has useable seteuid(2) call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ +# define HASSTRERROR 1 /* has strerror(3) */ +# define HAS_ST_GEN 1 /* has st_gen field in stat struct */ +# include +# define ERRLIST_PREDEFINED /* don't declare sys_errlist */ +# define BSD4_4_SOCKADDR /* has sa_len */ +# define NEED_PRINTF_PERCENTQ 1 /* doesn't have %lld */ +# define NETLINK 1 /* supports AF_LINK */ +# ifndef LA_TYPE +# define LA_TYPE LA_SUBR +# endif +# define SFS_TYPE SFS_MOUNT /* use statfs() impl */ +# define SPT_TYPE SPT_PSSTRINGS /* use PS_STRINGS pointer */ +#endif + + +/* +** BSD/OS (was BSD/386) (all versions) +** From Tony Sanders, BSDI +*/ + +#ifdef __bsdi__ +# include +# define HASUNSETENV 1 /* has the unsetenv(3) call */ +# define HASSETSID 1 /* has the setsid(2) POSIX syscall */ +# define USESETEUID 1 /* has useable seteuid(2) call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ +# define HASUNAME 1 /* has uname(2) syscall */ +# define HASSTRERROR 1 /* has strerror(3) */ +# define HAS_ST_GEN 1 /* has st_gen field in stat struct */ +# include +# define ERRLIST_PREDEFINED /* don't declare sys_errlist */ +# define BSD4_4_SOCKADDR /* has sa_len */ +# define NETLINK 1 /* supports AF_LINK */ +# define SFS_TYPE SFS_MOUNT /* use statfs() impl */ +# ifndef LA_TYPE +# define LA_TYPE LA_SUBR +# endif +# define GIDSET_T gid_t +# define QUAD_T quad_t +# if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199312 + /* version 1.1 or later */ +# undef SPT_TYPE +# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ +# else + /* version 1.0 or earlier */ +# define SPT_PADCHAR '\0' /* pad process title with nulls */ +# endif +# if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199701 /* on 3.x */ +# define HASSETUSERCONTEXT 1 /* has setusercontext */ +# endif +#endif + + +/* +** QNX 4.2x +** Contributed by Glen McCready . +** +** Should work with all versions of QNX. +*/ + +#if defined(__QNX__) +# include +# include +# undef NGROUPS_MAX +# define HASSETSID 1 /* has the setsid(2) POSIX syscall */ +# define USESETEUID 1 /* has useable seteuid(2) call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */ +# define HASSETREUID 1 /* has setreuid(2) call */ +# define HASSTRERROR 1 /* has strerror(3) */ +# define HASFLOCK 0 +# undef HASINITGROUPS /* has initgroups(3) call */ +# define NEEDGETOPT 1 /* use sendmail's getopt */ +# define IP_SRCROUTE 1 /* can check IP source routing */ +# define TZ_TYPE TZ_TMNAME /* use tmname variable */ +# define GIDSET_T gid_t +# define LA_TYPE LA_ZERO +# define SFS_TYPE SFS_NONE +# define SPT_TYPE SPT_REUSEARGV +# define SPT_PADCHAR '\0' /* pad process title with nulls */ +# define HASGETUSERSHELL 0 +# define E_PSEUDOBASE 512 +# define bcopy(s, d, l) (memmove((d), (s), (l))) +# define bzero(d, l) (memset((d), '\0', (l))) +# define bcmp(s, d, l) (memcmp((s), (d), (l))) +# define _FILE_H_INCLUDED +#endif + + +/* +** FreeBSD / NetBSD / OpenBSD (all architectures, all versions) +** +** 4.3BSD clone, closer to 4.4BSD for FreeBSD 1.x and NetBSD 0.9x +** 4.4BSD-Lite based for FreeBSD 2.x and NetBSD 1.x +** +** See also BSD defines. +*/ + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# include +# define HASUNSETENV 1 /* has unsetenv(3) call */ +# define HASSETSID 1 /* has the setsid(2) POSIX syscall */ +# define USESETEUID 1 /* has useable seteuid(2) call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ +# define HASUNAME 1 /* has uname(2) syscall */ +# define HASSTRERROR 1 /* has strerror(3) */ +# define HAS_ST_GEN 1 /* has st_gen field in stat struct */ +# define NEED_PRINTF_PERCENTQ 1 /* doesn't have %lld */ +# include +# define ERRLIST_PREDEFINED /* don't declare sys_errlist */ +# define BSD4_4_SOCKADDR /* has sa_len */ +# define NETLINK 1 /* supports AF_LINK */ +# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */ +# define GIDSET_T gid_t +# define QUAD_T unsigned long long +# ifndef LA_TYPE +# define LA_TYPE LA_SUBR +# endif +# define SFS_TYPE SFS_MOUNT /* use statfs() impl */ +# if defined(__NetBSD__) && (NetBSD > 199307 || NetBSD0_9 > 1) +# undef SPT_TYPE +# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ +# endif +# if defined(__FreeBSD__) +# undef SPT_TYPE +# if __FreeBSD__ == 2 +# include /* and this works */ +# if __FreeBSD_version >= 199512 /* 2.2-current right now */ +# include +# define SPT_TYPE SPT_BUILTIN +# endif +# endif +# ifndef SPT_TYPE +# define SPT_TYPE SPT_REUSEARGV +# define SPT_PADCHAR '\0' /* pad process title with nulls */ +# endif +# endif +# if defined(__OpenBSD__) +# undef SPT_TYPE +# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ +# endif +#endif + + + +/* +** Mach386 +** +** For mt Xinu's Mach386 system. +*/ + +#if defined(MACH) && defined(i386) && !defined(__GNU__) +# define MACH386 1 +# define HASUNSETENV 1 /* has unsetenv(3) call */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# ifndef HASFLOCK +# define HASFLOCK 1 /* has flock(2) call */ +# endif +# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ +# define NEEDSTRTOL 1 /* need the strtol() function */ +# define setpgid setpgrp +# ifndef LA_TYPE +# define LA_TYPE LA_FLOAT +# endif +# define SFS_TYPE SFS_VFS /* use statfs() implementation */ +# undef HASSETVBUF /* don't actually have setvbuf(3) */ +# undef WEXITSTATUS +# undef WIFEXITED +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/etc/sendmail.pid" +# endif +#endif + + + +/* +** GNU OS (hurd) +** Largely BSD & posix compatible. +** Port contributed by Miles Bader . +*/ + +#ifdef __GNU_HURD__ +# define SIOCGIFCONF_IS_BROKEN 1 +# define IP_SRCROUTE 0 +# define HASFCHMOD 1 +# define HASFLOCK 1 +# define HASUNAME 1 +# define HASUNSETENV 1 +# define HASSETSID 1 +# define HASINITGROUPS 1 +# define HASSETVBUF 1 +# define HASSETREUID 1 +# define USESETEUID 1 +# define HASLSTAT 1 +# define HASSETRLIMIT 1 +# define HASWAITPID 1 +# define HASGETDTABLESIZE 1 +# define HASSTRERROR 1 +/* # define NEEDGETOPT 1 */ +# define HASGETUSERSHELL 1 +# define ERRLIST_PREDEFINED 1 +# define BSD4_4_SOCKADDR 1 +# define GIDSET_T gid_t +# define LA_TYPE LA_MACH + +/* GNU uses mach[34], which renames some rpcs from mach2.x. */ +# define host_self mach_host_self +# define SFS_TYPE SFS_STATFS +# define SPT_TYPE SPT_CHANGEARGV + +/* GNU has no MAXPATHLEN; ideally the code should be changed to not use it. */ +# define MAXPATHLEN 2048 + +/* Define device num frobbing macros. */ +# define major(x) ((x)>>8) +# define minor(x) ((x)&0xFF) +#endif /* GNU */ + +/* +** 4.3 BSD -- this is for very old systems +** +** Should work for mt Xinu MORE/BSD and Mips UMIPS-BSD 2.1. +** +** You'll also have to install a new resolver library. +** I don't guarantee that support for this environment is complete. +*/ + +#if defined(oldBSD43) || defined(MORE_BSD) || defined(umipsbsd) +# define NEEDVPRINTF 1 /* need a replacement for vprintf(3) */ +# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ +# define ARBPTR_T char * +# define setpgid setpgrp +# ifndef LA_TYPE +# define LA_TYPE LA_FLOAT +# endif +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* TCP/IP implementation is broken */ +# endif +# undef WEXITSTATUS +# undef WIFEXITED +typedef short pid_t; +extern int errno; +#endif + + +/* +** SCO Unix +** +** This includes three parts: +** +** The first is for SCO OpenServer 5. +** (Contributed by Keith Reynolds ). +** +** SCO OpenServer 5 has a compiler version number macro, +** which we can use to figure out what version we're on. +** This may have to change in future releases. +** +** The second is for SCO UNIX 3.2v4.2/Open Desktop 3.0. +** (Contributed by Philippe Brand ). +** +** The third is for SCO UNIX 3.2v4.0/Open Desktop 2.0 and earlier. +*/ + +/* SCO OpenServer 5 */ +#if _SCO_DS >= 1 +# include +# define SIOCGIFNUM_IS_BROKEN 1 /* SIOCGIFNUM returns bogus value */ +# define HASSNPRINTF 1 /* has snprintf(3) call */ +# define HASFCHMOD 1 /* has fchmod(2) call */ +# define HASSETRLIMIT 1 /* has setrlimit(2) call */ +# define USESETEUID 1 /* has seteuid(2) call */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */ +# define RLIMIT_NEEDS_SYS_TIME_H 1 +# ifndef LA_TYPE +# define LA_TYPE LA_DEVSHORT +# endif +# define _PATH_AVENRUN "/dev/table/avenrun" +# ifndef _SCO_unix_4_2 +# define _SCO_unix_4_2 +# else +# define SOCKADDR_LEN_T size_t /* e.g., arg#3 to accept, getsockname */ +# define SOCKOPT_LEN_T size_t /* arg#5 to getsockopt */ +# endif +#endif + +/* SCO UNIX 3.2v4.2/Open Desktop 3.0 */ +#ifdef _SCO_unix_4_2 +# define _SCO_unix_ +# define HASSETREUID 1 /* has setreuid(2) call */ +#endif + +/* SCO UNIX 3.2v4.0 Open Desktop 2.0 and earlier */ +#ifdef _SCO_unix_ +# include /* needed for IP_SRCROUTE */ +# define SYSTEM5 1 /* include all the System V defines */ +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# define NOFTRUNCATE 0 /* has (simulated) ftruncate call */ +# define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */ +# define MAXPATHLEN PATHSIZE +# define SFS_TYPE SFS_4ARGS /* use 4-arg impl */ +# define SFS_BAVAIL f_bfree /* alternate field name */ +# define SPT_TYPE SPT_SCO /* write kernel u. area */ +# define TZ_TYPE TZ_TM_NAME /* use tm->tm_name */ +# define UID_T uid_t +# define GID_T gid_t +# define GIDSET_T gid_t +# define _PATH_UNIX "/unix" +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/etc/sendmail.pid" +# endif + +/* stuff fixed in later releases */ +# ifndef _SCO_unix_4_2 +# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ +# endif + +# ifndef _SCO_DS +# define ftruncate chsize /* use chsize(2) to emulate ftruncate */ +# define NEEDFSYNC 1 /* needs the fsync(2) call stub */ +# define NETUNIX 0 /* no unix domain socket support */ +# define LA_TYPE LA_SHORT +# endif + +#endif + + +/* +** ISC (SunSoft) Unix. +** +** Contributed by J.J. Bailey +*/ + +#ifdef ISC_UNIX +# include +# include /* needed for IP_SRCROUTE */ +# include +# define SYSTEM5 1 /* include all the System V defines */ +# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# define HASSETREUID 1 /* has setreuid(2) call */ +# define NEEDFSYNC 1 /* needs the fsync(2) call stub */ +# define NETUNIX 0 /* no unix domain socket support */ +# define MAXPATHLEN 1024 +# define LA_TYPE LA_SHORT +# define SFS_TYPE SFS_STATFS /* use statfs() impl */ +# define SFS_BAVAIL f_bfree /* alternate field name */ +# define _PATH_UNIX "/unix" +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/etc/sendmail.pid" +# endif + +#endif + + +/* +** Altos System V (5.3.1) +** Contributed by Tim Rice . +*/ + +#ifdef ALTOS_SYSTEM_V +# include +# include +# define SYSTEM5 1 /* include all the System V defines */ +# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# define WAITUNION 1 /* use "union wait" as wait argument type */ +# define NEEDFSYNC 1 /* no fsync(2) in system library */ +# define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ +# define NOFTRUNCATE 1 /* do not have ftruncate(2) */ +# define MAXPATHLEN PATH_MAX +# define LA_TYPE LA_SHORT +# define SFS_TYPE SFS_STATFS /* use statfs() impl */ +# define SFS_BAVAIL f_bfree /* alternate field name */ +# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ +# define NETUNIX 0 /* no unix domain socket support */ +# undef WIFEXITED +# undef WEXITSTATUS +# define strtoul strtol /* gcc library bogosity */ + +typedef unsigned short uid_t; +typedef unsigned short gid_t; +typedef short pid_t; +typedef unsigned long mode_t; + +/* some stuff that should have been in the include files */ +# include +extern char *malloc(); +extern struct passwd *getpwent(); +extern struct passwd *getpwnam(); +extern struct passwd *getpwuid(); +extern char *getenv(); +extern struct group *getgrgid(); +extern struct group *getgrnam(); + +#endif + + +/* +** ConvexOS 11.0 and later +** +** "Todd C. Miller" claims this +** works on 9.1 as well. +** +** ConvexOS 11.5 and later, should work on 11.0 as defined. +** For pre-ConvexOOS 11.0, define NEEDGETOPT, undef IDENTPROTO +** +** Eric Schnoebelen (eric@cirr.com) For CONVEX Computer Corp. +** (now the CONVEX Technologies Center of Hewlett Packard) +*/ + +#ifdef _CONVEX_SOURCE +# define HASGETDTABLESIZE 1 /* has getdtablesize(2) */ +# define HASINITGROUPS 1 /* has initgroups(3) */ +# define HASUNAME 1 /* use System V uname(2) system call */ +# define HASSETSID 1 /* has POSIX setsid(2) call */ +# define HASUNSETENV 1 /* has unsetenv(3) */ +# define HASFLOCK 1 /* has flock(2) */ +# define HASSETRLIMIT 1 /* has setrlimit(2) */ +# define HASSETREUID 1 /* has setreuid(2) */ +# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_error=0 */ +# define NEEDPUTENV 1 /* needs putenv (written in terms of setenv) */ +# define NEEDGETOPT 0 /* need replacement for getopt(3) */ +# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ +# define LA_TYPE LA_FLOAT +# define SFS_TYPE SFS_VFS /* use statfs() implementation */ +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +# ifndef S_IREAD +# define S_IREAD _S_IREAD +# define S_IWRITE _S_IWRITE +# define S_IEXEC _S_IEXEC +# define S_IFMT _S_IFMT +# define S_IFCHR _S_IFCHR +# define S_IFBLK _S_IFBLK +# endif +# ifndef TZ_TYPE +# define TZ_TYPE TZ_TIMEZONE +# endif +# ifndef IDENTPROTO +# define IDENTPROTO 1 +# endif +# ifndef SHARE_V1 +# define SHARE_V1 1 /* version 1 of the fair share scheduler */ +# endif +# if !defined(__GNUC__ ) +# define UID_T int /* GNUC gets it right, ConvexC botches */ +# define GID_T int /* GNUC gets it right, ConvexC botches */ +# endif +# if SECUREWARE +# define FORK fork /* SecureWare wants the real fork! */ +# else +# define FORK vfork /* the rest of the OS versions don't care */ +# endif +#endif + + +/* +** RISC/os 4.52 +** +** Gives a ton of warning messages, but otherwise compiles. +*/ + +#ifdef RISCOS + +# define HASUNSETENV 1 /* has unsetenv(3) call */ +# ifndef HASFLOCK +# define HASFLOCK 1 /* has flock(2) call */ +# endif +# define WAITUNION 1 /* use "union wait" as wait argument type */ +# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ +# define NEEDPUTENV 1 /* need putenv(3) call */ +# define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ +# define SFS_TYPE SFS_VFS /* use statfs() implementation */ +# define LA_TYPE LA_INT +# define LA_AVENRUN "avenrun" +# define _PATH_UNIX "/unix" +# undef WIFEXITED + +# define setpgid setpgrp + +extern int errno; +typedef int pid_t; +# define SIGFUNC_DEFINED +# define SIGFUNC_RETURN (0) +# define SIGFUNC_DECL int +typedef int (*sigfunc_t)(); +extern char *getenv(); +extern void *malloc(); + +/* added for RISC/os 4.01...which is dumber than 4.50 */ +# ifdef RISCOS_4_0 +# ifndef ARBPTR_T +# define ARBPTR_T char * +# endif +# undef HASFLOCK +# define HASFLOCK 0 +# endif /* RISCOS_4_0 */ + +# include + +#endif + + +/* +** Linux 0.99pl10 and above... +** +** Thanks to, in reverse order of contact: +** +** John Kennedy +** Andrew Pam +** Florian La Roche +** Karl London +** +** Last compiled against: [06/10/96 @ 09:21:40 PM (Monday)] +** sendmail 8.8-a4 named bind-4.9.4-T4B db-1.85 +** gcc 2.7.2 libc-5.3.12 linux 2.0.0 +** +** NOTE: Override HASFLOCK as you will but, as of 1.99.6, mixed-style +** file locking is no longer allowed. In particular, make sure +** your DBM library and sendmail are both using either flock(2) +** *or* fcntl(2) file locking, but not both. +*/ + +#ifdef __linux__ +# define BSD 1 /* include BSD defines */ +# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ +# define HASUNAME 1 /* use System V uname(2) system call */ +# define HASUNSETENV 1 /* has unsetenv(3) call */ +# ifndef HASSNPRINTF +# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ +# endif +# define ERRLIST_PREDEFINED /* don't declare sys_errlist */ +# define GIDSET_T gid_t /* from */ +# define HASGETUSERSHELL 0 /* getusershell(3) broken in Slackware 2.0 */ +# define IP_SRCROUTE 0 /* linux <= 1.2.8 doesn't support IP_OPTIONS */ +# define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */ +# ifndef HASFLOCK +# include +# if LINUX_VERSION_CODE < 66399 +# define HASFLOCK 0 /* flock(2) is broken after 0.99.13 */ +# else +# define HASFLOCK 1 /* flock(2) fixed after 1.3.95 */ +# endif +# endif +# ifndef LA_TYPE +# define LA_TYPE LA_PROCSTR +# endif +# define SFS_TYPE SFS_VFS /* use statfs() impl */ +# define SPT_PADCHAR '\0' /* pad process title with nulls */ +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/var/run/sendmail.pid" +# endif +# define TZ_TYPE TZ_TNAME +# include +# undef atol /* wounded in */ +#endif + + +/* +** DELL SVR4 Issue 2.2, and others +** From Kimmo Suominen +** +** It's on #ifdef DELL_SVR4 because Solaris also gets __svr4__ +** defined, and the definitions conflict. +** +** Peter Wemm claims that the setreuid +** trick works on DELL 2.2 (SVR4.0/386 version 4.0) and ESIX 4.0.3A +** (SVR4.0/386 version 3.0). +*/ + +#ifdef DELL_SVR4 + /* no changes necessary */ + /* see general __svr4__ defines below */ +#endif + + +/* +** Apple A/UX 3.0 +*/ + +#ifdef _AUX_SOURCE +# include +# define BSD /* has BSD routines */ +# define HASSETRLIMIT 0 /* ... but not setrlimit(2) */ +# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */ +# define BOGUS_O_EXCL 1 /* exclusive open follows symlinks */ +# define HASUNAME 1 /* use System V uname(2) system call */ +# define HASFCHMOD 1 /* has fchmod(2) syscall */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASSETVBUF 1 /* has setvbuf(3) in libc */ +# define HASSTRERROR 1 /* has strerror(3) */ +# define SIGFUNC_DEFINED /* sigfunc_t already defined */ +# define SIGFUNC_RETURN /* POSIX-mode */ +# define SIGFUNC_DECL void /* POSIX-mode */ +# define ERRLIST_PREDEFINED 1 +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* TCP/IP implementation is broken */ +# endif +# ifndef LA_TYPE +# define LA_TYPE LA_INT +# define FSHIFT 16 +# endif +# define LA_AVENRUN "avenrun" +# define SFS_TYPE SFS_VFS /* use statfs() implementation */ +# define TZ_TYPE TZ_TZNAME +# ifndef _PATH_UNIX +# define _PATH_UNIX "/unix" /* should be in */ +# endif +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +# undef WIFEXITED +# undef WEXITSTATUS +#endif + + +/* +** Encore UMAX V +** +** Not extensively tested. +*/ + +#ifdef UMAXV +# define HASUNAME 1 /* use System V uname(2) system call */ +# define HASSETVBUF 1 /* we have setvbuf(3) in libc */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ +# define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ +# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ +# define MAXPATHLEN PATH_MAX +extern struct passwd *getpwent(), *getpwnam(), *getpwuid(); +extern struct group *getgrent(), *getgrnam(), *getgrgid(); +# undef WIFEXITED +# undef WEXITSTATUS +#endif + + +/* +** Stardent Titan 3000 running TitanOS 4.2. +** +** Must be compiled in "cc -43" mode. +** +** From Kate Hedstrom . +** +** Note the tweaking below after the BSD defines are set. +*/ + +#ifdef titan +# define setpgid setpgrp +typedef int pid_t; +# undef WIFEXITED +# undef WEXITSTATUS +#endif + + +/* +** Sequent DYNIX 3.2.0 +** +** From Jim Davis . +*/ + +#ifdef sequent + +# define BSD 1 +# define HASUNSETENV 1 +# define BSD4_3 1 /* to get signal() in conf.c */ +# define WAITUNION 1 +# define LA_TYPE LA_FLOAT +# ifdef _POSIX_VERSION +# undef _POSIX_VERSION /* set in */ +# endif +# undef HASSETVBUF /* don't actually have setvbuf(3) */ +# define setpgid setpgrp + +/* Have to redefine WIFEXITED to take an int, to work with waitfor() */ +# undef WIFEXITED +# define WIFEXITED(s) (((union wait*)&(s))->w_stopval != WSTOPPED && \ + ((union wait*)&(s))->w_termsig == 0) +# define WEXITSTATUS(s) (((union wait*)&(s))->w_retcode) +typedef int pid_t; +# define isgraph(c) (isprint(c) && (c != ' ')) + +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* TCP/IP implementation is broken */ +# endif + +# ifndef _PATH_UNIX +# define _PATH_UNIX "/dynix" +# endif +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +#endif + + +/* +** Sequent DYNIX/ptx v2.0 (and higher) +** +** For DYNIX/ptx v1.x, undefine HASSETREUID. +** +** From Tim Wright . +** Update from Jack Woolley , 26 Dec 1995, +** for DYNIX/ptx 4.0.2. +*/ + +#ifdef _SEQUENT_ +# include +# define SYSTEM5 1 /* include all the System V defines */ +# define HASSETSID 1 /* has POSIX setsid(2) call */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define HASSETREUID 1 /* has setreuid(2) call */ +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# define GIDSET_T gid_t +# define LA_TYPE LA_INT +# define SFS_TYPE SFS_STATFS /* use statfs() impl */ +# define SPT_TYPE SPT_NONE /* don't use setproctitle */ +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* TCP/IP implementation is broken */ +# endif +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/etc/sendmail.pid" +# endif +#endif + + +/* +** Cray Unicos +** +** Ported by David L. Kensiski, Sterling Sofware +*/ + +#ifdef UNICOS +# define SYSTEM5 1 /* include all the System V defines */ +# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ +# define MAXPATHLEN PATHSIZE +# define LA_TYPE LA_ZERO +# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ +# define SFS_BAVAIL f_bfree /* alternate field name */ +#endif + + +/* +** Apollo DomainOS +** +** From Todd Martin & Don Lewis +** +** 15 Jan 1994; updated 2 Aug 1995 +** +*/ + +#ifdef apollo +# define HASSETREUID 1 /* has setreuid(2) call */ +# define HASINITGROUPS 1 /* has initgroups(2) call */ +# define IP_SRCROUTE 0 /* does not have */ +# define SPT_TYPE SPT_NONE /* don't use setproctitle */ +# define LA_TYPE LA_SUBR /* use getloadavg.c */ +# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ +# define SFS_BAVAIL f_bfree /* alternate field name */ +# define TZ_TYPE TZ_TZNAME +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/etc/sendmail.pid" +# endif +# undef S_IFSOCK /* S_IFSOCK and S_IFIFO are the same */ +# undef S_IFIFO +# define S_IFIFO 0010000 +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* TCP/IP implementation is broken */ +# endif +# define RLIMIT_NEEDS_SYS_TIME_H 1 +# if defined(NGROUPS_MAX) && !NGROUPS_MAX +# undef NGROUPS_MAX +# endif +#endif + + +/* +** UnixWare 2.x +*/ + +#ifdef UNIXWARE2 +# define UNIXWARE 1 +# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ +# undef offsetof /* avoid stddefs.h, sys/sysmacros.h conflict */ +#endif + + +/* +** UnixWare 1.1.2. +** +** Updated by Petr Lampa . +** From Evan Champion . +*/ + +#ifdef UNIXWARE +# include +# define SYSTEM5 1 +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# define HASSETREUID 1 +# define HASSETSID 1 +# define HASINITGROUPS 1 +# define GIDSET_T gid_t +# define SLEEP_T unsigned +# define SFS_TYPE SFS_STATVFS +# define LA_TYPE LA_ZERO +# undef WIFEXITED +# undef WEXITSTATUS +# define _PATH_UNIX "/unix" +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" +# endif +# define SYSLOG_BUFSIZE 128 +#endif + + +/* +** Intergraph CLIX 3.1 +** +** From Paul Southworth +*/ + +#ifdef CLIX +# define SYSTEM5 1 /* looks like System V */ +# ifndef HASGETUSERSHELL +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# endif +# define DEV_BSIZE 512 /* device block size not defined */ +# define GIDSET_T gid_t +# undef LOG /* syslog not available */ +# define NEEDFSYNC 1 /* no fsync in system library */ +# define GETSHORT _getshort +#endif + + +/* +** NCR MP-RAS 2.x (SysVr4) with Wollongong TCP/IP +** +** From Kevin Darcy . +*/ + +#ifdef NCR_MP_RAS2 +# include +# define __svr4__ +# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ +# define SYSLOG_BUFSIZE 1024 +# define SPT_TYPE SPT_NONE +#endif + + +/* +** NCR MP-RAS 3.x (SysVr4) with STREAMware TCP/IP +** +** From Tom Moore +*/ + +#ifdef NCR_MP_RAS3 +# define __svr4__ +# define SIOCGIFNUM_IS_BROKEN 1 /* SIOCGIFNUM has non-std interface */ +# define SYSLOG_BUFSIZE 1024 +# define SPT_TYPE SPT_NONE +#endif + + +/* +** Tandem NonStop-UX SVR4 +** +** From Rick McCarty . +*/ + +#ifdef NonStop_UX_BXX +# define __svr4__ +#endif + + +/* +** Hitachi 3050R & 3050RX Workstations running HI-UX/WE2. +** +** Tested for 1.04 and 1.03 +** From Akihiro Hashimoto ("Hash") . +*/ + +#ifdef __H3050R +# define SYSTEM5 1 /* include all the System V defines */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define setreuid(r, e) setresuid(r, e, -1) +# define LA_TYPE LA_FLOAT +# define SFS_TYPE SFS_VFS /* use statfs() implementation */ +# define HASSETVBUF /* HI-UX has no setlinebuf */ +# ifndef GIDSET_T +# define GIDSET_T gid_t +# endif +# ifndef _PATH_UNIX +# define _PATH_UNIX "/HI-UX" +# endif +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* TCP/IP implementation is broken */ +# endif +# ifndef HASGETUSERSHELL +# define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ +# endif + +/* +** avoid m_flags conflict between Berkeley DB 1.85 db.h & sys/sysmacros.h +** on HIUX 3050 +*/ +# undef m_flags + +# ifdef __STDC__ +extern int syslog(int, char *, ...); +#else +extern int syslog(); +# endif + +#endif + + +/* +** Amdahl UTS System V 2.1.5 (SVr3-based) +** +** From: Janet Jackson . +*/ + +#ifdef _UTS +# include +# undef HASLSTAT /* has symlinks, but they cause problems */ +# define NEEDFSYNC 1 /* system fsync(2) fails on non-EFS filesys */ +# define SYS5SIGNALS 1 /* System V signal semantics */ +# define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ +# define HASUNAME 1 /* use System V uname(2) system call */ +# define HASINITGROUPS 1 /* has initgroups(3) function */ +# define HASSETVBUF 1 /* has setvbuf(3) function */ +# ifndef HASGETUSERSHELL +# define HASGETUSERSHELL 0 /* does not have getusershell(3) function */ +# endif +# define GIDSET_T gid_t /* type of 2nd arg to getgroups(2) isn't int */ +# define LA_TYPE LA_ZERO /* doesn't have load average */ +# define SFS_TYPE SFS_4ARGS /* use 4-arg statfs() */ +# define SFS_BAVAIL f_bfree /* alternate field name */ +# define _PATH_UNIX "/unix" +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +#endif + +/* +** Cray Computer Corporation's CSOS +** +** From Scott Bolte . +*/ + +#ifdef _CRAYCOM +# define SYSTEM5 1 /* include all the System V defines */ +# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ +# define NEEDFSYNC 1 /* no fsync in system library */ +# define MAXPATHLEN PATHSIZE +# define LA_TYPE LA_ZERO +# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ +# define SFS_BAVAIL f_bfree /* alternate field name */ +# define _POSIX_CHOWN_RESTRICTED -1 +extern struct group *getgrent(), *getgrnam(), *getgrgid(); +#endif + + +/* +** Sony NEWS-OS 4.2.1R and 6.0.3 +** +** From Motonori NAKAMURA . +*/ + +#ifdef sony_news +# ifndef __svr4 + /* NEWS-OS 4.2.1R */ +# ifndef BSD +# define BSD /* has BSD routines */ +# endif +# define HASUNSETENV 1 /* has unsetenv(2) call */ +# undef HASSETVBUF /* don't actually have setvbuf(3) */ +# define WAITUNION 1 /* use "union wait" as wait argument type */ +# define LA_TYPE LA_INT +# define SFS_TYPE SFS_VFS /* use statfs() implementation */ +# ifndef HASFLOCK +# define HASFLOCK 1 /* has flock(2) call */ +# endif +# define setpgid setpgrp +# undef WIFEXITED +# undef WEXITSTATUS +# define MODE_T int /* system include files have no mode_t */ +typedef int pid_t; +typedef int (*sigfunc_t)(); +# define SIGFUNC_DEFINED +# define SIGFUNC_RETURN (0) +# define SIGFUNC_DECL int + +# else + /* NEWS-OS 6.0.3 with /bin/cc */ +# ifndef __svr4__ +# define __svr4__ /* use all System V Releae 4 defines below */ +# endif +# define HASSETSID 1 /* has Posix setsid(2) call */ +# define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */ +# define LA_TYPE LA_READKSYM /* use MIOC_READKSYM ioctl */ +# ifndef SPT_TYPE +# define SPT_TYPE SPT_SYSMIPS /* use sysmips() (OS 6.0.2 or later) */ +# endif +# define GIDSET_T gid_t +# undef WIFEXITED +# undef WEXITSTATUS +# ifndef SYSLOG_BUFSIZE +# define SYSLOG_BUFSIZE 1024 +# endif +# define _PATH_UNIX "/stand/unix" +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" +# endif + +# endif +#endif + + +/* +** Omron LUNA/UNIOS-B 3.0, LUNA2/Mach and LUNA88K Mach +** +** From Motonori NAKAMURA . +*/ + +#ifdef luna +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* TCP/IP implementation is broken */ +# endif +# define HASUNSETENV 1 /* has unsetenv(2) call */ +# define NEEDPUTENV 1 /* need putenv(3) call */ +# define NEEDGETOPT 1 /* need a replacement for getopt(3) */ +# define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ +# define WAITUNION 1 /* use "union wait" as wait argument type */ +# ifdef uniosb +# include +# define NEEDVPRINTF 1 /* need a replacement for vprintf(3) */ +# define LA_TYPE LA_INT +# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ +# endif +# ifdef luna2 +# define LA_TYPE LA_SUBR +# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ +# endif +# ifdef luna88k +# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ +# define LA_TYPE LA_INT +# endif +# define SFS_TYPE SFS_VFS /* use statfs() implementation */ +# define setpgid setpgrp +# undef WIFEXITED +# undef WEXITSTATUS +typedef int pid_t; +typedef int (*sigfunc_t)(); +# define SIGFUNC_DEFINED +# define SIGFUNC_RETURN (0) +# define SIGFUNC_DECL int +extern char *getenv(); +extern int errno; +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" +# endif +#endif + + +/* +** NEC EWS-UX/V 4.2 (with /usr/ucb/cc) +** +** From Motonori NAKAMURA . +*/ + +#if defined(nec_ews_svr4) || defined(_nec_ews_svr4) +# ifndef __svr4__ +# define __svr4__ /* use all System V Releae 4 defines below */ +# endif +# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ +# define HASSETSID 1 /* has Posix setsid(2) call */ +# define LA_TYPE LA_READKSYM /* use MIOC_READSYM ioctl */ +# define SFS_TYPE SFS_USTAT /* use System V ustat(2) syscall */ +# define GIDSET_T gid_t +# undef WIFEXITED +# undef WEXITSTATUS +# define NAMELISTMASK 0x7fffffff /* mask for nlist() values */ +# ifndef SYSLOG_BUFSIZE +# define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */ +# endif +#endif + + +/* +** Fujitsu/ICL UXP/DS (For the DS/90 Series) +** +** From Diego R. Lopez . +** Additional changes from Fumio Moriya and Toshiaki Nomura of the +** Fujitsu Fresoftware gruop . +*/ + +#ifdef __uxp__ +# include +# include +# include +# define __svr4__ +# define HASGETUSERSHELL 0 +# define HASFLOCK 0 +# if UXPDS == 10 +# define HASSNPRINTF 0 /* no snprintf(3) or vsnprintf(3) */ +# else +# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ +# endif +# define _PATH_UNIX "/stand/unix" +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" +# endif +#endif + +/* +** Pyramid DC/OSx +** +** From Earle Ake . +*/ + +#ifdef DCOSx +# define GIDSET_T gid_t +# ifndef IDENTPROTO +# define IDENTPROTO 0 /* TCP/IP implementation is broken */ +# endif +#endif + +/* +** Concurrent Computer Corporation Maxion +** +** From Donald R. Laster Jr. . +*/ + +#ifdef __MAXION__ + +# include +# define __svr4__ 1 /* SVR4.2MP */ +# define HASSETREUID 1 /* have setreuid(2) */ +# define HASLSTAT 1 /* have lstat(2) */ +# define HASSETRLIMIT 1 /* have setrlimit(2) */ +# define HASGETDTABLESIZE 1 /* have getdtablesize(2) */ +# define HASSNPRINTF 1 /* have snprintf(3) */ +# define HASGETUSERSHELL 1 /* have getusershell(3) */ +# define NOFTRUNCATE 1 /* do not have ftruncate(2) */ +# define SLEEP_T unsigned +# define SFS_TYPE SFS_STATVFS +# define SFS_BAVAIL f_bavail +# ifndef SYSLOG_BUFSIZE +# define SYSLOG_BUFSIZE 256 /* Use 256 bytes */ +# endif + +# undef WUNTRACED +# undef WIFEXITED +# undef WIFSIGNALED +# undef WIFSTOPPED +# undef WEXITSTATUS +# undef WTERMSIG +# undef WSTOPSIG + +#endif + +/* +** Harris Nighthawk PowerUX (nh6000 box) +** +** Contributed by Bob Miorelli, Pratt & Whitney +*/ + +#ifdef _PowerUX +# ifndef __svr4__ +# define __svr4__ +# endif +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" +# endif +# define SYSLOG_BUFSIZE 1024 +# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ +# define LA_TYPE LA_ZERO +typedef struct msgb mblk_t; +# undef offsetof /* avoid stddefs.h and sys/sysmacros.h conflict */ +#endif + +/* +** Siemens Nixdorf Informationssysteme AG SINIX +** +** Contributed by Gerald Rinske +** of Siemens Business Services VAS. +*/ +#ifdef sinix +# define SYSLOG_BUFSIZE 1024 +#endif + +/* +** CRAY T3E +** +** Contributed by Manu Mahonen +** of Center for Scientific Computing. +*/ +#ifdef _CRAY +# define GET_IPOPT_DST(dst) *(struct in_addr *)&(dst) +#endif + +/********************************************************************** +** End of Per-Operating System defines +**********************************************************************/ + /********************************************************************** +** More general defines +**********************************************************************/ + +/* general BSD defines */ +#ifdef BSD +# define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */ +# define HASSETREUID 1 /* has setreuid(2) call */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# ifndef IP_SRCROUTE +# define IP_SRCROUTE 1 /* can check IP source routing */ +# endif +# ifndef HASSETRLIMIT +# define HASSETRLIMIT 1 /* has setrlimit(2) call */ +# endif +# ifndef HASFLOCK +# define HASFLOCK 1 /* has flock(2) call */ +# endif +# ifndef TZ_TYPE +# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone variable */ +# endif +#endif + +/* general System V Release 4 defines */ +#ifdef __svr4__ +# define SYSTEM5 1 +# define USESETEUID 1 /* has useable seteuid(2) call */ +# define HASINITGROUPS 1 /* has initgroups(3) call */ +# define BSD_COMP 1 /* get BSD ioctl calls */ +# ifndef HASSETRLIMIT +# define HASSETRLIMIT 1 /* has setrlimit(2) call */ +# endif +# ifndef HASGETUSERSHELL +# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ +# endif +# ifndef HASFCHMOD +# define HASFCHMOD 1 /* most (all?) SVr4s seem to have fchmod(2) */ +# endif + +# ifndef _PATH_UNIX +# define _PATH_UNIX "/unix" +# endif +# ifndef _PATH_VENDOR_CF +# define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" +# endif +# ifndef _PATH_SENDMAILPID +# define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" +# endif +# ifndef SYSLOG_BUFSIZE +# define SYSLOG_BUFSIZE 128 +# endif +# ifndef SFS_TYPE +# define SFS_TYPE SFS_STATVFS +# endif + +# define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */ +#endif + +/* general System V defines */ +#ifdef SYSTEM5 +# include +# define HASUNAME 1 /* use System V uname(2) system call */ +# define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ +# define HASSETVBUF 1 /* we have setvbuf(3) in libc */ +# ifndef HASULIMIT +# define HASULIMIT 1 /* has the ulimit(2) syscall */ +# endif +# ifndef LA_TYPE +# ifdef MIOC_READKSYM +# define LA_TYPE LA_READKSYM /* use MIOC_READKSYM ioctl */ +# else +# define LA_TYPE LA_INT /* assume integer load average */ +# endif +# endif +# ifndef SFS_TYPE +# define SFS_TYPE SFS_USTAT /* use System V ustat(2) syscall */ +# endif +# ifndef TZ_TYPE +# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ +# endif +# define bcopy(s, d, l) (memmove((d), (s), (l))) +# define bzero(d, l) (memset((d), '\0', (l))) +# define bcmp(s, d, l) (memcmp((s), (d), (l))) +#endif + +/* general POSIX defines */ +#ifdef _POSIX_VERSION +# define HASSETSID 1 /* has Posix setsid(2) call */ +# define HASWAITPID 1 /* has Posix waitpid(2) call */ +# if _POSIX_VERSION >= 199500 && !defined(USESETEUID) +# define USESETEUID 1 /* has useable seteuid(2) call */ +# endif +# ifndef bcopy +# define bcopy(s, d, l) (memmove((d), (s), (l))) +# define bzero(d, l) (memset((d), '\0', (l))) +# define bcmp(s, d, l) (memcmp((s), (d), (l))) +# endif +#endif + /* +** Tweaking for systems that (for example) claim to be BSD or POSIX +** but don't have all the standard BSD or POSIX routines (boo hiss). +*/ + +#ifdef titan +# undef HASINITGROUPS /* doesn't have initgroups(3) call */ +#endif + +#ifdef _CRAYCOM +# undef HASSETSID /* despite POSIX claim, doesn't have setsid */ +#endif + +#ifdef ISC_UNIX +# undef bcopy /* despite SystemV claim, uses BSD bcopy */ +#endif + +#ifdef ALTOS_SYSTEM_V +# undef bcopy /* despite SystemV claim, uses BSD bcopy */ +# undef bzero /* despite SystemV claim, uses BSD bzero */ +# undef bcmp /* despite SystemV claim, uses BSD bcmp */ +#endif + +#if defined(sun) && !defined(BSD) && !defined(SOLARIS) && !defined(__svr4__) && !defined(__SVR4) +# undef bcopy /* SunOS 4 doesn't have memmove() */ +#endif + + +/* +** Due to a "feature" in some operating systems such as Ultrix 4.3 and +** HPUX 8.0, if you receive a "No route to host" message (ICMP message +** ICMP_UNREACH_HOST) on _any_ connection, all connections to that host +** are closed. Some firewalls return this error if you try to connect +** to the IDENT port (113), so you can't receive email from these hosts +** on these systems. The firewall really should use a more specific +** message such as ICMP_UNREACH_PROTOCOL or _PORT or _FILTER_PROHIB. If +** not explicitly set to zero above, default it on. +*/ + +#ifndef IDENTPROTO +# define IDENTPROTO 1 /* use IDENT proto (RFC 1413) */ +#endif + +#ifndef IP_SRCROUTE +# define IP_SRCROUTE 1 /* Detect IP source routing */ +#endif + +#ifndef HASGETUSERSHELL +# define HASGETUSERSHELL 1 /* libc has getusershell(3) call */ +#endif + +#ifndef NETUNIX +# define NETUNIX 1 /* include unix domain support */ +#endif + +#ifndef HASFLOCK +# define HASFLOCK 0 /* assume no flock(2) support */ +#endif + +#ifndef HASSETREUID +# define HASSETREUID 0 /* assume no setreuid(2) call */ +#endif + +#ifndef HASFCHMOD +# define HASFCHMOD 0 /* assume no fchmod(2) syscall */ +#endif + +#ifndef USESETEUID +# define USESETEUID 0 /* assume no seteuid(2) call or no saved ids */ +#endif + +#ifndef HASSETRLIMIT +# define HASSETRLIMIT 0 /* assume no setrlimit(2) support */ +#endif + +#ifndef HASULIMIT +# define HASULIMIT 0 /* assume no ulimit(2) support */ +#endif + +#ifndef SECUREWARE +# define SECUREWARE 0 /* assume no SecureWare C2 auditing hooks */ +#endif + +#ifndef USE_SIGLONGJMP +# define USE_SIGLONGJMP 0 /* assume setjmp handles signals properly */ +#endif + +#ifndef FDSET_CAST +# define FDSET_CAST /* (empty) cast for fd_set arg to select */ +#endif + +/* +** If no type for argument two of getgroups call is defined, assume +** it's an integer -- unfortunately, there seem to be several choices +** here. +*/ + +#ifndef GIDSET_T +# define GIDSET_T int +#endif + +#ifndef UID_T +# define UID_T uid_t +#endif + +#ifndef GID_T +# define GID_T gid_t +#endif + +#ifndef SIZE_T +# define SIZE_T size_t +#endif + +#ifndef MODE_T +# define MODE_T mode_t +#endif + +#ifndef ARGV_T +# define ARGV_T char ** +#endif + +#ifndef SOCKADDR_LEN_T +# define SOCKADDR_LEN_T int +#endif + +#ifndef SOCKOPT_LEN_T +# define SOCKOPT_LEN_T int +#endif + +#ifndef QUAD_T +# define QUAD_T unsigned long +#endif + /********************************************************************** +** Remaining definitions should never have to be changed. They are +** primarily to provide back compatibility for older systems -- for +** example, it includes some POSIX compatibility definitions +**********************************************************************/ + +/* System 5 compatibility */ +#ifndef S_ISREG +# define S_ISREG(foo) ((foo & S_IFMT) == S_IFREG) +#endif +#ifndef S_ISDIR +# define S_ISDIR(foo) ((foo & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +# define S_ISLNK(foo) ((foo & S_IFMT) == S_IFLNK) +#endif +#ifndef S_IRUSR +# define S_IRUSR 0400 +#endif +#ifndef S_IWUSR +# define S_IWUSR 0200 +#endif +#ifndef S_IRGRP +# define S_IRGRP 0040 +#endif +#ifndef S_IWGRP +# define S_IWGRP 0020 +#endif +#ifndef S_IROTH +# define S_IROTH 0004 +#endif +#ifndef S_IWOTH +# define S_IWOTH 0002 +#endif + +/* +** Older systems don't have this error code -- it should be in +** /usr/include/sysexits.h. +*/ + +# ifndef EX_CONFIG +# define EX_CONFIG 78 /* configuration error */ +# endif + +/* pseudo-code used in server SMTP */ +# define EX_QUIT 22 /* drop out of server immediately */ + +/* pseudo-code used for mci_setstat */ +# define EX_NOTSTICKY -5 /* don't save persistent status */ + + +/* +** An "impossible" file mode to indicate that the file does not exist. +*/ + +#define ST_MODE_NOFILE 0171147 /* unlikely to occur */ + + +/* +** These are used in a few cases where we need some special +** error codes, but where the system doesn't provide something +** reasonable. They are printed in errstring. +*/ + +#ifndef E_PSEUDOBASE +# define E_PSEUDOBASE 256 +#endif + +#define E_SM_OPENTIMEOUT (E_PSEUDOBASE + 0) /* Timeout on file open */ +#define E_SM_NOSLINK (E_PSEUDOBASE + 1) /* Symbolic links not allowed */ +#define E_SM_NOHLINK (E_PSEUDOBASE + 2) /* Hard links not allowed */ +#define E_SM_REGONLY (E_PSEUDOBASE + 3) /* Regular files only */ +#define E_SM_ISEXEC (E_PSEUDOBASE + 4) /* Executable files not allowed */ +#define E_SM_WWDIR (E_PSEUDOBASE + 5) /* World writable directory */ +#define E_SM_GWDIR (E_PSEUDOBASE + 6) /* Group writable directory */ +#define E_SM_FILECHANGE (E_PSEUDOBASE + 7) /* File changed after open */ +#define E_SM_WWFILE (E_PSEUDOBASE + 8) /* World writable file */ +#define E_SM_GWFILE (E_PSEUDOBASE + 9) /* Group writable file */ +#define E_DNSBASE (E_PSEUDOBASE + 20) /* base for DNS h_errno */ + +/* type of arbitrary pointer */ +#ifndef ARBPTR_T +# define ARBPTR_T void * +#endif + +#ifndef __P +# include "cdefs.h" +#endif + +#if HESIOD && !defined(NAMED_BIND) +# define NAMED_BIND 1 /* not one without the other */ +#endif + +#if NAMED_BIND && !defined(__ksr__) && !defined(h_errno) +extern int h_errno; +#endif + +/* +** Do some required dependencies +*/ + +#if NETINET || NETISO +# ifndef SMTP +# define SMTP 1 /* enable user and server SMTP */ +# endif +# ifndef QUEUE +# define QUEUE 1 /* enable queueing */ +# endif +# ifndef DAEMON +# define DAEMON 1 /* include the daemon (requires IPC & SMTP) */ +# endif +#endif + + +/* +** Arrange to use either varargs or stdargs +*/ + +# ifdef __STDC__ + +# include + +# define VA_LOCAL_DECL va_list ap; +# define VA_START(f) va_start(ap, f) +# define VA_END va_end(ap) + +# else + +# include + +# define VA_LOCAL_DECL va_list ap; +# define VA_START(f) va_start(ap) +# define VA_END va_end(ap) + +# endif + +#ifdef HASUNAME +# include +# ifdef newstr +# undef newstr +# endif +#else /* ! HASUNAME */ +# define NODE_LENGTH 32 +struct utsname +{ + char nodename[NODE_LENGTH+1]; +}; +#endif /* HASUNAME */ + +#if !defined(MAXHOSTNAMELEN) && !defined(_SCO_unix_) && !defined(NonStop_UX_BXX) && !defined(ALTOS_SYSTEM_V) +# define MAXHOSTNAMELEN 256 +#endif + +#if !defined(SIGCHLD) && defined(SIGCLD) +# define SIGCHLD SIGCLD +#endif + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +#ifndef LOCK_SH +# define LOCK_SH 0x01 /* shared lock */ +# define LOCK_EX 0x02 /* exclusive lock */ +# define LOCK_NB 0x04 /* non-blocking lock */ +# define LOCK_UN 0x08 /* unlock */ +#endif + +#ifndef S_IXOTH +# define S_IXOTH (S_IEXEC >> 6) +#endif + +#ifndef S_IXGRP +# define S_IXGRP (S_IEXEC >> 3) +#endif + +#ifndef S_IXUSR +# define S_IXUSR (S_IEXEC) +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 +# define SEEK_CUR 1 +# define SEEK_END 2 +#endif + +#ifndef SIG_ERR +# define SIG_ERR ((void (*)()) -1) +#endif + +#ifndef WEXITSTATUS +# define WEXITSTATUS(st) (((st) >> 8) & 0377) +#endif +#ifndef WIFEXITED +# define WIFEXITED(st) (((st) & 0377) == 0) +#endif + +#ifndef SIGFUNC_DEFINED +typedef void (*sigfunc_t) __P((int)); +#endif +#ifndef SIGFUNC_RETURN +# define SIGFUNC_RETURN +#endif +#ifndef SIGFUNC_DECL +# define SIGFUNC_DECL void +#endif + +/* size of syslog buffer */ +#ifndef SYSLOG_BUFSIZE +# define SYSLOG_BUFSIZE 1024 +#endif + +/* +** Size of prescan buffer. +** Despite comments in the _sendmail_ book, this probably should +** not be changed; there are some hard-to-define dependencies. +*/ + +# define PSBUFSIZE (MAXNAME + MAXATOM) /* size of prescan buffer */ + +/* fork routine -- set above using #ifdef _osname_ or in Makefile */ +# ifndef FORK +# define FORK fork /* function to call to fork mailer */ +# endif + +/* +** Default to using scanf in readcf. +*/ + +#ifndef SCANF +# define SCANF 1 +#endif + +/* +** SVr4 and similar systems use different routines for setjmp/longjmp +** with signal support +*/ + +#if USE_SIGLONGJMP +# ifdef jmp_buf +# undef jmp_buf +# endif +# define jmp_buf sigjmp_buf +# ifdef setjmp +# undef setjmp +# endif +# define setjmp(env) sigsetjmp(env, 1) +# ifdef longjmp +# undef longjmp +# endif +# define longjmp(env, val) siglongjmp(env, val) +#endif + +#if !defined(NGROUPS_MAX) && defined(NGROUPS) +# define NGROUPS_MAX NGROUPS /* POSIX naming convention */ +#endif + +/* +** If we don't have a system syslog, simulate it. +*/ + +#if !LOG +# define LOG_EMERG 0 /* system is unusable */ +# define LOG_ALERT 1 /* action must be taken immediately */ +# define LOG_CRIT 2 /* critical conditions */ +# define LOG_ERR 3 /* error conditions */ +# define LOG_WARNING 4 /* warning conditions */ +# define LOG_NOTICE 5 /* normal but significant condition */ +# define LOG_INFO 6 /* informational */ +# define LOG_DEBUG 7 /* debug-level messages */ +#endif diff --git a/contrib/sendmail/src/convtime.c b/contrib/sendmail/src/convtime.c new file mode 100644 index 0000000..02c287b --- /dev/null +++ b/contrib/sendmail/src/convtime.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)convtime.c 8.14 (Berkeley) 5/19/98"; +#endif /* not lint */ + +# include "sendmail.h" + +/* +** 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; + char units; +{ + register time_t t, r; + register char c; + + r = 0; + 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; + + case 'd': /* days */ + default: + t *= 24; + + case 'h': /* hours */ + t *= 60; + + case 'm': /* minutes */ + t *= 60; + + case 's': /* seconds */ + break; + } + r += t; + } + + return (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"); + + /* 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) snprintf(p, SPACELEFT(buf, p), "%d+", dy); + p += strlen(p); + } + (void) snprintf(p, SPACELEFT(buf, p), "%02d:%02d:%02d", + hr, mi, se); + return (buf); + } + + /* use the verbose form */ + if (wk > 0) + { + (void) snprintf(p, SPACELEFT(buf, p), ", %d week%s", wk, PLURAL(wk)); + p += strlen(p); + } + if (dy > 0) + { + (void) snprintf(p, SPACELEFT(buf, p), ", %d day%s", dy, PLURAL(dy)); + p += strlen(p); + } + if (hr > 0) + { + (void) snprintf(p, SPACELEFT(buf, p), ", %d hour%s", hr, PLURAL(hr)); + p += strlen(p); + } + if (mi > 0) + { + (void) snprintf(p, SPACELEFT(buf, p), ", %d minute%s", mi, PLURAL(mi)); + p += strlen(p); + } + if (se > 0) + { + (void) snprintf(p, SPACELEFT(buf, p), ", %d second%s", se, PLURAL(se)); + p += strlen(p); + } + + return (buf + 2); +} diff --git a/contrib/sendmail/src/daemon.c b/contrib/sendmail/src/daemon.c new file mode 100644 index 0000000..dc0b5b5 --- /dev/null +++ b/contrib/sendmail/src/daemon.c @@ -0,0 +1,2073 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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 "sendmail.h" + +#ifndef lint +#ifdef DAEMON +static char sccsid[] = "@(#)daemon.c 8.220 (Berkeley) 6/24/98 (with daemon mode)"; +#else +static char sccsid[] = "@(#)daemon.c 8.220 (Berkeley) 6/24/98 (without daemon mode)"; +#endif +#endif /* not lint */ + +#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) +# define USE_SOCK_STREAM 1 +#endif + +#if DAEMON || defined(USE_SOCK_STREAM) +# include +# if NAMED_BIND +# include +# ifndef NO_DATA +# define NO_DATA NO_ADDRESS +# endif +# endif +#endif + +#if DAEMON + +# include + +# if IP_SRCROUTE +# include +# include +# include +# endif + +/* +** 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, outfile, infile, e) +** Make a connection to the named host on the given +** port. Set *outfile and *infile to the files +** appropriate for communication. Returns zero on +** success, else an exit status describing the +** error. +** host_map_lookup(map, hbuf, avp, pstat) +** Convert the entry in hbuf into a canonical form. +*/ + /* +** GETREQUESTS -- open mail IPC port and get requests. +** +** Parameters: +** e -- the current envelope. +** +** Returns: +** none. +** +** 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. +*/ + +int DaemonSocket = -1; /* fd describing socket */ +SOCKADDR DaemonAddr; /* socket for incoming */ +int ListenQueueSize = 10; /* size of listen queue */ +int TcpRcvBufferSize = 0; /* size of TCP receive buffer */ +int TcpSndBufferSize = 0; /* size of TCP send buffer */ + +void +getrequests(e) + ENVELOPE *e; +{ + int t; + bool refusingconnections = TRUE; + FILE *pidf; + int socksize; + u_short port; +#if XDEBUG + bool j_has_dot; +#endif + extern void reapchild __P((int)); + extern int opendaemonsocket __P((bool)); + + /* + ** Set up the address for the mailer. + */ + + switch (DaemonAddr.sa.sa_family) + { + case AF_UNSPEC: + DaemonAddr.sa.sa_family = AF_INET; + /* fall through ... */ + + case AF_INET: + if (DaemonAddr.sin.sin_addr.s_addr == 0) + DaemonAddr.sin.sin_addr.s_addr = INADDR_ANY; + port = DaemonAddr.sin.sin_port; + break; + + default: + /* unknown protocol */ + port = 0; + break; + } + if (port == 0) + { + register struct servent *sp; + + sp = getservbyname("smtp", "tcp"); + if (sp == NULL) + { + syserr("554 service \"smtp\" unknown"); + port = htons(25); + } + else + port = sp->s_port; + } + + switch (DaemonAddr.sa.sa_family) + { + case AF_INET: + DaemonAddr.sin.sin_port = port; + break; + + default: + /* unknown protocol */ + break; + } + + /* + ** Try to actually open the connection. + */ + + if (tTd(15, 1)) + printf("getrequests: port 0x%x\n", port); + + /* get a socket for the SMTP connection */ + socksize = opendaemonsocket(TRUE); + + (void) setsignal(SIGCHLD, reapchild); + + /* write the pid to the log file for posterity */ + pidf = safefopen(PidFile, O_WRONLY|O_TRUNC, 0644, + SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT); + if (pidf == NULL) + { + sm_syslog(LOG_ERR, NOQID, "unable to write %s", PidFile); + } + else + { + extern char *CommandLineArgs; + + /* write the process id on line 1 */ + fprintf(pidf, "%ld\n", (long) getpid()); + + /* line 2 contains all command line flags */ + fprintf(pidf, "%s\n", CommandLineArgs); + + /* flush and close */ + fclose(pidf); + } + +#if XDEBUG + { + char jbuf[MAXHOSTNAMELEN]; + + expand("\201j", jbuf, sizeof jbuf, e); + j_has_dot = strchr(jbuf, '.') != NULL; + } +#endif + + if (tTd(15, 1)) + printf("getrequests: %d\n", DaemonSocket); + + for (;;) + { + register pid_t pid; + auto SOCKADDR_LEN_T lotherend; + int savederrno; + int pipefd[2]; + extern bool refuseconnections __P((int)); + + /* see if we are rejecting connections */ + (void) blocksignal(SIGALRM); + if (refuseconnections(ntohs(port))) + { + if (DaemonSocket >= 0) + { + /* close socket so peer will fail quickly */ + (void) close(DaemonSocket); + DaemonSocket = -1; + } + refusingconnections = TRUE; + sleep(15); + continue; + } + + /* arrange to (re)open the socket if necessary */ + if (refusingconnections) + { + (void) opendaemonsocket(FALSE); + refusingconnections = FALSE; + } + +#if XDEBUG + /* check for disaster */ + { + char jbuf[MAXHOSTNAMELEN]; + extern void dumpstate __P((char *)); + + expand("\201j", jbuf, sizeof jbuf, e); + if (!wordinclass(jbuf, 'w')) + { + dumpstate("daemon lost $j"); + sm_syslog(LOG_ALERT, NOQID, + "daemon process doesn't have $j in $=w; see syslog"); + 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 + + /* wait for a connection */ + setproctitle("accepting connections on port %d", + ntohs(port)); +#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 (SetNonBlocking(DaemonSocket, FALSE) < 0) + log an error here; +#endif + (void) releasesignal(SIGALRM); + for (;;) + { + fd_set readfds; + struct timeval timeout; + + FD_ZERO(&readfds); + FD_SET(DaemonSocket, &readfds); + timeout.tv_sec = 60; + timeout.tv_usec = 0; + + t = select(DaemonSocket + 1, FDSET_CAST &readfds, + NULL, NULL, &timeout); + if (DoQueueRun) + (void) runqueue(TRUE, FALSE); + if (t <= 0 || !FD_ISSET(DaemonSocket, &readfds)) + continue; + + errno = 0; + lotherend = socksize; + t = accept(DaemonSocket, + (struct sockaddr *)&RealHostAddr, &lotherend); + if (t >= 0 || errno != EINTR) + break; + } + savederrno = errno; + (void) blocksignal(SIGALRM); + if (t < 0) + { + errno = savederrno; + syserr("getrequests: accept"); + + /* arrange to re-open the socket next time around */ + (void) close(DaemonSocket); + DaemonSocket = -1; + refusingconnections = TRUE; + sleep(5); + continue; + } + + /* + ** Create a subprocess to process the mail. + */ + + if (tTd(15, 2)) + printf("getrequests: forking (fd = %d)\n", t); + + /* + ** 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; + + blocksignal(SIGCHLD); + pid = fork(); + if (pid < 0) + { + syserr("daemon: cannot fork"); + if (pipefd[0] != -1) + { + (void) close(pipefd[0]); + (void) close(pipefd[1]); + } + (void) releasesignal(SIGCHLD); + sleep(10); + (void) close(t); + continue; + } + + if (pid == 0) + { + char *p; + extern SIGFUNC_DECL intsig __P((int)); + FILE *inchannel, *outchannel; + + /* + ** CHILD -- return to caller. + ** Collect verified idea of sending host. + ** Verify calling user id if possible here. + */ + + (void) releasesignal(SIGALRM); + (void) releasesignal(SIGCHLD); + (void) setsignal(SIGCHLD, SIG_DFL); + (void) setsignal(SIGHUP, intsig); + (void) close(DaemonSocket); + proc_list_clear(); + + /* don't schedule queue runs if we are told to ETRN */ + QueueIntvl = 0; + + setproctitle("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]); + } + + /* determine host name */ + p = hostnamebyanyaddr(&RealHostAddr); + if (strlen(p) > (SIZE_T) MAXNAME) + p[MAXNAME] = '\0'; + RealHostName = newstr(p); + setproctitle("startup with %s", p); + + if ((inchannel = fdopen(t, "r")) == NULL || + (t = dup(t)) < 0 || + (outchannel = fdopen(t, "w")) == NULL) + { + syserr("cannot open SMTP server channel, fd=%d", t); + exit(EX_OK); + } + + InChannel = inchannel; + OutChannel = outchannel; + DisConnected = FALSE; + + /* open maps for check_relay ruleset */ + initmaps(FALSE, e); + +#ifdef XLA + if (!xla_host_ok(RealHostName)) + { + message("421 Too many SMTP sessions for this host"); + exit(EX_OK); + } +#endif + + break; + } + + /* parent -- keep track of children */ + proc_list_add(pid); + (void) releasesignal(SIGCHLD); + + /* close the read end of the synchronization pipe */ + if (pipefd[0] != -1) + (void) close(pipefd[0]); + + /* 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]); + } + if (tTd(15, 2)) + printf("getreq: returning\n"); + return; +} + /* +** OPENDAEMONSOCKET -- open the SMTP socket +** +** Deals with setting all appropriate options. DaemonAddr must +** be set up in advance. +** +** Parameters: +** 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 */ + +int +opendaemonsocket(firsttime) + bool firsttime; +{ + int on = 1; + int socksize = 0; + int ntries = 0; + int saveerrno; + + if (tTd(15, 2)) + printf("opendaemonsocket()\n"); + + do + { + if (ntries > 0) + sleep(5); + if (firsttime || DaemonSocket < 0) + { + DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0); + if (DaemonSocket < 0) + { + saveerrno = errno; + syserr("opendaemonsocket: can't create server SMTP socket"); + severe: + if (LogLevel > 0) + sm_syslog(LOG_ALERT, NOQID, + "problem creating SMTP socket"); + DaemonSocket = -1; + continue; + } + + /* turn on network debugging? */ + if (tTd(15, 101)) + (void) setsockopt(DaemonSocket, SOL_SOCKET, + SO_DEBUG, (char *)&on, + sizeof on); + + (void) setsockopt(DaemonSocket, SOL_SOCKET, + SO_REUSEADDR, (char *)&on, sizeof on); + (void) setsockopt(DaemonSocket, SOL_SOCKET, + SO_KEEPALIVE, (char *)&on, sizeof on); + +#ifdef SO_RCVBUF + if (TcpRcvBufferSize > 0) + { + if (setsockopt(DaemonSocket, SOL_SOCKET, + SO_RCVBUF, + (char *) &TcpRcvBufferSize, + sizeof(TcpRcvBufferSize)) < 0) + syserr("opendaemonsocket: setsockopt(SO_RCVBUF)"); + } +#endif + + switch (DaemonAddr.sa.sa_family) + { +# if NETINET + case AF_INET: + socksize = sizeof DaemonAddr.sin; + break; +# endif + +# if NETISO + case AF_ISO: + socksize = sizeof DaemonAddr.siso; + break; +# endif + + default: + socksize = sizeof DaemonAddr; + break; + } + + if (bind(DaemonSocket, &DaemonAddr.sa, socksize) < 0) + { + /* probably another daemon already */ + saveerrno = errno; + syserr("opendaemonsocket: cannot bind"); + (void) close(DaemonSocket); + goto severe; + } + } + if (!firsttime && listen(DaemonSocket, ListenQueueSize) < 0) + { + saveerrno = errno; + syserr("opendaemonsocket: cannot listen"); + (void) close(DaemonSocket); + goto severe; + } + return socksize; + } while (ntries++ < MAXOPENTRIES && transienterror(saveerrno)); + syserr("!opendaemonsocket: server SMTP socket wedged: exiting"); + finis(); + return -1; /* avoid compiler warning on IRIX */ +} + /* +** CLRDAEMON -- reset the daemon connection +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** releases any resources used by the passive daemon. +*/ + +void +clrdaemon() +{ + if (DaemonSocket >= 0) + (void) close(DaemonSocket); + DaemonSocket = -1; +} + /* +** SETDAEMONOPTIONS -- set options for running the daemon +** +** Parameters: +** p -- the options line. +** +** Returns: +** none. +*/ + +void +setdaemonoptions(p) + register char *p; +{ + if (DaemonAddr.sa.sa_family == AF_UNSPEC) + DaemonAddr.sa.sa_family = AF_INET; + + 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; + if (isascii(*f) && islower(*f)) + *f = toupper(*f); + + switch (*f) + { + case 'F': /* address family */ + if (isascii(*v) && isdigit(*v)) + DaemonAddr.sa.sa_family = atoi(v); +#if NETINET + else if (strcasecmp(v, "inet") == 0) + DaemonAddr.sa.sa_family = AF_INET; +#endif +#if NETISO + else if (strcasecmp(v, "iso") == 0) + DaemonAddr.sa.sa_family = AF_ISO; +#endif +#if NETNS + else if (strcasecmp(v, "ns") == 0) + DaemonAddr.sa.sa_family = AF_NS; +#endif +#if NETX25 + else if (strcasecmp(v, "x.25") == 0) + DaemonAddr.sa.sa_family = AF_CCITT; +#endif + else + syserr("554 Unknown address family %s in Family=option", v); + break; + + case 'A': /* address */ + switch (DaemonAddr.sa.sa_family) + { +#if NETINET + case AF_INET: + if (isascii(*v) && isdigit(*v)) + DaemonAddr.sin.sin_addr.s_addr = inet_addr(v); + else + { + register struct hostent *hp; + + hp = sm_gethostbyname(v); + if (hp == NULL) + syserr("554 host \"%s\" unknown", v); + else + bcopy(hp->h_addr, &DaemonAddr.sin.sin_addr, INADDRSZ); + } + break; +#endif + + default: + syserr("554 Address= option unsupported for family %d", + DaemonAddr.sa.sa_family); + break; + } + break; + + case 'P': /* port */ + switch (DaemonAddr.sa.sa_family) + { +#if NETISO + short port; +#endif + +#if NETINET + case AF_INET: + if (isascii(*v) && isdigit(*v)) + DaemonAddr.sin.sin_port = htons(atoi(v)); + else + { + register struct servent *sp; + + sp = getservbyname(v, "tcp"); + if (sp == NULL) + syserr("554 service \"%s\" unknown", v); + else + DaemonAddr.sin.sin_port = sp->s_port; + } + break; +#endif + +#if NETISO + case AF_ISO: + /* assume two byte transport selector */ + if (isascii(*v) && isdigit(*v)) + port = htons(atoi(v)); + else + { + register struct servent *sp; + + sp = getservbyname(v, "tcp"); + if (sp == NULL) + syserr("554 service \"%s\" unknown", v); + else + port = sp->s_port; + } + bcopy((char *) &port, TSEL(&DaemonAddr.siso), 2); + break; +#endif + + default: + syserr("554 Port= option unsupported for family %d", + DaemonAddr.sa.sa_family); + break; + } + break; + + case 'L': /* listen queue size */ + ListenQueueSize = atoi(v); + break; + + case 'S': /* send buffer size */ + TcpSndBufferSize = atoi(v); + break; + + case 'R': /* receive buffer size */ + TcpRcvBufferSize = atoi(v); + break; + + default: + syserr("554 DaemonPortOptions parameter \"%s\" unknown", f); + } + } +} + /* +** MAKECONNECTION -- make a connection to an SMTP socket on another 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. +** +** Returns: +** An exit code telling whether the connection could be +** made and if not why not. +** +** Side Effects: +** none. +*/ + +static jmp_buf CtxConnectTimeout; + +static void +connecttimeout() +{ + errno = ETIMEDOUT; + longjmp(CtxConnectTimeout, 1); +} + +SOCKADDR CurHostAddr; /* address of current host */ + +int +makeconnection(host, port, mci, e) + char *host; + u_short port; + register MCI *mci; + ENVELOPE *e; +{ + register volatile int addrno = 0; + register volatile int s; + register struct hostent *volatile hp = (struct hostent *)NULL; + SOCKADDR addr; + int sav_errno; + volatile int addrlen; + volatile bool firstconnect; + EVENT *volatile ev = NULL; + + /* + ** Set up the address for the mailer. + ** Accept "[a.b.c.d]" syntax for host name. + */ + +#if NAMED_BIND + h_errno = 0; +#endif + errno = 0; + bzero(&CurHostAddr, sizeof CurHostAddr); + SmtpPhase = mci->mci_phase = "initial connection"; + CurHostName = host; + + if (host[0] == '[') + { +#if NETINET + unsigned long hid = INADDR_NONE; +#endif + register char *p = strchr(host, ']'); + + if (p != NULL) + { + *p = '\0'; +#if NETINET + hid = inet_addr(&host[1]); + if (hid == INADDR_NONE) +#endif + { + /* try it as a host name (avoid MX lookup) */ + hp = sm_gethostbyname(&host[1]); + if (hp == NULL && p[-1] == '.') + { +#if NAMED_BIND + int oldopts = _res.options; + + _res.options &= ~(RES_DEFNAMES|RES_DNSRCH); +#endif + p[-1] = '\0'; + hp = sm_gethostbyname(&host[1]); + p[-1] = '.'; +#if NAMED_BIND + _res.options = oldopts; +#endif + } + *p = ']'; + goto gothostent; + } + *p = ']'; + } + if (p == NULL) + { + extern char MsgBuf[]; + + usrerr("553 Invalid numeric domain spec \"%s\"", host); + mci_setstat(mci, EX_NOHOST, "5.1.2", MsgBuf); + return EX_NOHOST; + } +#if NETINET + addr.sin.sin_family = AF_INET; /*XXX*/ + addr.sin.sin_addr.s_addr = hid; +#endif + } + else + { + /* contortion to get around SGI cc complaints */ + { + register char *p = &host[strlen(host) - 1]; + + hp = sm_gethostbyname(host); + if (hp == NULL && *p == '.') + { +#if NAMED_BIND + int oldopts = _res.options; + + _res.options &= ~(RES_DEFNAMES|RES_DNSRCH); +#endif + *p = '\0'; + hp = sm_gethostbyname(host); + *p = '.'; +#if NAMED_BIND + _res.options = oldopts; +#endif + } + } +gothostent: + if (hp == NULL) + { +#if NAMED_BIND + /* check for name server timeouts */ + if (errno == ETIMEDOUT || h_errno == TRY_AGAIN || + (errno == ECONNREFUSED && UseNameServer)) + { + mci_setstat(mci, EX_TEMPFAIL, "4.4.3", NULL); + return EX_TEMPFAIL; + } +#endif + mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); + return (EX_NOHOST); + } + addr.sa.sa_family = hp->h_addrtype; + switch (hp->h_addrtype) + { +#if NETINET + case AF_INET: + bcopy(hp->h_addr, + &addr.sin.sin_addr, + INADDRSZ); + break; +#endif + + 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); + return EX_NOHOST; + } + bcopy(hp->h_addr, + addr.sa.sa_data, + hp->h_length); + break; + } + addrno = 1; + } + + /* + ** Determine the port number. + */ + + if (port == 0) + { + 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; + } + + switch (addr.sa.sa_family) + { +#if NETINET + case AF_INET: + addr.sin.sin_port = port; + addrlen = sizeof (struct sockaddr_in); + break; +#endif + +#if NETISO + case AF_ISO: + /* assume two byte transport selector */ + bcopy((char *) &port, TSEL((struct sockaddr_iso *) &addr), 2); + addrlen = sizeof (struct sockaddr_iso); + break; +#endif + + default: + syserr("Can't connect to address family %d", addr.sa.sa_family); + mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); + return (EX_NOHOST); + } + + /* + ** Try to actually open the connection. + */ + +#ifdef XLA + /* if too many connections, don't bother trying */ + if (!xla_noqueue_ok(host)) + return EX_TEMPFAIL; +#endif + + firstconnect = TRUE; + for (;;) + { + if (tTd(16, 1)) + printf("makeconnection (%s [%s])\n", + host, anynet_ntoa(&addr)); + + /* save for logging */ + CurHostAddr = addr; + + if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags)) + { + int rport = IPPORT_RESERVED - 1; + + s = rresvport(&rport); + } + else + { + s = socket(addr.sa.sa_family, SOCK_STREAM, 0); + } + if (s < 0) + { + sav_errno = errno; + syserr("makeconnection: cannot create socket"); +#ifdef XLA + xla_host_end(host); +#endif + mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); + return EX_TEMPFAIL; + } + +#ifdef SO_SNDBUF + if (TcpSndBufferSize > 0) + { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, + (char *) &TcpSndBufferSize, + sizeof(TcpSndBufferSize)) < 0) + syserr("makeconnection: setsockopt(SO_SNDBUF)"); + } +#endif + + if (tTd(16, 1)) + printf("makeconnection: fd=%d\n", s); + + /* turn on network debugging? */ + if (tTd(16, 101)) + { + int on = 1; + (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof on); + } + if (e->e_xfp != NULL) + (void) fflush(e->e_xfp); /* for debugging */ + errno = 0; /* for debugging */ + + /* + ** 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 = setevent(TimeOuts.to_iconnect, connecttimeout, 0); + else if (TimeOuts.to_connect != 0) + ev = setevent(TimeOuts.to_connect, connecttimeout, 0); + else + ev = NULL; + +#if _FFR_CONNECTONLYTO_OPTION + /* for testing */ + if (ConnectOnlyTo != 0) + addr.sin.sin_addr.s_addr = ConnectOnlyTo; +#endif + i = connect(s, (struct sockaddr *) &addr, addrlen); + sav_errno = errno; + if (ev != NULL) + clrevent(ev); + if (i >= 0) + break; + } + else + sav_errno = errno; + + /* if running demand-dialed connection, try again */ + if (DialDelay > 0 && firstconnect) + { + if (tTd(16, 1)) + printf("Connect failed (%s); trying again...\n", + errstring(sav_errno)); + firstconnect = FALSE; + sleep(DialDelay); + continue; + } + + /* couldn't connect.... figure out why */ + (void) close(s); + + if (LogLevel >= 14) + sm_syslog(LOG_INFO, e->e_id, + "makeconnection (%s [%s]) failed: %s", + host, anynet_ntoa(&addr), + errstring(sav_errno)); + + if (hp != NULL && hp->h_addr_list[addrno] != NULL) + { + if (tTd(16, 1)) + printf("Connect failed (%s); trying new address....\n", + errstring(sav_errno)); + switch (addr.sa.sa_family) + { +#if NETINET + case AF_INET: + bcopy(hp->h_addr_list[addrno++], + &addr.sin.sin_addr, + INADDRSZ); + break; +#endif + + default: + bcopy(hp->h_addr_list[addrno++], + addr.sa.sa_data, + hp->h_length); + break; + } + continue; + } + + /* couldn't open connection */ +#ifdef XLA + xla_host_end(host); +#endif + mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL); + return EX_TEMPFAIL; + } + + /* connection ok, put it into canonical form */ + if ((mci->mci_out = fdopen(s, "w")) == NULL || + (s = dup(s)) < 0 || + (mci->mci_in = fdopen(s, "r")) == NULL) + { + syserr("cannot open SMTP client channel, fd=%d", s); + mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); + return EX_TEMPFAIL; + } + + mci_setstat(mci, EX_OK, NULL, NULL); + return (EX_OK); +} + /* +** 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) + { + (void) strcpy(hostbuf, "localhost"); + } + hp = sm_gethostbyname(hostbuf); + if (hp == NULL) + return NULL; + if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL) + { + (void) strncpy(hostbuf, hp->h_name, size - 1); + hostbuf[size - 1] = '\0'; + } + + /* + ** 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++) + { + if (strchr(*ha, '.') != NULL) + { + (void) strncpy(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)) + { + 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); + sleep(60); + if (!getcanonname(hostbuf, size, TRUE)) + { + 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 +*/ + +int +addrcmp(hp, ha, sa) + struct hostent *hp; + char *ha; + SOCKADDR *sa; +{ + switch (sa->sa.sa_family) + { + case AF_INET: + if (hp->h_addrtype == AF_INET) + return bcmp(ha, (char *) &sa->sin.sin_addr, hp->h_length); + break; + + } + return -1; +} + /* +** GETAUTHINFO -- get the real host name asociated 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() +{ + longjmp(CtxAuthTimeout, 1); +} + +char * +getauthinfo(fd, may_be_forged) + int fd; + bool *may_be_forged; +{ + SOCKADDR_LEN_T falen; + register char *volatile p = NULL; + SOCKADDR la; + SOCKADDR_LEN_T lalen; + register struct servent *sp; + volatile int s; + int i = 0; + EVENT *ev; + int nleft; + struct hostent *hp; + char *ostype = NULL; + char **ha; + char ibuf[MAXNAME + 1]; + static char hbuf[MAXNAME * 2 + 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 && errno != ENOTSOCK) + return NULL; + (void) snprintf(hbuf, sizeof hbuf, "%s@localhost", + RealUserName); + if (tTd(9, 1)) + printf("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 - 1] = '\0'; + } + + /* cross check RealHostName with forward DNS lookup */ + if (anynet_ntoa(&RealHostAddr)[0] == '[' || + RealHostName[0] == '[') + { + /* + ** address is not a socket or have an + ** IP address with no forward lookup + */ + *may_be_forged = FALSE; + } + else + { + /* try to match the reverse against the forward lookup */ + hp = sm_gethostbyname(RealHostName); + + if (hp == NULL) + *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 (TimeOuts.to_ident == 0) + goto noident; + + lalen = sizeof la; + if (RealHostAddr.sa.sa_family != AF_INET || + getsockname(fd, &la.sa, &lalen) < 0 || lalen <= 0 || + la.sa.sa_family != AF_INET) + { + /* no ident info */ + goto noident; + } + + /* create ident query */ + (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n", + ntohs(RealHostAddr.sin.sin_port), ntohs(la.sin.sin_port)); + + /* create local address */ + la.sin.sin_port = 0; + + /* create foreign address */ + sp = getservbyname("auth", "tcp"); + if (sp != NULL) + RealHostAddr.sin.sin_port = sp->s_port; + else + RealHostAddr.sin.sin_port = htons(113); + + s = -1; + if (setjmp(CtxAuthTimeout) != 0) + { + if (s >= 0) + (void) close(s); + goto noident; + } + + /* put a timeout around the whole thing */ + ev = setevent(TimeOuts.to_ident, authtimeout, 0); + + /* connect to foreign IDENT server using same address as SMTP socket */ + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + { + clrevent(ev); + goto noident; + } + if (bind(s, &la.sa, sizeof la.sin) < 0 || + connect(s, &RealHostAddr.sa, sizeof RealHostAddr.sin) < 0) + { + goto closeident; + } + + if (tTd(9, 10)) + printf("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) + { + p += i; + nleft -= i; + *p = '\0'; + if (strchr(ibuf, '\n') != NULL) + break; + } + (void) close(s); + clrevent(ev); + if (i < 0 || p == &ibuf[0]) + goto noident; + + if (*--p == '\n' && *--p == '\r') + p--; + *++p = '\0'; + + if (tTd(9, 3)) + printf("getauthinfo: got %s\n", ibuf); + + /* parse result */ + p = strchr(ibuf, ':'); + if (p == NULL) + { + /* malformed response */ + goto noident; + } + while (isascii(*++p) && isspace(*p)) + continue; + if (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 (strncasecmp(ostype, "other", 5) == 0 && + (ostype[5] == ' ' || ostype[5] == '\0')) + { + snprintf(hbuf, sizeof hbuf, "IDENT:"); + cleanstrcpy(&hbuf[6], p, MAXNAME); + } + else + cleanstrcpy(hbuf, p, MAXNAME); + i = strlen(hbuf); + snprintf(&hbuf[i], sizeof hbuf - i, "@%s", + RealHostName == NULL ? "localhost" : RealHostName); + goto postident; + +closeident: + (void) close(s); + clrevent(ev); + +noident: + if (RealHostName == NULL) + { + if (tTd(9, 1)) + printf("getauthinfo: NULL\n"); + return NULL; + } + snprintf(hbuf, sizeof hbuf, "%s", RealHostName); + +postident: +#if IP_SRCROUTE +# ifndef GET_IPOPT_DST +# define GET_IPOPT_DST(dst) (dst) +# endif + /* + ** 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; + u_char *q; + u_char *o; + int l; + struct in_addr addr; + 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 = (u_char *) ipopt.ipopt_list; + while (o != NULL && o < (u_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; + snprintf(p, SPACELEFT(hbuf, p), " [%s@%.*s", + *o == IPOPT_SSRR ? "!" : "", + l > 240 ? 120 : l / 2, + inet_ntoa(GET_IPOPT_DST(ipopt.ipopt_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--) + { + memcpy(&addr, q, sizeof(addr)); + snprintf(p, SPACELEFT(hbuf, p), + "%c%.*s", + j != 0 ? '@' : ':', + l > 240 ? 120 : + j == 0 ? l : l / 2, + inet_ntoa(addr)); + 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; + } + } + snprintf(p, SPACELEFT(hbuf, p), "]"); + goto postipsr; + } + +noipsr: +#endif + if (RealHostName != NULL && RealHostName[0] != '[') + { + p = &hbuf[strlen(hbuf)]; + (void) snprintf(p, SPACELEFT(hbuf, p), " [%.100s]", + anynet_ntoa(&RealHostAddr)); + } + if (*may_be_forged) + { + p = &hbuf[strlen(hbuf)]; + (void) snprintf(p, SPACELEFT(hbuf, p), " (may be forged)"); + } + +#if IP_SRCROUTE +postipsr: +#endif + if (tTd(9, 1)) + printf("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; + struct in_addr in_addr; + char *cp; + register STAB *s; + char hbuf[MAXNAME + 1]; + + /* + ** See if we have already looked up this name. If so, just + ** return it. + */ + + s = stab(name, ST_NAMECANON, ST_ENTER); + if (bitset(NCF_VALID, s->s_namecanon.nc_flags)) + { + if (tTd(9, 1)) + printf("host_map_lookup(%s) => CACHE %s\n", + name, + s->s_namecanon.nc_cname == NULL + ? "NULL" + : s->s_namecanon.nc_cname); + errno = s->s_namecanon.nc_errno; +#if NAMED_BIND + h_errno = s->s_namecanon.nc_herrno; +#endif + *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) + { + if (tTd(9, 1)) + printf("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 (*name != '[') + { + if (tTd(9, 1)) + printf("host_map_lookup(%s) => ", name); + s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */ + snprintf(hbuf, sizeof hbuf, "%s", name); + if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX)) + { + if (tTd(9, 1)) + printf("%s\n", hbuf); + s->s_namecanon.nc_stat = EX_OK; + s->s_namecanon.nc_cname = newstr(hbuf); + if (bitset(MF_MATCHONLY, map->map_mflags)) + cp = map_rewrite(map, name, strlen(name), NULL); + else + cp = map_rewrite(map, hbuf, strlen(hbuf), av); + return cp; + } + else + { + s->s_namecanon.nc_errno = errno; +#if NAMED_BIND + s->s_namecanon.nc_herrno = h_errno; + if (tTd(9, 1)) + printf("FAIL (%d)\n", h_errno); + switch (h_errno) + { + case TRY_AGAIN: + if (UseNameServer) + { + CurEnv->e_status = "4.4.3"; + message("851 %s: Name server timeout", + shortenstring(name, 33)); + } + *statp = EX_TEMPFAIL; + break; + + case HOST_NOT_FOUND: + case NO_DATA: + *statp = EX_NOHOST; + break; + + case NO_RECOVERY: + *statp = EX_SOFTWARE; + break; + + default: + *statp = EX_UNAVAILABLE; + break; + } +#else + if (tTd(9, 1)) + printf("FAIL\n"); + *statp = EX_NOHOST; +#endif + s->s_namecanon.nc_stat = *statp; + return NULL; + } + } + if ((cp = strchr(name, ']')) == NULL) + return (NULL); + *cp = '\0'; + in_addr.s_addr = inet_addr(&name[1]); + *cp = ']'; + + /* nope -- ask the name server */ + hp = sm_gethostbyaddr((char *)&in_addr, INADDRSZ, AF_INET); + s->s_namecanon.nc_errno = errno; +#if NAMED_BIND + s->s_namecanon.nc_herrno = h_errno; +#endif + s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */ + if (hp == NULL) + { + s->s_namecanon.nc_stat = *statp = EX_NOHOST; + return (NULL); + } + + /* found a match -- copy out */ + hp->h_name = denlstring((char *) hp->h_name, TRUE, TRUE); + s->s_namecanon.nc_stat = *statp = EX_OK; + s->s_namecanon.nc_cname = newstr(hp->h_name); + if (bitset(MF_MATCHONLY, map->map_mflags)) + cp = map_rewrite(map, name, strlen(name), NULL); + else + cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av); + return cp; +} + +# else /* DAEMON */ +/* code for systems without sophisticated networking */ + +/* +** MYHOSTNAME -- stub version for case of no daemon code. +** +** Can't convert to upper case here because might be a UUCP name. +** +** Mark, you can change this to be anything you want...... +*/ + +char ** +myhostname(hostbuf, size) + char hostbuf[]; + int size; +{ + register FILE *f; + + hostbuf[0] = '\0'; + f = fopen("/usr/include/whoami", "r"); + if (f != NULL) + { + (void) fgets(hostbuf, size, f); + fixcrlf(hostbuf, TRUE); + (void) fclose(f); + } + return (NULL); +} + /* +** GETAUTHINFO -- get the real host name asociated with a file descriptor +** +** Parameters: +** fd -- the descriptor +** may_be_forged -- an outage that is set to TRUE if the +** forward lookup of RealHostName does not match +** RealHostAddr; set to FALSE if they do match. +** +** Returns: +** The host name associated with this descriptor, if it can +** be determined. +** NULL otherwise. +** +** Side Effects: +** none +*/ + +char * +getauthinfo(fd, may_be_forged) + int fd; + bool *may_be_forged; +{ + *may_be_forged = FALSE; + return NULL; +} + /* +** MAPHOSTNAME -- turn a hostname into canonical form +** +** Parameters: +** map -- a pointer to the database map. +** name -- a buffer containing a hostname. +** avp -- a pointer to a (cf file defined) argument vector. +** statp -- an exit status (out parameter). +** +** Returns: +** mapped host name +** FALSE otherwise. +** +** Side Effects: +** Looks up the host specified in name. If it is not +** the canonical name for that host, replace it with +** the canonical name. If the name is unknown, or it +** is already the canonical name, leave it unchanged. +*/ + +/*ARGSUSED*/ +char * +host_map_lookup(map, name, avp, statp) + MAP *map; + char *name; + char **avp; + char *statp; +{ + register struct hostent *hp; + char *cp; + + hp = sm_gethostbyname(name); + if (hp == NULL) + { + *statp = EX_NOHOST; + return NULL; + } + if (bitset(MF_MATCHONLY, map->map_mflags)) + cp = map_rewrite(map, name, strlen(name), NULL); + else + cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), avp); + return cp; +} + +#endif /* DAEMON */ + /* +** HOST_MAP_INIT -- initialize host class structures +*/ + +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; + } + 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; +} + /* +** 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 + +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') + snprintf(buf, sizeof buf, "[UNIX: %.64s]", + sap->sunix.sun_path); + else + snprintf(buf, sizeof buf, "[UNIX: localhost]"); + return buf; +#endif + +#if NETINET + case AF_INET: + return inet_ntoa(sap->sin.sin_addr); +#endif + +#if NETLINK + case AF_LINK: + snprintf(buf, sizeof buf, "[LINK: %s]", + link_ntoa((struct sockaddr_dl *) &sap->sa)); + return buf; +#endif + default: + /* this case is needed when nothing is #defined */ + /* in order to keep the switch syntactically correct */ + break; + } + + /* unknown family -- just dump bytes */ + (void) snprintf(buf, sizeof buf, "Family %d: ", sap->sa.sa_family); + bp = &buf[strlen(buf)]; + ap = sap->sa.sa_data; + for (l = sizeof sap->sa.sa_data; --l >= 0; ) + { + (void) snprintf(bp, SPACELEFT(buf, bp), "%02x:", *ap++ & 0377); + 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; + int saveretry; + +#if NAMED_BIND + /* shorten name server timeout to avoid higher level timeouts */ + saveretry = _res.retry; + _res.retry = 3; +#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 + +#if NETISO + case AF_ISO: + hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr, + sizeof sap->siso.siso_addr, + AF_ISO); + break; +#endif + +#if NETUNIX + case AF_UNIX: + hp = NULL; + break; +#endif + + 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 (hp != NULL && hp->h_name[0] != '[' && + inet_addr(hp->h_name) == INADDR_NONE) + return denlstring((char *) hp->h_name, TRUE, TRUE); +#if NETUNIX + else if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0') + return "localhost"; +#endif + else + { + /* produce a dotted quad */ + static char buf[203]; + + (void) snprintf(buf, sizeof buf, "[%.200s]", anynet_ntoa(sap)); + return buf; + } +} + +#endif /* SOCK_STREAM */ diff --git a/contrib/sendmail/src/deliver.c b/contrib/sendmail/src/deliver.c new file mode 100644 index 0000000..0e5eb07 --- /dev/null +++ b/contrib/sendmail/src/deliver.c @@ -0,0 +1,3722 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)deliver.c 8.353 (Berkeley) 6/30/98"; +#endif /* not lint */ + +#include "sendmail.h" +#include +#include +#if NAMED_BIND +#include +#endif + +#if HASSETUSERCONTEXT +# include +#endif + +#if SMTP +extern char SmtpError[]; +#endif + +/* +** 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; + register ENVELOPE *ee; + ENVELOPE *splitenv = NULL; + int oldverbose = Verbose; + bool somedeliveries = FALSE, expensive = FALSE; + pid_t pid; + void sendenvelope __P((ENVELOPE *, int)); + + /* + ** If this message is to be discarded, don't bother sending + ** the message at all. + */ + + if (bitset(EF_DISCARD, e->e_flags)) + { + if (tTd(13, 1)) + printf("sendall: discarding id %s\n", e->e_id); + e->e_flags |= EF_CLRQUEUE; + if (LogLevel > 4) + sm_syslog(LOG_INFO, e->e_id, "discarded"); + markstats(e, NULL, TRUE); + 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)) + { + extern void printenvflags __P((ENVELOPE *)); + + printf("\n===== SENDALL: mode %c, id %s, e_from ", + mode, e->e_id); + printaddr(&e->e_from, FALSE); + printf("\te_flags = "); + printenvflags(e); + printf("sendqueue:\n"); + printaddr(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) + { + errno = 0; +#if QUEUE + queueup(e, mode == SM_QUEUE || mode == SM_DEFER); +#endif + e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; + syserr("554 Too many hops %d (%d max): from %s via %s, to %s", + e->e_hopcount, MaxHopCount, e->e_from.q_paddr, + RealHostName == NULL ? "localhost" : RealHostName, + e->e_sendqueue->q_paddr); + e->e_sendqueue->q_status = "5.4.6"; + return; + } + + /* + ** Do sender deletion. + ** + ** If the sender has the QQUEUEUP flag set, skip this. + ** This can happen if the name server is hosed when you + ** are trying to send mail. The result is that the sender + ** is instantiated in the queue as a recipient. + */ + + if (!bitset(EF_METOO, e->e_flags) && + !bitset(QQUEUEUP, e->e_from.q_flags)) + { + if (tTd(13, 5)) + { + printf("sendall: QDONTSEND "); + printaddr(&e->e_from, FALSE); + } + e->e_from.q_flags |= QDONTSEND; + (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 && + !bitset(QDONTSEND, q->q_flags) && + strcmp(q->q_owner, e->e_from.q_paddr) == 0) + q->q_owner = NULL; + } + + if (tTd(13, 25)) + { + printf("\nAfter first owner pass, sendq =\n"); + printaddr(e->e_sendqueue, TRUE); + } + + owner = ""; + otherowners = 1; + while (owner != NULL && otherowners > 0) + { + if (tTd(13, 28)) + printf("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)) + { + printf("Checking "); + printaddr(q, FALSE); + } + if (bitset(QDONTSEND, q->q_flags)) + { + if (tTd(13, 30)) + printf(" ... QDONTSEND\n"); + continue; + } + if (tTd(13, 29) && !tTd(13, 30)) + { + printf("Checking "); + printaddr(q, FALSE); + } + + if (q->q_owner != NULL) + { + if (owner == NULL) + { + if (tTd(13, 40)) + printf(" ... 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)) + printf(" ... Same owner = \"%s\"\n", + owner); + + /* make future comparisons cheap */ + q->q_owner = owner; + } + else + { + if (tTd(13, 40)) + printf(" ... Another owner \"%s\"\n", + q->q_owner); + otherowners++; + } + owner = q->q_owner; + } + else if (tTd(13, 40)) + printf(" ... Same owner = \"%s\"\n", + owner); + } + else + { + if (tTd(13, 40)) + printf(" ... Null owner\n"); + otherowners++; + } + + /* + ** 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 (bitset(QBADADDR|QQUEUEUP, q->q_flags)) + { + if (tTd(13, 30)) + printf(" ... QBADADDR|QQUEUEUP\n"); + continue; + } + if (NoConnect && !Verbose && + bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) + { + if (tTd(13, 30)) + printf(" ... expensive\n"); + q->q_flags |= QQUEUEUP; + expensive = TRUE; + } + else + { + if (tTd(13, 30)) + printf(" ... deliverable\n"); + somedeliveries = TRUE; + } + } + + if (owner != NULL && otherowners > 0) + { + extern HDR *copyheader __P((HDR *)); + extern ADDRESS *copyqueue __P((ADDRESS *)); + extern void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); + + /* + ** Split this envelope into two. + */ + + ee = (ENVELOPE *) xalloc(sizeof(ENVELOPE)); + *ee = *e; + ee->e_id = NULL; + (void) queuename(ee, '\0'); + + if (tTd(13, 1)) + printf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", + e->e_id, ee->e_id, owner, otherowners); + + ee->e_header = copyheader(e->e_header); + ee->e_sendqueue = copyqueue(e->e_sendqueue); + ee->e_errorqueue = copyqueue(e->e_errorqueue); + 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)) + { + printf("sendall(split): QDONTSEND "); + printaddr(&ee->e_from, FALSE); + } + ee->e_from.q_flags |= QDONTSEND; + ee->e_dfp = NULL; + ee->e_xfp = NULL; + ee->e_errormode = EM_MAIL; + ee->e_sibling = splitenv; + splitenv = ee; + + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (q->q_owner == owner) + { + q->q_flags |= QDONTSEND; + q->q_flags &= ~(QQUEUEUP|QBADADDR); + if (tTd(13, 6)) + printf("\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_flags |= QDONTSEND; + q->q_flags &= ~(QQUEUEUP|QBADADDR); + if (tTd(13, 6)) + printf("\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)) + printf("\t... moving %s to cloned envelope\n", + q->q_paddr); + } + } + + if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) + dup_queue_file(e, ee, 'd'); + openxscript(ee); + if (LogLevel > 4) + sm_syslog(LOG_INFO, ee->e_id, + "clone %s, owner=%s", + e->e_id, owner); + } + } + + if (owner != NULL) + { + setsender(owner, e, NULL, '\0', TRUE); + if (tTd(13, 5)) + { + printf("sendall(owner): QDONTSEND "); + printaddr(&e->e_from, FALSE); + } + e->e_from.q_flags |= QDONTSEND; + 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 && mode != SM_QUEUE && mode != SM_DEFER && + mode != SM_VERIFY) + { + if (tTd(13, 29)) + printf("No deliveries: auto-queuing\n"); + mode = SM_QUEUE; + + /* treat this as a delivery in terms of counting tries */ + e->e_dtime = curtime(); + if (!expensive) + e->e_ntries++; + for (ee = splitenv; ee != NULL; ee = ee->e_sibling) + { + ee->e_dtime = curtime(); + if (!expensive) + ee->e_ntries++; + } + } + +# if QUEUE + if ((mode == SM_QUEUE || mode == SM_DEFER || mode == SM_FORK || + (mode != SM_VERIFY && SuperSafe)) && + (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) + { + /* be sure everything is instantiated in the queue */ + queueup(e, mode == SM_QUEUE || mode == SM_DEFER); + for (ee = splitenv; ee != NULL; ee = ee->e_sibling) + queueup(ee, mode == SM_QUEUE || mode == SM_DEFER); + } +#endif /* QUEUE */ + + if (tTd(62, 10)) + checkfds("after envelope splitting"); + + /* + ** If we belong in background, fork now. + */ + + if (tTd(13, 20)) + { + printf("sendall: final mode = %c\n", mode); + if (tTd(13, 21)) + { + printf("\n================ Final Send Queue(s) =====================\n"); + printf("\n *** Envelope %s, e_from=%s ***\n", + e->e_id, e->e_from.q_paddr); + printaddr(e->e_sendqueue, TRUE); + for (ee = splitenv; ee != NULL; ee = ee->e_sibling) + { + printf("\n *** Envelope %s, e_from=%s ***\n", + ee->e_id, ee->e_from.q_paddr); + printaddr(ee->e_sendqueue, TRUE); + } + printf("==========================================================\n\n"); + } + } + switch (mode) + { + case SM_VERIFY: + Verbose = 2; + break; + + case SM_QUEUE: + case SM_DEFER: +# if HASFLOCK + queueonly: +# endif + if (e->e_nrcpts > 0) + e->e_flags |= EF_INQUEUE; + dropenvelope(e, splitenv != NULL); + for (ee = splitenv; ee != NULL; ee = ee->e_sibling) + { + if (ee->e_nrcpts > 0) + ee->e_flags |= EF_INQUEUE; + dropenvelope(ee, FALSE); + } + return; + + case SM_FORK: + if (e->e_xfp != NULL) + (void) fflush(e->e_xfp); + +# 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); + + /* 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); + + /* and save qid for reacquisition */ + ee->e_id = qid; + } + +# endif /* !HASFLOCK */ + + pid = fork(); + if (pid < 0) + { +# if HASFLOCK + goto queueonly; +# else + 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) xfclose(e->e_dfp, "sendenvelope dfp", e->e_id); + e->e_dfp = NULL; + e->e_flags &= ~EF_HAS_DF; + + /* can't call unlockqueue to avoid unlink of xfp */ + if (e->e_lockfp != NULL) + (void) xfclose(e->e_lockfp, "sendenvelope lockfp", e->e_id); + e->e_lockfp = NULL; +# endif + + /* make sure the parent doesn't own the envelope */ + e->e_id = NULL; + + /* catch intermediate zombie */ + (void) waitfor(pid); + return; + } + + /* double fork to avoid zombies */ + pid = fork(); + if (pid > 0) + exit(EX_OK); + + /* be sure we are immune from the terminal */ + disconnect(2, e); + + /* prevent parent from waiting if there was an error */ + if (pid < 0) + { +# if HASFLOCK + e->e_flags |= EF_INQUEUE; +# else + e->e_id = NULL; +# endif /* HASFLOCK */ + finis(); + } + + /* 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 + + /* + ** 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_id, FALSE, FALSE, ee); + ee->e_sibling = sibling; + } + (void) dowork(e->e_id, FALSE, FALSE, e); + finis(); +# endif /* !HASFLOCK */ + } + + sendenvelope(e, mode); + dropenvelope(e, TRUE); + for (ee = splitenv; ee != NULL; ee = ee->e_sibling) + { + CurEnv = ee; + if (mode != SM_VERIFY) + openxscript(ee); + sendenvelope(ee, mode); + dropenvelope(ee, TRUE); + } + CurEnv = e; + + Verbose = oldverbose; + if (mode == SM_FORK) + finis(); +} + +void +sendenvelope(e, mode) + register ENVELOPE *e; + int mode; +{ + register ADDRESS *q; + bool didany; + + if (tTd(13, 10)) + printf("sendenvelope(%s) e_flags=0x%lx\n", + e->e_id == NULL ? "[NOQUEUE]" : e->e_id, + e->e_flags); + if (LogLevel > 80) + sm_syslog(LOG_DEBUG, e->e_id, + "sendenvelope, flags=0x%x", + e->e_flags); + + /* + ** 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; + } + + /* + ** 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; + define(macid("{envid}", NULL), e->e_envid, e); + define(macid("{bodytype}", NULL), e->e_bodytype, e); + didany = FALSE; + + /* now run through the queue */ + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { +#if XDEBUG + char wbuf[MAXNAME + 20]; + + (void) snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", + MAXNAME, q->q_paddr); + checkfd012(wbuf); +#endif + if (mode == SM_VERIFY) + { + e->e_to = q->q_paddr; + if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) + { + 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 (!bitset(QDONTSEND|QBADADDR, q->q_flags)) + { + extern int deliver __P((ENVELOPE *, ADDRESS *)); + +# if QUEUE + /* + ** Checkpoint the send list every few addresses + */ + + if (e->e_nsent >= CheckpointInterval) + { + queueup(e, FALSE); + e->e_nsent = 0; + } +# endif /* QUEUE */ + (void) deliver(e, q); + didany = TRUE; + } + } + if (didany) + { + e->e_dtime = curtime(); + e->e_ntries++; + } + +#if XDEBUG + checkfd012("end of sendenvelope"); +#endif +} + /* +** DUP_QUEUE_FILE -- duplicate a queue file into a split queue +** +** Parameters: +** e -- the existing envelope +** ee -- the new envelope +** type -- the queue file type (e.g., 'd') +** +** Returns: +** none +*/ + +void +dup_queue_file(e, ee, type) + struct envelope *e, *ee; + int type; +{ + char f1buf[MAXQFNAME], f2buf[MAXQFNAME]; + + ee->e_dfp = NULL; + ee->e_xfp = NULL; + snprintf(f1buf, sizeof f1buf, "%s", queuename(e, type)); + snprintf(f2buf, sizeof f2buf, "%s", queuename(ee, type)); + if (link(f1buf, f2buf) < 0) + { + int saverrno = errno; + + syserr("sendall: link(%s, %s)", f1buf, f2buf); + if (saverrno == 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*/ + } + } + } +} + /* +** 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 + +# define DOFORK(fORKfN) \ +{\ + register int i;\ +\ + for (i = NFORKTRIES; --i >= 0; )\ + {\ + pid = fORKfN();\ + if (pid >= 0)\ + break;\ + if (i > 0)\ + 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. +*/ + +int +dofork() +{ + register pid_t pid = -1; + + DOFORK(fork); + return (pid); +} + /* +** 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. +** +** 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. +*/ + +#ifndef NO_UID +# define NO_UID -1 +#endif +#ifndef NO_GID +# define NO_GID -1 +#endif + +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; + ADDRESS *volatile contextaddr = NULL; + register MCI *volatile mci; + register ADDRESS *to = firstto; + volatile bool clever = FALSE; /* running user smtp to this mailer */ + ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ + int rcode; /* response code */ + int lmtp_rcode = EX_OK; + char *firstsig; /* signature of firstto */ + pid_t pid = -1; + char *volatile curhost; + register u_short port = 0; + time_t xstart; + bool suidwarn; + bool anyok; /* at least one address was OK */ + bool goodmxfound = FALSE; /* at least one MX was OK */ + int mpvect[2]; + int rpvect[2]; + char *pv[MAXPV+1]; + char tobuf[TOBUFSIZE]; /* text line of to people */ + char buf[MAXNAME + 1]; + char rpathbuf[MAXNAME + 1]; /* translated return path */ + extern int checkcompat __P((ADDRESS *, ENVELOPE *)); + extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int)); + + errno = 0; + if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags)) + return (0); + + suidwarn = geteuid() == 0; + +#if NAMED_BIND + /* unless interactive, try twice, over a minute */ + if (OpMode == MD_DAEMON || OpMode == MD_SMTP) + { + _res.retrans = 30; + _res.retry = 2; + } +#endif + + m = to->q_mailer; + host = to->q_host; + CurEnv = e; /* just in case */ + e->e_statmsg = NULL; +#if SMTP + SmtpError[0] = '\0'; +#endif + xstart = curtime(); + + if (tTd(10, 1)) + printf("\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)) + { + define(macid("{client_name}", NULL), "", e); + define(macid("{client_addr}", NULL), "", e); + define(macid("{client_port}", NULL), "", e); + } + + /* + ** Do initial argv setup. + ** Insert the mailer name. Notice that $x expansion is + ** 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; + if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) + p = e->e_sender; + else + p = e->e_from.q_paddr; + p = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); + if (strlen(p) >= (SIZE_T) sizeof rpathbuf) + { + p = shortenstring(p, MAXSHORTSTR); + syserr("remotename: huge return %s", p); + } + snprintf(rpathbuf, sizeof rpathbuf, "%s", p); + define('g', rpathbuf, e); /* translated return path */ + define('h', host, e); /* to host */ + Errors = 0; + pvp = pv; + *pvp++ = m->m_argv[0]; + + /* insert -f or -r flag as appropriate */ + if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags))) + { + if (bitnset(M_FOPT, m->m_flags)) + *pvp++ = "-f"; + else + *pvp++ = "-r"; + *pvp++ = newstr(rpathbuf); + } + + /* + ** 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++ = newstr(buf); + if (pvp >= &pv[MAXPV - 3]) + { + syserr("554 Too many parameters to %s before $u", pv[0]); + return (-1); + } + } + + /* + ** 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 SMTP */ +# if SMTP + clever = TRUE; + *pvp = NULL; +# else /* SMTP */ + /* oops! we don't implement SMTP */ + syserr("554 SMTP style mailer not implemented"); + return (EX_SOFTWARE); +# endif /* SMTP */ + } + + /* + ** 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. + */ + + tobuf[0] = '\0'; + e->e_to = tobuf; + ctladdr = NULL; + firstsig = hostsignature(firstto->q_mailer, firstto->q_host, e); + for (; to != NULL; to = to->q_next) + { + /* avoid sending multiple recipients to dumb mailers */ + if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags)) + break; + + /* if already sent or not for this host, don't send */ + if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags) || + to->q_mailer != firstto->q_mailer || + strcmp(hostsignature(to->q_mailer, to->q_host, e), firstsig) != 0) + continue; + + /* avoid overflowing tobuf */ + if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2)) + break; + + if (tTd(10, 1)) + { + printf("\nsend to "); + printaddr(to, FALSE); + } + + /* compute effective uid/gid when sending */ + if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) + contextaddr = ctladdr = getctladdr(to); + + if (tTd(10, 2)) + { + printf("ctladdr="); + printaddr(ctladdr, FALSE); + } + + user = to->q_user; + e->e_to = to->q_paddr; + if (tTd(10, 5)) + { + printf("deliver: QDONTSEND "); + printaddr(to, FALSE); + } + to->q_flags |= QDONTSEND; + + /* + ** Check to see that these people are allowed to + ** talk to each other. + */ + + if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize) + { + 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"; + usrerr("552 Message is too large; %ld bytes max", m->m_maxsize); + markfailure(e, to, NULL, EX_UNAVAILABLE); + giveresponse(EX_UNAVAILABLE, m, NULL, ctladdr, xstart, e); + continue; + } +#if NAMED_BIND + h_errno = 0; +#endif + + /* do config file checking of compatibility */ + rcode = rscheck("check_compat", + e->e_from.q_paddr, to->q_paddr, e); + if (rcode == EX_OK) + { + /* do in-code checking */ + rcode = checkcompat(to, e); + } + if (rcode != EX_OK) + { + markfailure(e, to, NULL, rcode); + giveresponse(rcode, m, NULL, ctladdr, xstart, e); + continue; + } + + /* + ** Strip quote bits from names if the mailer is dumb + ** about them. + */ + + if (bitnset(M_STRIPQ, m->m_flags)) + { + stripquotes(user); + stripquotes(host); + } + + /* 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 (bitset(QBADADDR|QQUEUEUP, to->q_flags)) + 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) + { + define('u', user, e); /* to user */ + p = to->q_home; + if (p == NULL && ctladdr != NULL) + p = ctladdr->q_home; + define('z', p, e); /* user's home */ + 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, m, NULL, ctladdr, xstart, e); + markfailure(e, to, NULL, rcode); + e->e_nsent++; + if (rcode == EX_OK) + { + to->q_flags |= QSENT; + if (bitnset(M_LOCALMAILER, m->m_flags) && + bitset(QPINGONSUCCESS, to->q_flags)) + { + to->q_flags |= QDELIVERED; + to->q_status = "2.1.5"; + fprintf(e->e_xfp, "%s... Successfully delivered\n", + to->q_paddr); + } + } + to->q_statdate = curtime(); + markstats(e, to, FALSE); + 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; + + /* create list of users for error messages */ + (void) strcat(tobuf, ","); + (void) strcat(tobuf, to->q_paddr); + define('u', user, e); /* to user */ + p = to->q_home; + if (p == NULL && ctladdr != NULL) + p = ctladdr->q_home; + define('z', p, e); /* user's home */ + + /* + ** Expand out this user into argument list. + */ + + if (!clever) + { + expand(*mvp, buf, sizeof buf, e); + *pvp++ = newstr(buf); + if (pvp >= &pv[MAXPV - 2]) + { + /* allow some space for trailing parms */ + break; + } + } + } + + /* see if any addresses still exist */ + if (tobuf[0] == '\0') + { + define('g', (char *) NULL, e); + return (0); + } + + /* print out messages as full list */ + e->e_to = tobuf + 1; + + /* + ** Fill out any parameters after the $u parameter. + */ + + while (!clever && *++mvp != NULL) + { + expand(*mvp, buf, sizeof buf, e); + *pvp++ = newstr(buf); + if (pvp >= &pv[MAXPV]) + syserr("554 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 + + if (tTd(11, 1)) + { + printf("openmailer:"); + printav(pv); + } + errno = 0; +#if NAMED_BIND + h_errno = 0; +#endif + + 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 */ + snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", + shortenstring(e->e_to, MAXSHORTSTR), m->m_name); + checkfd012(wbuf); + } +#endif + + /* 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))))) + { + usrerr("554 Cannot send 8-bit data to 7-bit destination"); + rcode = EX_DATAERR; + e->e_status = "5.6.3"; + 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) + { + mci = (MCI *) xalloc(sizeof *mci); + bzero((char *) mci, sizeof *mci); + mci->mci_in = stdin; + mci->mci_out = stdout; + mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; + mci->mci_mailer = m; + } + else if (strcmp(m->m_mailer, "[IPC]") == 0 || + strcmp(m->m_mailer, "[TCP]") == 0) + { +#if DAEMON + register int i; + + if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') + { + syserr("null host name for %s mailer", m->m_mailer); + rcode = EX_CONFIG; + goto give_up; + } + + CurHostName = pv[1]; + curhost = hostsignature(m, pv[1], e); + + if (curhost == NULL || curhost[0] == '\0') + { + syserr("null host signature for %s", pv[1]); + rcode = EX_CONFIG; + goto give_up; + } + + if (!clever) + { + syserr("554 non-clever IPC"); + rcode = EX_CONFIG; + goto give_up; + } + if (pv[2] != NULL) + { + port = htons(atoi(pv[2])); + if (port == 0) + { + struct servent *sp = getservbyname(pv[2], "tcp"); + + if (sp == NULL) + syserr("Service %s unknown", pv[2]); + else + port = sp->s_port; + } + } +tryhost: + while (*curhost != '\0') + { + static char hostbuf[MAXNAME + 1]; + extern int makeconnection __P((char *, u_short, MCI *, ENVELOPE *)); + + /* pull the next host from the signature */ + p = strchr(curhost, ':'); + if (p == NULL) + p = (char *) &curhost[strlen(curhost)]; + if (p == curhost) + { + syserr("deliver: null host name in signature"); + curhost++; + continue; + } + i = p - curhost; + if (i >= sizeof hostbuf) + i = sizeof hostbuf - 1; + strncpy(hostbuf, curhost, i); + hostbuf[i] = '\0'; + if (*p != '\0') + p++; + curhost = p; + + /* see if we already know that this host is fried */ + CurHostName = hostbuf; + mci = mci_get(hostbuf, m); + if (mci->mci_state != MCIS_CLOSED) + { + if (tTd(11, 1)) + { + printf("openmailer: "); + mci_dump(mci, FALSE); + } + CurHostName = mci->mci_host; + message("Using cached %sSMTP connection to %s via %s...", + bitset(MCIF_ESMTP, mci->mci_flags) ? "E" : "", + hostbuf, m->m_name); + break; + } + mci->mci_mailer = m; + if (mci->mci_exitstat != EX_OK) + { + if (mci->mci_exitstat == EX_TEMPFAIL) + goodmxfound = TRUE; + continue; + } + + if (mci_lock_host(mci) != EX_OK) + { + mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); + goodmxfound = TRUE; + continue; + } + + /* try the connection */ + setproctitle("%s %s: %s", e->e_id, hostbuf, "user open"); + if (port == 0) + message("Connecting to %s via %s...", + hostbuf, m->m_name); + else + message("Connecting to %s port %d via %s...", + hostbuf, ntohs(port), m->m_name); + i = makeconnection(hostbuf, port, mci, e); + mci->mci_lastuse = curtime(); + mci->mci_exitstat = i; + mci->mci_errno = errno; +#if NAMED_BIND + mci->mci_herrno = h_errno; +#endif + if (i == EX_OK) + { + goodmxfound = TRUE; + mci->mci_state = MCIS_OPENING; + mci_cache(mci); + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d === CONNECT %s\n", + (int) getpid(), hostbuf); + break; + } + else + { + if (tTd(11, 1)) + printf("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 /* no DAEMON */ + syserr("554 openmailer: no IPC"); + if (tTd(11, 1)) + printf("openmailer: NULL\n"); + rcode = EX_UNAVAILABLE; + goto give_up; +#endif /* DAEMON */ + } + else + { + /* flush any expired connections */ + (void) mci_scan(NULL); + mci = NULL; + +#if SMTP + 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); + goto do_transfer; + } + } +#endif + + /* 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; + + fprintf(TrafficLogFile, "%05d === EXEC", (int) getpid()); + for (av = pv; *av != NULL; av++) + fprintf(TrafficLogFile, " %s", *av); + fprintf(TrafficLogFile, "\n"); + } + +#if XDEBUG + checkfd012("before creating mail pipe"); +#endif + + /* 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)) + printf("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)) + printf("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] == fileno(e->e_lockfp) || + mpvect[1] == fileno(e->e_lockfp)))) + { + 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], + fileno(e->e_lockfp)); + } +#endif + + /* if this mailer speaks smtp, create a return pipe */ +#if SMTP + if (clever) + { + if (pipe(rpvect) < 0) + { + syserr("%s... openmailer(%s): pipe (from mailer)", + shortenstring(e->e_to, MAXSHORTSTR), + m->m_name); + (void) close(mpvect[0]); + (void) close(mpvect[1]); + if (tTd(11, 1)) + printf("openmailer: NULL\n"); + rcode = EX_OSERR; + goto give_up; + } +# if XDEBUG + checkfdopen(rpvect[0], "rpvect[0]"); + checkfdopen(rpvect[1], "rpvect[1]"); +# endif + } +#endif + + /* + ** Actually fork the mailer process. + ** DOFORK is clever about retrying. + ** + ** Dispose of SIGCHLD signal catchers that may be laying + ** around so that endmail will get it. + */ + + if (e->e_xfp != NULL) + (void) fflush(e->e_xfp); /* for debugging */ + (void) fflush(stdout); + (void) setsignal(SIGCHLD, SIG_DFL); + DOFORK(FORK); + /* pid is set by DOFORK */ + if (pid < 0) + { + /* failure */ + syserr("%s... openmailer(%s): cannot fork", + shortenstring(e->e_to, MAXSHORTSTR), m->m_name); + (void) close(mpvect[0]); + (void) close(mpvect[1]); +#if SMTP + if (clever) + { + (void) close(rpvect[0]); + (void) close(rpvect[1]); + } +#endif + if (tTd(11, 1)) + printf("openmailer: NULL\n"); + rcode = EX_OSERR; + goto give_up; + } + else if (pid == 0) + { + int i; + int saveerrno; + int new_euid = NO_UID; + int new_ruid = NO_UID; + int new_gid = NO_GID; + struct stat stb; + extern int DtableSize; + + if (e->e_lockfp != NULL) + (void) close(fileno(e->e_lockfp)); + + /* child -- set up input & exec mailer */ + (void) setsignal(SIGINT, SIG_IGN); + (void) setsignal(SIGHUP, SIG_IGN); + (void) setsignal(SIGTERM, SIG_DFL); + + if (m != FileMailer || stat(tochain->q_user, &stb) < 0) + stb.st_mode = 0; + +#if HASSETUSERCONTEXT + /* + ** Set user resources. + */ + + if (contextaddr != NULL) + { + struct passwd *pwd; + + if (contextaddr->q_ruser != NULL) + pwd = sm_getpwnam(contextaddr->q_ruser); + else + pwd = sm_getpwnam(contextaddr->q_user); + if (pwd != NULL) + (void) setusercontext(NULL, + pwd, pwd->pw_uid, + LOGIN_SETRESOURCES|LOGIN_SETPRIORITY); + } +#endif + + /* tweak niceness */ + if (m->m_nice != 0) + nice(m->m_nice); + + /* reset group id */ + if (bitnset(M_SPECIFIC_UID, m->m_flags)) + 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) + { + char *u = ctladdr->q_ruser; + + if (u == NULL) + u = ctladdr->q_user; + + if (initgroups(u, ctladdr->q_gid) == -1 && suidwarn) + syserr("openmailer: initgroups(%s, %d) failed", + u, ctladdr->q_gid); + } + else + { + GIDSET_T gidset[1]; + + gidset[0] = ctladdr->q_gid; + if (setgroups(1, gidset) == -1 && suidwarn) + syserr("openmailer: setgroups() failed"); + } + new_gid = ctladdr->q_gid; + } + else + { + if (!DontInitGroups) + { + if (initgroups(DefUser, DefGid) == -1 && suidwarn) + syserr("openmailer: initgroups(%s, %d) failed", + DefUser, DefGid); + } + else + { + GIDSET_T gidset[1]; + + gidset[0] = DefGid; + if (setgroups(1, gidset) == -1 && suidwarn) + syserr("openmailer: setgroups() failed"); + } + if (m->m_gid == 0) + new_gid = DefGid; + else + new_gid = m->m_gid; + } + if (new_gid != NO_GID && setgid(new_gid) < 0 && suidwarn) + syserr("openmailer: setgid(%ld) failed", + (long) new_gid); + + /* reset user id */ + endpwent(); + if (bitnset(M_SPECIFIC_UID, m->m_flags)) + new_euid = m->m_uid; + 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 != 0) + new_ruid = m->m_uid; + else + new_ruid = DefUid; + if (new_euid != NO_UID) + { + vendor_set_uid(new_euid); +#if USESETEUID + if (seteuid(new_euid) < 0 && suidwarn) + syserr("openmailer: seteuid(%ld) failed", + (long) new_euid); +#else +# if HASSETREUID + if (setreuid(new_ruid, new_euid) < 0 && suidwarn) + syserr("openmailer: setreuid(%ld, %ld) failed", + (long) new_ruid, (long) new_euid); +# else + if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) + syserr("openmailer: setuid(%ld) failed", + (long) new_euid); +# endif +#endif + } + 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); + } + + if (tTd(11, 2)) + printf("openmailer: running as r/euid=%d/%d\n", + (int) getuid(), (int) geteuid()); + + /* move into some "safe" directory */ + if (m->m_execdir != NULL) + { + char *q; + char buf[MAXLINE + 1]; + + for (p = m->m_execdir; p != NULL; p = q) + { + q = strchr(p, ':'); + if (q != NULL) + *q = '\0'; + expand(p, buf, sizeof buf, e); + if (q != NULL) + *q++ = ':'; + if (tTd(11, 20)) + printf("openmailer: trydir %s\n", + buf); + if (buf[0] != '\0' && chdir(buf) >= 0) + break; + } + } + + /* arrange to filter std & diag output of command */ +#if SMTP + if (clever) + { + (void) close(rpvect[0]); + if (dup2(rpvect[1], STDOUT_FILENO) < 0) + { + syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", + shortenstring(e->e_to, MAXSHORTSTR), + m->m_name, rpvect[1]); + _exit(EX_OSERR); + } + (void) close(rpvect[1]); + } + else + { + /* put mailer output in transcript */ + if (dup2(fileno(e->e_xfp), STDOUT_FILENO) < 0) + { + syserr("%s... openmailer(%s): cannot dup xscript %d for stdout", + shortenstring(e->e_to, MAXSHORTSTR), + m->m_name, fileno(e->e_xfp)); + _exit(EX_OSERR); + } + } +#endif + 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 */ + for (i = 3; i < DtableSize; i++) + { + register int j; + + if ((j = fcntl(i, F_GETFD, 0)) != -1) + (void) fcntl(i, F_SETFD, j | 1); + } + + /* run disconnected from terminal */ + (void) setsid(); + + /* try to execute the mailer */ + execve(m->m_mailer, (ARGV_T) pv, (ARGV_T) UserEnviron); + saveerrno = errno; + syserr("Cannot exec %s", m->m_mailer); + if (bitnset(M_LOCALMAILER, m->m_flags) || + transienterror(saveerrno)) + _exit(EX_OSERR); + _exit(EX_UNAVAILABLE); + } + + /* + ** Set up return value. + */ + + if (mci == NULL) + { + mci = (MCI *) xalloc(sizeof *mci); + bzero((char *) mci, sizeof *mci); + } + 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 = fdopen(mpvect[1], "w"); + if (mci->mci_out == NULL) + { + syserr("deliver: cannot create mailer output channel, fd=%d", + mpvect[1]); + (void) close(mpvect[1]); +#if SMTP + if (clever) + { + (void) close(rpvect[0]); + (void) close(rpvect[1]); + } +#endif + rcode = EX_OSERR; + goto give_up; + } +#if SMTP + if (clever) + { + (void) close(rpvect[1]); + mci->mci_in = fdopen(rpvect[0], "r"); + if (mci->mci_in == NULL) + { + syserr("deliver: cannot create mailer input channel, fd=%d", + mpvect[1]); + (void) close(rpvect[0]); + fclose(mci->mci_out); + mci->mci_out = NULL; + rcode = EX_OSERR; + goto give_up; + } + } + else +#endif + { + mci->mci_flags |= MCIF_TEMP; + mci->mci_in = NULL; + } + } + + /* + ** 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 SMTP + if (clever && mci->mci_state != MCIS_CLOSED) + { + extern void smtpinit __P((MAILER *, MCI *, ENVELOPE *)); + + smtpinit(m, mci, e); + } +#endif + +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 && + (strcasecmp(p, "quoted-printable") == 0 || + strcasecmp(p, "base64") == 0) && + (p = hvalue("Content-Type", e->e_header)) != NULL) + { + /* may want to convert 7 -> 8 */ + /* XXX should really parse it here -- and use a class XXX */ + if (strncasecmp(p, "text/plain", 10) == 0 && + (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) + mci->mci_flags |= MCIF_CVT7TO8; + } +#endif + + if (tTd(11, 1)) + { + printf("openmailer: "); + mci_dump(mci, FALSE); + } + + if (mci->mci_state != MCIS_OPEN) + { + /* couldn't open the mailer */ + rcode = mci->mci_exitstat; + errno = mci->mci_errno; +#if NAMED_BIND + h_errno = mci->mci_herrno; +#endif + if (rcode == EX_OK) + { + /* shouldn't happen */ + syserr("554 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", + (long) mci, rcode, errno, mci->mci_state, + firstsig); + mci_dump_all(TRUE); + rcode = EX_SOFTWARE; + } +#if DAEMON + else if (curhost != NULL && *curhost != '\0') + { + /* try next MX site */ + goto tryhost; + } +#endif + } + else if (!clever) + { + /* + ** Format and send message. + */ + + mci->mci_contentlen = 0; + putfromline(mci, e); + (*e->e_puthdr)(mci, e->e_header, e); + (*e->e_putbody)(mci, e, NULL); + + /* get the exit status */ + rcode = endmailer(mci, e, pv); + } + else +#if SMTP + { + extern int smtpmailfrom __P((MAILER *, MCI *, ENVELOPE *)); + extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *)); + extern int smtpdata __P((MAILER *, MCI *, ENVELOPE *)); + + /* + ** Send the MAIL FROM: protocol + */ + + rcode = smtpmailfrom(m, mci, e); + if (rcode == EX_OK) + { + register char *t = tobuf; + register int i; + + /* send the recipient list */ + tobuf[0] = '\0'; + for (to = tochain; to != NULL; to = to->q_tchain) + { + e->e_to = to->q_paddr; + if (strlen(to->q_paddr) + (t - tobuf) + 2 > sizeof tobuf) + { + /* not enough room */ + continue; + } + else if ((i = smtprcpt(to, m, mci, e)) != EX_OK) + { + markfailure(e, to, mci, i); + giveresponse(i, m, mci, ctladdr, xstart, e); + } + else + { + *t++ = ','; + for (p = to->q_paddr; *p; *t++ = *p++) + continue; + *t = '\0'; + } + } + + /* now send the data */ + if (tobuf[0] == '\0') + { + 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); + } + } +# if DAEMON + if (rcode == EX_TEMPFAIL && curhost != NULL && *curhost != '\0') + { + /* try next MX site */ + goto tryhost; + } +# endif + } +#else /* not SMTP */ + { + syserr("554 deliver: need SMTP compiled to use clever mailer"); + rcode = EX_CONFIG; + goto give_up; + } +#endif /* SMTP */ +#if NAMED_BIND + if (ConfigLevel < 2) + _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ +#endif + + 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 SMTP + if (bitnset(M_LMTP, m->m_flags)) + { + lmtp_rcode = rcode; + tobuf[0] = '\0'; + anyok = FALSE; + } + else +#endif + anyok = rcode == EX_OK; + + for (to = tochain; to != NULL; to = to->q_tchain) + { + /* see if address already marked */ + if (bitset(QBADADDR|QQUEUEUP, to->q_flags)) + continue; + +#if SMTP + /* if running LMTP, get the status for each address */ + if (bitnset(M_LMTP, m->m_flags)) + { + extern int smtpgetstat __P((MAILER *, MCI *, ENVELOPE *)); + + if (lmtp_rcode == EX_OK) + rcode = smtpgetstat(m, mci, e); + if (rcode == EX_OK) + { + if (strlen(to->q_paddr) + strlen(tobuf) + 2 >= sizeof tobuf) + { + syserr("LMTP tobuf overflow"); + } + else + { + strcat(tobuf, ","); + strcat(tobuf, to->q_paddr); + } + anyok = TRUE; + } + else + { + e->e_to = to->q_paddr; + markfailure(e, to, mci, rcode); + giveresponse(rcode, m, mci, ctladdr, xstart, e); + e->e_to = tobuf + 1; + continue; + } + } + else +#endif + { + /* mark bad addresses */ + if (rcode != EX_OK) + { + if (goodmxfound && rcode == EX_NOHOST) + rcode = EX_TEMPFAIL; + markfailure(e, to, mci, rcode); + continue; + } + } + + /* successful delivery */ + to->q_flags |= QSENT; + to->q_statdate = curtime(); + e->e_nsent++; + if (bitnset(M_LOCALMAILER, m->m_flags) && + bitset(QPINGONSUCCESS, to->q_flags)) + { + to->q_flags |= QDELIVERED; + to->q_status = "2.1.5"; + fprintf(e->e_xfp, "%s... Successfully delivered\n", + to->q_paddr); + } + else if (bitset(QPINGONSUCCESS, to->q_flags) && + bitset(QPRIMARY, to->q_flags) && + !bitset(MCIF_DSN, mci->mci_flags)) + { + to->q_flags |= QRELAYED; + fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n", + to->q_paddr); + } + } + +#if SMTP + 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_ACTIVE) + mci->mci_state = MCIS_OPEN; + } +#endif + + if (tobuf[0] != '\0') + giveresponse(rcode, m, mci, ctladdr, xstart, e); + if (anyok) + markstats(e, tochain, FALSE); + mci_store_persistent(mci); + +#if SMTP + /* now close the connection */ + if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && + !bitset(MCIF_CACHED, mci->mci_flags)) + smtpquit(m, mci, e); +#endif + + /* + ** Restore state and return. + */ + +#if XDEBUG + { + char wbuf[MAXLINE]; + + /* make absolutely certain 0, 1, and 2 are in use */ + snprintf(wbuf, sizeof wbuf, "%s... end of deliver(%s)", + e->e_to == NULL ? "NO-TO-LIST" + : shortenstring(e->e_to, MAXSHORTSTR), + m->m_name); + checkfd012(wbuf); + } +#endif + + errno = 0; + define('g', (char *) NULL, e); + 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. +** +** 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) + register ENVELOPE *e; + register ADDRESS *q; + register MCI *mci; + int rcode; +{ + char *stat = NULL; + + switch (rcode) + { + case EX_OK: + break; + + case EX_TEMPFAIL: + case EX_IOERR: + case EX_OSERR: + q->q_flags |= QQUEUEUP; + q->q_flags &= ~QDONTSEND; + break; + + default: + q->q_flags |= QBADADDR; + break; + } + + /* find most specific error code possible */ + if (mci != NULL && mci->mci_status != NULL) + { + q->q_status = mci->mci_status; + if (mci->mci_rstatus != NULL) + q->q_rstatus = newstr(mci->mci_rstatus); + else + q->q_rstatus = NULL; + } + else if (e->e_status != NULL) + { + q->q_status = e->e_status; + q->q_rstatus = NULL; + } + else + { + switch (rcode) + { + case EX_USAGE: + stat = "5.5.4"; + break; + + case EX_DATAERR: + stat = "5.5.2"; + break; + + case EX_NOUSER: + stat = "5.1.1"; + break; + + case EX_NOHOST: + stat = "5.1.2"; + break; + + case EX_NOINPUT: + case EX_CANTCREAT: + case EX_NOPERM: + stat = "5.3.0"; + break; + + case EX_UNAVAILABLE: + case EX_SOFTWARE: + case EX_OSFILE: + case EX_PROTOCOL: + case EX_CONFIG: + stat = "5.5.0"; + break; + + case EX_OSERR: + case EX_IOERR: + stat = "4.5.0"; + break; + + case EX_TEMPFAIL: + stat = "4.2.0"; + break; + } + if (stat != NULL) + q->q_status = stat; + } + + q->q_statdate = curtime(); + if (CurHostName != NULL && CurHostName[0] != '\0') + q->q_statmta = newstr(CurHostName); + if (rcode != EX_OK && q->q_rstatus == NULL && + q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && + strcasecmp(q->q_mailer->m_diagtype, "UNIX") == 0) + { + char buf[30]; + + (void) snprintf(buf, sizeof buf, "%d", rcode); + q->q_rstatus = newstr(buf); + } +} + /* +** 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: +** pid -- pid of mailer. +** e -- the current envelope. +** pv -- the parameter vector that invoked the mailer +** (for error messages). +** +** Returns: +** exit code of mailer. +** +** Side Effects: +** none. +*/ + +int +endmailer(mci, e, pv) + register MCI *mci; + register ENVELOPE *e; + char **pv; +{ + int st; + + mci_unlock_host(mci); + + /* close any connections */ + if (mci->mci_in != NULL) + (void) xfclose(mci->mci_in, mci->mci_mailer->m_name, "mci_in"); + if (mci->mci_out != NULL) + (void) xfclose(mci->mci_out, mci->mci_mailer->m_name, "mci_out"); + mci->mci_in = mci->mci_out = NULL; + mci->mci_state = MCIS_CLOSED; + + /* in the IPC case there is nothing to wait for */ + if (mci->mci_pid == 0) + return (EX_OK); + +#if _FFR_TIMEOUT_WAIT + put a timeout around the wait +#endif + + /* wait for the mailer process to die and collect status */ + st = waitfor(mci->mci_pid); + 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 mailer %s died with signal %o", + mci->mci_mailer->m_name, st); + + /* log the arguments */ + if (pv != NULL && e->e_xfp != NULL) + { + register char **av; + + fprintf(e->e_xfp, "Arguments:"); + for (av = pv; *av != NULL; av++) + fprintf(e->e_xfp, " %s", *av); + fprintf(e->e_xfp, "\n"); + } + + ExitStat = EX_TEMPFAIL; + return (EX_TEMPFAIL); +} + /* +** GIVERESPONSE -- Interpret an error response from a mailer +** +** Parameters: +** stat -- the status code from the mailer (high byte +** only; core dumps must have been taken care of +** already). +** 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. +** +** Returns: +** none. +** +** Side Effects: +** Errors may be incremented. +** ExitStat may be set. +*/ + +void +giveresponse(stat, m, mci, ctladdr, xstart, e) + int stat; + register MAILER *m; + register MCI *mci; + ADDRESS *ctladdr; + time_t xstart; + ENVELOPE *e; +{ + register const char *statmsg; + extern char *SysExMsg[]; + register int i; + extern int N_SysEx; + char buf[MAXLINE]; + + if (e == NULL) + syserr("giveresponse: null envelope"); + + /* + ** Compute status message from code. + */ + + i = stat - EX__BASE; + if (stat == 0) + { + statmsg = "250 Sent"; + if (e->e_statmsg != NULL) + { + (void) snprintf(buf, sizeof buf, "%s (%s)", + statmsg, shortenstring(e->e_statmsg, 403)); + statmsg = buf; + } + } + else if (i < 0 || i >= N_SysEx) + { + (void) snprintf(buf, sizeof buf, "554 unknown mailer error %d", + stat); + stat = EX_UNAVAILABLE; + statmsg = buf; + } + else if (stat == EX_TEMPFAIL) + { + char *bp = buf; + + snprintf(bp, SPACELEFT(buf, bp), "%s", SysExMsg[i] + 1); + bp += strlen(bp); +#if NAMED_BIND + if (h_errno == TRY_AGAIN) + statmsg = errstring(h_errno+E_DNSBASE); + else +#endif + { + if (errno != 0) + statmsg = errstring(errno); + else + { +#if SMTP + statmsg = SmtpError; +#else /* SMTP */ + statmsg = NULL; +#endif /* SMTP */ + } + } + if (statmsg != NULL && statmsg[0] != '\0') + snprintf(bp, SPACELEFT(buf, bp), ": %s", statmsg); + statmsg = buf; + } +#if NAMED_BIND + else if (stat == EX_NOHOST && h_errno != 0) + { + statmsg = errstring(h_errno + E_DNSBASE); + (void) snprintf(buf, sizeof buf, "%s (%s)", + SysExMsg[i] + 1, statmsg); + statmsg = buf; + } +#endif + else + { + statmsg = SysExMsg[i]; + if (*statmsg++ == ':' && errno != 0) + { + (void) snprintf(buf, sizeof buf, "%s: %s", + statmsg, errstring(errno)); + statmsg = buf; + } + } + + /* + ** Print the message as appropriate + */ + + if (stat == EX_OK || stat == EX_TEMPFAIL) + { + extern char MsgBuf[]; + + message("%s", &statmsg[4]); + if (stat == EX_TEMPFAIL && e->e_xfp != NULL) + fprintf(e->e_xfp, "%s\n", &MsgBuf[4]); + } + else + { + char mbuf[8]; + + Errors++; + snprintf(mbuf, sizeof mbuf, "%.3s %%s", statmsg); + usrerr(mbuf, &statmsg[4]); + } + + /* + ** 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 > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6)) + logdelivery(m, mci, &statmsg[4], ctladdr, xstart, e); + + if (tTd(11, 2)) + printf("giveresponse: stat=%d, e->e_message=%s\n", + stat, e->e_message == NULL ? "" : e->e_message); + + if (stat != EX_TEMPFAIL) + setstat(stat); + if (stat != EX_OK && (stat != EX_TEMPFAIL || e->e_message == NULL)) + { + if (e->e_message != NULL) + free(e->e_message); + e->e_message = newstr(&statmsg[4]); + } + errno = 0; +#if NAMED_BIND + h_errno = 0; +#endif +} + /* +** 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 occuring when no connection is active. +** stat -- 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, stat, ctladdr, xstart, e) + MAILER *m; + register MCI *mci; + const char *stat; + ADDRESS *ctladdr; + time_t xstart; + register ENVELOPE *e; +{ + register char *bp; + register char *p; + int l; + char buf[1024]; + +# if (SYSLOG_BUFSIZE) >= 256 + /* ctladdr: max 106 bytes */ + bp = buf; + if (ctladdr != NULL) + { + snprintf(bp, SPACELEFT(buf, bp), ", ctladdr=%s", + shortenstring(ctladdr->q_paddr, 83)); + bp += strlen(bp); + if (bitset(QGOODUID, ctladdr->q_flags)) + { + (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", + ctladdr->q_uid, ctladdr->q_gid); + bp += strlen(bp); + } + } + + /* delay & xdelay: max 41 bytes */ + snprintf(bp, SPACELEFT(buf, bp), ", delay=%s", + pintvl(curtime() - e->e_ctime, TRUE)); + bp += strlen(bp); + + if (xstart != (time_t) 0) + { + snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s", + pintvl(curtime() - xstart, TRUE)); + bp += strlen(bp); + } + + /* mailer: assume about 19 bytes (max 10 byte mailer name) */ + if (m != NULL) + { + snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name); + bp += strlen(bp); + } + + /* relay: max 66 bytes for IPv4 addresses */ + if (mci != NULL && mci->mci_host != NULL) + { +# if DAEMON + extern SOCKADDR CurHostAddr; +# endif + + snprintf(bp, SPACELEFT(buf, bp), ", relay=%s", + shortenstring(mci->mci_host, 40)); + bp += strlen(bp); + +# if DAEMON + if (CurHostAddr.sa.sa_family != 0) + { + snprintf(bp, SPACELEFT(buf, bp), " [%s]", + anynet_ntoa(&CurHostAddr)); + } +# endif + } + else if (strcmp(stat, "queued") != 0) + { + p = macvalue('h', e); + if (p != NULL && p[0] != '\0') + { + snprintf(bp, SPACELEFT(buf, bp), ", relay=%s", + shortenstring(p, 40)); + } + } + bp += strlen(bp); + +#define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) +#if (STATLEN) < 63 +# undef STATLEN +# define STATLEN 63 +#endif +#if (STATLEN) > 203 +# undef STATLEN +# define STATLEN 203 +#endif + + /* stat: max 210 bytes */ + if ((bp - buf) > (sizeof buf - ((STATLEN) + 20))) + { + /* desperation move -- truncate data */ + bp = buf + sizeof buf - ((STATLEN) + 17); + strcpy(bp, "..."); + bp += 3; + } + + (void) strcpy(bp, ", stat="); + bp += strlen(bp); + + (void) strcpy(bp, shortenstring(stat, (STATLEN))); + + /* id, to: max 13 + TOBUFSIZE bytes */ + l = SYSLOG_BUFSIZE - 100 - strlen(buf); + p = e->e_to; + while (strlen(p) >= (SIZE_T) l) + { + register char *q = strchr(p + l, ','); + + if (q == NULL) + break; + sm_syslog(LOG_INFO, e->e_id, + "to=%.*s [more]%s", + ++q - p, p, buf); + p = q; + } + sm_syslog(LOG_INFO, e->e_id, "to=%s%s", p, buf); + +# else /* we have a very short log buffer size */ + + l = SYSLOG_BUFSIZE - 85; + p = e->e_to; + while (strlen(p) >= (SIZE_T) l) + { + register char *q = strchr(p + l, ','); + + if (q == NULL) + break; + sm_syslog(LOG_INFO, e->e_id, + "to=%.*s [more]", + ++q - p, p); + p = q; + } + sm_syslog(LOG_INFO, e->e_id, "to=%s", p); + + if (ctladdr != NULL) + { + bp = buf; + snprintf(bp, SPACELEFT(buf, bp), "ctladdr=%s", + shortenstring(ctladdr->q_paddr, 83)); + bp += strlen(bp); + if (bitset(QGOODUID, ctladdr->q_flags)) + { + (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", + ctladdr->q_uid, ctladdr->q_gid); + bp += strlen(bp); + } + sm_syslog(LOG_INFO, e->e_id, "%s", buf); + } + bp = buf; + snprintf(bp, SPACELEFT(buf, bp), "delay=%s", + pintvl(curtime() - e->e_ctime, TRUE)); + bp += strlen(bp); + if (xstart != (time_t) 0) + { + snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s", + pintvl(curtime() - xstart, TRUE)); + bp += strlen(bp); + } + + if (m != NULL) + { + snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", 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) + { +# if DAEMON + extern SOCKADDR CurHostAddr; +# endif + + snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", mci->mci_host); + bp += strlen(bp); + +# if DAEMON + if (CurHostAddr.sa.sa_family != 0) + snprintf(bp, SPACELEFT(buf, bp), " [%.100s]", + anynet_ntoa(&CurHostAddr)); +# endif + } + else if (strcmp(stat, "queued") != 0) + { + p = macvalue('h', e); + if (p != NULL && p[0] != '\0') + 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(stat, 63)); +# endif /* short log buffer */ +} + /* +** 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: +** none +** +** Side Effects: +** outputs some text to fp. +*/ + +void +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; + + 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) snprintf(xbuf, sizeof xbuf, + "From %.800s \201d remote from %.100s\n", + buf, at); + } + else + { + *bang++ = '\0'; + (void) snprintf(xbuf, sizeof xbuf, + "From %.800s \201d remote from %.100s\n", + bang, buf); + template = xbuf; + } + } + expand(template, buf, sizeof buf, e); + 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: +** none. +** +** Side Effects: +** The message is written onto fp. +*/ + +/* values for output state variable */ +#define OS_HEAD 0 /* at beginning of line */ +#define OS_CR 1 /* read a carriage return */ +#define OS_INLINE 2 /* putting rest of line */ + +void +putbody(mci, e, separator) + register MCI *mci; + register ENVELOPE *e; + char *separator; +{ + char buf[MAXLINE]; + + /* + ** Output the body of the message + */ + + if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) + { + char *df = queuename(e, 'd'); + + e->e_dfp = fopen(df, "r"); + if (e->e_dfp == NULL) + syserr("putbody: Cannot open %s for %s from %s", + df, e->e_to, e->e_from.q_paddr); + } + if (e->e_dfp == NULL) + { + if (bitset(MCIF_INHEADER, mci->mci_flags)) + { + putline("", mci); + mci->mci_flags &= ~MCIF_INHEADER; + } + putline("<<< No Message Collected >>>", mci); + goto endofmessage; + } + if (e->e_dfino == (ino_t) 0) + { + struct stat stbuf; + + if (fstat(fileno(e->e_dfp), &stbuf) < 0) + e->e_dfino = -1; + else + { + e->e_dfdev = stbuf.st_dev; + e->e_dfino = stbuf.st_ino; + } + } + rewind(e->e_dfp); + +#if MIME8TO7 + if (bitset(MCIF_CVT8TO7, mci->mci_flags)) + { + char *boundaries[MAXMIMENESTING + 1]; + + /* + ** 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); + + if (hvalue("Content-Type", e->e_header) == NULL) + { + snprintf(buf, sizeof buf, + "Content-Type: text/plain; charset=%s", + defcharset(e)); + putline(buf, mci); + } + + /* now do the hard work */ + boundaries[0] = NULL; + mci->mci_flags |= MCIF_INHEADER; + mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER); + } +# if MIME7TO8 + else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) + { + mime7to8(mci, e->e_header, e); + } +# endif + else +#endif + { + int ostate; + register char *bp; + register char *pbp; + register int c; + register char *xp; + int padc; + char *buflim; + int pos = 0; + size_t eol_len; + char peekbuf[10]; + + /* we can pass it through unmodified */ + if (bitset(MCIF_INHEADER, mci->mci_flags)) + { + putline("", mci); + 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]; + eol_len = strlen(mci->mci_mailer->m_eol); + + /* copy temp file to output with mapping */ + ostate = OS_HEAD; + bp = buf; + pbp = peekbuf; + while (!ferror(mci->mci_out)) + { + if (pbp > peekbuf) + c = *--pbp; + else if ((c = getc(e->e_dfp)) == EOF) + break; + if (bitset(MCIF_7BIT, mci->mci_flags)) + c &= 0x7f; + switch (ostate) + { + case OS_HEAD: +#if _FFR_NONULLS + if (c == '\0' && + bitnset(M_NONULLS, mci->mci_mailer->m_flags)) + break; +#endif + if (c != '\r' && c != '\n' && bp < buflim) + { + *bp++ = c; + break; + } + + /* check beginning of line for special cases */ + *bp = '\0'; + pos = 0; + padc = 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) + { + fprintf(TrafficLogFile, "%05d >>> ", + (int) getpid()); + if (padc != EOF) + putc(padc, TrafficLogFile); + for (xp = buf; xp < bp; xp++) + putc(*xp, TrafficLogFile); + if (c == '\n') + fputs(mci->mci_mailer->m_eol, + TrafficLogFile); + } + if (padc != EOF) + { + putc(padc, mci->mci_out); + mci->mci_contentlen++; + pos++; + } + for (xp = buf; xp < bp; xp++) + { + putc(*xp, mci->mci_out); + mci->mci_contentlen++; + } + if (c == '\n') + { + fputs(mci->mci_mailer->m_eol, + mci->mci_out); + mci->mci_contentlen += eol_len; + pos = 0; + } + else + { + pos += bp - buf; + if (c != '\r') + *pbp++ = c; + } + bp = buf; + + /* determine next state */ + if (c == '\n') + ostate = OS_HEAD; + else if (c == '\r') + ostate = OS_CR; + else + ostate = OS_INLINE; + continue; + + case OS_CR: + if (c == '\n') + { + /* got CRLF */ + fputs(mci->mci_mailer->m_eol, mci->mci_out); + mci->mci_contentlen += eol_len; + if (TrafficLogFile != NULL) + { + fputs(mci->mci_mailer->m_eol, + TrafficLogFile); + } + ostate = OS_HEAD; + continue; + } + + /* had a naked carriage return */ + *pbp++ = c; + c = '\r'; + ostate = OS_INLINE; + goto putch; + + case OS_INLINE: + if (c == '\r') + { + ostate = OS_CR; + continue; + } +#if _FFR_NONULLS + if (c == '\0' && + bitnset(M_NONULLS, mci->mci_mailer->m_flags)) + break; +#endif +putch: + if (mci->mci_mailer->m_linelimit > 0 && + pos > mci->mci_mailer->m_linelimit && + c != '\n') + { + putc('!', mci->mci_out); + mci->mci_contentlen++; + fputs(mci->mci_mailer->m_eol, mci->mci_out); + mci->mci_contentlen += eol_len; + if (TrafficLogFile != NULL) + { + fprintf(TrafficLogFile, "!%s", + mci->mci_mailer->m_eol); + } + ostate = OS_HEAD; + *pbp++ = c; + continue; + } + if (c == '\n') + { + if (TrafficLogFile != NULL) + fputs(mci->mci_mailer->m_eol, + TrafficLogFile); + fputs(mci->mci_mailer->m_eol, mci->mci_out); + mci->mci_contentlen += eol_len; + pos = 0; + ostate = OS_HEAD; + } + else + { + if (TrafficLogFile != NULL) + putc(c, TrafficLogFile); + putc(c, mci->mci_out); + mci->mci_contentlen++; + pos++; + ostate = OS_INLINE; + } + break; + } + } + + /* make sure we are at the beginning of a line */ + if (bp > buf) + { + if (TrafficLogFile != NULL) + { + for (xp = buf; xp < bp; xp++) + putc(*xp, TrafficLogFile); + } + for (xp = buf; xp < bp; xp++) + { + putc(*xp, mci->mci_out); + mci->mci_contentlen++; + } + pos += bp - buf; + } + if (pos > 0) + { + if (TrafficLogFile != NULL) + fputs(mci->mci_mailer->m_eol, TrafficLogFile); + fputs(mci->mci_mailer->m_eol, mci->mci_out); + mci->mci_contentlen += eol_len; + } + } + + if (ferror(e->e_dfp)) + { + syserr("putbody: df%s: read error", e->e_id); + ExitStat = EX_IOERR; + } + +endofmessage: + /* some mailers want extra blank line at end of message */ + if (bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && + buf[0] != '\0' && buf[0] != '\n') + putline("", mci); + + (void) fflush(mci->mci_out); + if (ferror(mci->mci_out) && errno != EPIPE) + { + syserr("putbody: write error"); + ExitStat = EX_IOERR; + } + errno = 0; +} + /* +** MAILFILE -- Send a message to a file. +** +** If the file has the setuid/setgid bits set, but NO execute +** bits, sendmail will try to become the owner of that file +** 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. +*/ + +static jmp_buf CtxMailfileTimeout; +static void mailfiletimeout __P((void)); + +int +mailfile(filename, mailer, ctladdr, sfflags, e) + char *volatile filename; + MAILER *volatile mailer; + ADDRESS *ctladdr; + volatile int sfflags; + register ENVELOPE *e; +{ + register FILE *f; + register pid_t pid = -1; + volatile int mode = ST_MODE_NOFILE; + bool suidwarn = geteuid() == 0; + char *p; + EVENT *ev; + + if (tTd(11, 1)) + { + printf("mailfile %s\n ctladdr=", filename); + printaddr(ctladdr, FALSE); + } + + if (mailer == NULL) + mailer = FileMailer; + + if (e->e_xfp != NULL) + fflush(e->e_xfp); + + /* + ** Special case /dev/null. This allows us to restrict file + ** delivery to regular files only. + */ + + if (strcmp(filename, "/dev/null") == 0) + 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))))) + { + usrerr("554 Cannot send 8-bit data to 7-bit destination"); + e->e_status = "5.6.3"; + return(EX_DATAERR); + } + + /* + ** Fork so we can change permissions here. + ** Note that we MUST use fork, not vfork, because of + ** the complications of calling subroutines, etc. + */ + + DOFORK(fork); + + if (pid < 0) + return (EX_OSERR); + else if (pid == 0) + { + /* child -- actually write to file */ + struct stat stb; + MCI mcibuf; + volatile int oflags = O_WRONLY|O_APPEND; + + if (e->e_lockfp != NULL) + (void) close(fileno(e->e_lockfp)); + + (void) setsignal(SIGINT, SIG_DFL); + (void) setsignal(SIGHUP, SIG_DFL); + (void) setsignal(SIGTERM, SIG_DFL); + (void) umask(OldUmask); + e->e_to = filename; + ExitStat = EX_OK; + + if (setjmp(CtxMailfileTimeout) != 0) + { + exit(EX_TEMPFAIL); + } + + if (TimeOuts.to_fileopen > 0) + ev = setevent(TimeOuts.to_fileopen, mailfiletimeout, 0); + else + ev = NULL; + +#ifdef HASLSTAT + if (lstat(filename, &stb) < 0) +#else + if (stat(filename, &stb) < 0) +#endif + { + stb.st_mode = ST_MODE_NOFILE; + mode = FileMode; + oflags |= O_CREAT|O_EXCL; + } + else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, stb.st_mode) || + (!bitset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail) && + stb.st_nlink != 1) || + (SafeFileEnv != NULL && !S_ISREG(stb.st_mode))) + exit(EX_CANTCREAT); + if (mode == ST_MODE_NOFILE) + mode = stb.st_mode; + + /* limit the errors to those actually caused in the child */ + errno = 0; + ExitStat = EX_OK; + + if (ctladdr != NULL || bitset(SFF_RUNASREALUID, sfflags)) + { + /* ignore setuid and setgid bits */ + mode &= ~(S_ISGID|S_ISUID); + } + + /* we have to open the dfile BEFORE setuid */ + if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) + { + char *df = queuename(e, 'd'); + + e->e_dfp = fopen(df, "r"); + 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; + RealUid = mailer->m_uid; + } + 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 != 0) + { + 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)) + RealGid = mailer->m_gid; + else if (bitset(S_ISGID, mode)) + RealGid = stb.st_gid; + else if (ctladdr != NULL && ctladdr->q_uid != 0) + RealGid = ctladdr->q_gid; + else if (mailer != NULL && mailer->m_gid != 0) + RealGid = mailer->m_gid; + else + 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); + } + else + { + GIDSET_T gidset[1]; + + gidset[0] = RealGid; + if (setgroups(1, gidset) == -1 && suidwarn) + syserr("mailfile: setgroups() failed"); + } + + /* if you have a safe environment, go into it */ + if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') + { + int i; + + if (chroot(SafeFileEnv) < 0) + { + syserr("mailfile: Cannot chroot(%s)", + SafeFileEnv); + exit(EX_CANTCREAT); + } + i = strlen(SafeFileEnv); + if (strncmp(SafeFileEnv, filename, i) == 0) + filename += i; + } + if (chdir("/") < 0) + syserr("mailfile: cannot chdir(/)"); + + /* now reset the group and user ids */ + endpwent(); + if (setgid(RealGid) < 0 && suidwarn) + syserr("mailfile: setgid(%ld) failed", (long) RealGid); + vendor_set_uid(RealUid); + if (setuid(RealUid) < 0 && suidwarn) + syserr("mailfile: setuid(%ld) failed", (long) RealUid); + + /* move into some "safe" directory */ + if (mailer->m_execdir != NULL) + { + char *q; + char buf[MAXLINE + 1]; + + for (p = mailer->m_execdir; p != NULL; p = q) + { + q = strchr(p, ':'); + if (q != NULL) + *q = '\0'; + expand(p, buf, sizeof buf, e); + if (q != NULL) + *q++ = ':'; + if (tTd(11, 20)) + printf("mailfile: trydir %s\n", + buf); + if (buf[0] != '\0' && chdir(buf) >= 0) + break; + } + } + + sfflags |= SFF_NOPATHCHECK; + if (!bitset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) + sfflags |= SFF_NOSLINK; + if (!bitset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) + sfflags |= SFF_NOHLINK; + sfflags &= ~SFF_OPENASROOT; + f = safefopen(filename, oflags, FileMode, sfflags); + if (f == NULL) + { + message("554 cannot open %s: %s", + shortenstring(filename, MAXSHORTSTR), + errstring(errno)); + exit(EX_CANTCREAT); + } + if (filechanged(filename, fileno(f), &stb)) + { + message("554 file changed after open"); + exit(EX_CANTCREAT); + } + if (fstat(fileno(f), &stb) < 0) + { + message("554 cannot fstat %s", errstring(errno)); + exit(EX_CANTCREAT); + } + + if (ev != NULL) + clrevent(ev); + + bzero(&mcibuf, sizeof mcibuf); + mcibuf.mci_mailer = mailer; + mcibuf.mci_out = f; + mcibuf.mci_contentlen = 0; + if (bitnset(M_7BITS, mailer->m_flags)) + mcibuf.mci_flags |= MCIF_7BIT; + + /* 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 && + (strcasecmp(p, "quoted-printable") == 0 || + strcasecmp(p, "base64") == 0) && + (p = hvalue("Content-Type", e->e_header)) != NULL) + { + /* may want to convert 7 -> 8 */ + /* XXX should really parse it here -- and use a class XXX */ + if (strncasecmp(p, "text/plain", 10) == 0 && + (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) + mcibuf.mci_flags |= MCIF_CVT7TO8; + } +#endif + + putfromline(&mcibuf, e); + (*e->e_puthdr)(&mcibuf, e->e_header, e); + (*e->e_putbody)(&mcibuf, e, NULL); + putline("\n", &mcibuf); + if (fflush(f) < 0 || ferror(f)) + { + message("451 I/O error: %s", errstring(errno)); + setstat(EX_IOERR); + } + + /* reset ISUID & ISGID bits for paranoid systems */ +#if HASFCHMOD + (void) fchmod(fileno(f), (MODE_T) stb.st_mode); +#else + (void) chmod(filename, (MODE_T) stb.st_mode); +#endif + (void) xfclose(f, "mailfile", filename); + (void) fflush(stdout); + 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)) + 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() +{ + 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. +** e -- the current envelope. +** +** Returns: +** The signature for this host. +** +** Side Effects: +** Can tweak the symbol table. +*/ + +char * +hostsignature(m, host, e) + register MAILER *m; + char *host; + ENVELOPE *e; +{ + register char *p; + register STAB *s; + int i; + int len; +#if NAMED_BIND + int nmx; + char *hp; + char *endp; + int oldoptions = _res.options; + char *mxhosts[MAXMXHOSTS + 1]; +#endif + + /* + ** Check to see if this uses IPC -- if not, it can't have MX records. + */ + + p = m->m_mailer; + if (strcmp(p, "[IPC]") != 0 && strcmp(p, "[TCP]") != 0) + { + /* just an ordinary mailer */ + return host; + } + + /* + ** Look it up in the symbol table. + */ + + s = stab(host, ST_HOSTSIG, ST_ENTER); + if (s->s_hostsig != NULL) + return s->s_hostsig; + + /* + ** Not already there -- create a signature. + */ + +#if NAMED_BIND + if (ConfigLevel < 2) + _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ + + for (hp = host; hp != NULL; hp = endp) + { + endp = strchr(hp, ':'); + if (endp != NULL) + *endp = '\0'; + + if (bitnset(M_NOMX, m->m_flags)) + { + /* skip MX lookups */ + nmx = 1; + mxhosts[0] = hp; + } + else + { + auto int rcode; + + nmx = getmxrr(hp, mxhosts, TRUE, &rcode); + if (nmx <= 0) + { + register MCI *mci; + + /* update the connection info for this host */ + mci = mci_get(hp, m); + mci->mci_errno = errno; + mci->mci_herrno = h_errno; + mci->mci_lastuse = curtime(); + mci_setstat(mci, rcode, NULL, NULL); + + /* use the original host name as signature */ + nmx = 1; + mxhosts[0] = hp; + } + } + + len = 0; + for (i = 0; i < nmx; i++) + { + len += strlen(mxhosts[i]) + 1; + } + if (s->s_hostsig != NULL) + len += strlen(s->s_hostsig) + 1; + p = xalloc(len); + if (s->s_hostsig != NULL) + { + (void) strcpy(p, s->s_hostsig); + free(s->s_hostsig); + s->s_hostsig = p; + p += strlen(p); + *p++ = ':'; + } + else + s->s_hostsig = p; + for (i = 0; i < nmx; i++) + { + if (i != 0) + *p++ = ':'; + strcpy(p, mxhosts[i]); + p += strlen(p); + } + if (endp != NULL) + *endp++ = ':'; + } + makelower(s->s_hostsig); + if (ConfigLevel < 2) + _res.options = oldoptions; +#else + /* not using BIND -- the signature is just the host name */ + s->s_hostsig = host; +#endif + if (tTd(17, 1)) + printf("hostsignature(%s) = %s\n", host, s->s_hostsig); + return s->s_hostsig; +} diff --git a/contrib/sendmail/src/domain.c b/contrib/sendmail/src/domain.c new file mode 100644 index 0000000..e3a5500 --- /dev/null +++ b/contrib/sendmail/src/domain.c @@ -0,0 +1,913 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#include "sendmail.h" + +#ifndef lint +#if NAMED_BIND +static char sccsid[] = "@(#)domain.c 8.77 (Berkeley) 6/4/98 (with name server)"; +#else +static char sccsid[] = "@(#)domain.c 8.77 (Berkeley) 6/4/98 (without name server)"; +#endif +#endif /* not lint */ + +#if NAMED_BIND + +#include +#include +#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 + +typedef union +{ + HEADER qb1; + u_char qb2[MAXPACKET]; +} querybuf; + +#ifndef MXHOSTBUFSIZE +# define MXHOSTBUFSIZE (128 * MAXMXHOSTS) +#endif + +static char MXHostBuf[MXHOSTBUFSIZE]; + +#ifndef MAXDNSRCH +# define MAXDNSRCH 6 /* number of possible domains to search */ +#endif + +#ifndef MAX +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef NO_DATA +# define NO_DATA NO_ADDRESS +#endif + +#ifndef HFIXEDSZ +# define HFIXEDSZ 12 /* sizeof(HEADER) */ +#endif + +#define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ + +#if defined(__RES) && (__RES >= 19940415) +# define RES_UNC_T char * +#else +# define RES_UNC_T u_char * +#endif + /* +** GETMXRR -- get MX resource records for a domain +** +** Parameters: +** host -- the name of the host to MX. +** mxhosts -- a pointer to a return buffer of MX records. +** 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. +** +** 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. +*/ + +int +getmxrr(host, mxhosts, droplocalhost, rcode) + char *host; + char **mxhosts; + bool droplocalhost; + int *rcode; +{ + register u_char *eom, *cp; + register int i, j, n; + int nmx = 0; + register char *bp; + HEADER *hp; + querybuf answer; + int ancount, qdcount, buflen; + bool seenlocal = FALSE; + u_short pref, type; + u_short localpref = 256; + char *fallbackMX = FallBackMX; + bool trycanon = FALSE; + int (*resfunc)(); + extern int res_query(), res_search(); + u_short prefer[MAXMXHOSTS]; + int weight[MAXMXHOSTS]; + extern int mxrand __P((char *)); + + if (tTd(8, 2)) + printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost); + + if (fallbackMX != NULL && droplocalhost && + wordinclass(fallbackMX, 'w')) + { + /* don't use fallback for this pass */ + fallbackMX = NULL; + } + + *rcode = EX_OK; + + /* 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, (u_char *) &answer, sizeof(answer)); + if (n < 0) + { + if (tTd(8, 1)) + printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", + (host == NULL) ? "" : host, errno, h_errno); + switch (h_errno) + { + case NO_DATA: + trycanon = TRUE; + /* fall through */ + + 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 + /* 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 */ + mxhosts[nmx++] = fallbackMX; + return nmx; + } + /* it might come up later; better queue it up */ + *rcode = EX_TEMPFAIL; + break; + + default: + syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n", + 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 = (u_char *)&answer + HFIXEDSZ; + eom = (u_char *)&answer + n; + for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) + if ((n = dn_skipname(cp, eom)) < 0) + goto punt; + buflen = sizeof(MXHostBuf) - 1; + bp = MXHostBuf; + ancount = ntohs(hp->ancount); + while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) + { + if ((n = dn_expand((u_char *)&answer, + eom, cp, (RES_UNC_T) bp, buflen)) < 0) + break; + cp += n; + GETSHORT(type, cp); + cp += INT16SZ + INT32SZ; + GETSHORT(n, cp); + if (type != T_MX) + { + if (tTd(8, 8) || _res.options & RES_DEBUG) + printf("unexpected answer type %d, size %d\n", + type, n); + cp += n; + continue; + } + GETSHORT(pref, cp); + if ((n = dn_expand((u_char *)&answer, eom, cp, + (RES_UNC_T) bp, buflen)) < 0) + break; + cp += n; + if (wordinclass(bp, 'w')) + { + if (tTd(8, 3)) + printf("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); + prefer[nmx] = pref; + mxhosts[nmx++] = bp; + n = strlen(bp); + bp += n; + if (bp[-1] != '.') + { + *bp++ = '.'; + n++; + } + *bp++ = '\0'; + buflen -= n + 1; + } + + /* sort the records */ + for (i = 0; i < nmx; i++) + { + for (j = i + 1; j < nmx; j++) + { + if (prefer[i] > prefer[j] || + (prefer[i] == prefer[j] && weight[i] > weight[j])) + { + register int temp; + register char *temp1; + + temp = prefer[i]; + prefer[i] = prefer[j]; + prefer[j] = temp; + temp1 = mxhosts[i]; + mxhosts[i] = mxhosts[j]; + mxhosts[j] = temp1; + temp = weight[i]; + weight[i] = weight[j]; + weight[j] = temp; + } + } + if (seenlocal && prefer[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 (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) + i++; + else + { + /* compress out duplicate */ + for (j = i + 1; j < nmx; j++) + mxhosts[j] = mxhosts[j + 1]; + nmx--; + } + } + + if (nmx == 0) + { +punt: + if (seenlocal && + (!TryNullMXList || sm_gethostbyname(host) == 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.... + */ + + *rcode = EX_CONFIG; + syserr("MX list for %s points back to %s", + host, MyHostName); + return -1; + } + if (strlen(host) >= (SIZE_T) sizeof MXHostBuf) + { + *rcode = EX_CONFIG; + syserr("Host name %s too long", + shortenstring(host, MAXSHORTSTR)); + return -1; + } + snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host); + mxhosts[0] = MXHostBuf; + if (host[0] == '[') + { + register char *p; + + /* 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 = ']'; + } + else + { + trycanon = TRUE; + mxhosts[0]++; + } + } + } + if (trycanon && + getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE)) + { + 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) + mxhosts[nmx++] = fallbackMX; + + 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. +** +** Side Effects: +** none. +*/ + +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)) + printf("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)) + printf(" = %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; + auto int rcode; + int saveopts = _res.options; + int i, len = 0; + char *p; + char *mxhosts[MAXMXHOSTS + 1]; + char buf[MXHOSTBUFSIZE + 1]; + + _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); + nmx = getmxrr(name, mxhosts, FALSE, &rcode); + _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. + */ + p = buf; + for (i = 0; i < nmx; i++) + { + int slen; + + if (strchr(mxhosts[i], map->map_coldelim) != NULL) + { + syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", + 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++; + } + strcpy(p, mxhosts[i]); + p += slen; + len += slen; + } + return map_rewrite(map, buf, len, av); +} + /* +** 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. +** +** Returns: +** TRUE -- if the host matched. +** FALSE -- otherwise. +*/ + +bool +dns_getcanonname(host, hbsize, trymx, statp) + char *host; + int hbsize; + bool trymx; + int *statp; +{ + register u_char *eom, *ap; + register char *cp; + register int n; + HEADER *hp; + querybuf answer; + int ancount, qdcount; + int ret; + char **domain; + int type; + char **dp; + char *mxmatch; + bool amatch; + bool gotmx = FALSE; + int qtype; + int loopcnt; + char *xp; + char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)]; + char *searchlist[MAXDNSRCH+2]; + extern char *gethostalias __P((char *)); + + if (tTd(8, 2)) + printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) + { + *statp = EX_UNAVAILABLE; + return FALSE; + } + + /* + ** 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). + ** + ** 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++; + + /* + ** If this is a simple name, determine whether it matches an + ** alias in the file defined by the environment variable HOSTALIASES. + */ + if (n == 0 && (xp = gethostalias(host)) != NULL) + { + if (loopcnt++ > MAXCNAMEDEPTH) + { + syserr("loop in ${HOSTALIASES} file"); + } + else + { + strncpy(host, xp, hbsize); + host[hbsize - 1] = '\0'; + goto cnameloop; + } + } + + /* + ** 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)) + { + for (domain = _res.dnsrch; *domain != NULL; ) + *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; + qtype = T_ANY; + + for (dp = searchlist; *dp != NULL; ) + { + if (qtype == T_ANY) + gotmx = FALSE; + if (tTd(8, 5)) + printf("dns_getcanonname: trying %s.%s (%s)\n", + host, *dp, + qtype == T_ANY ? "ANY" : qtype == T_A ? "A" : + qtype == T_MX ? "MX" : "???"); + ret = res_querydomain(host, *dp, C_IN, qtype, + answer.qb2, sizeof(answer.qb2)); + if (ret <= 0) + { + if (tTd(8, 7)) + printf("\tNO: errno=%d, h_errno=%d\n", + errno, h_errno); + + if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) + { + /* the name server seems to be down */ + h_errno = TRY_AGAIN; + *statp = EX_TEMPFAIL; + return FALSE; + } + + if (h_errno != HOST_NOT_FOUND) + { + /* might have another type of interest */ + if (qtype == T_ANY) + { + qtype = T_A; + continue; + } + else if (qtype == T_A && !gotmx && trymx) + { + qtype = T_MX; + continue; + } + } + + /* definite no -- try the next domain */ + dp++; + qtype = T_ANY; + continue; + } + else if (tTd(8, 7)) + printf("\tYES\n"); + + /* avoid problems after truncation in tcp packets */ + if (ret > sizeof(answer)) + ret = sizeof(answer); + + /* + ** 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 = (u_char *) &answer + HFIXEDSZ; + eom = (u_char *) &answer + ret; + + /* skip question part of response -- we know what we asked */ + for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) + { + if ((ret = dn_skipname(ap, eom)) < 0) + { + if (tTd(8, 20)) + printf("qdcount failure (%d)\n", + ntohs(hp->qdcount)); + *statp = EX_SOFTWARE; + return FALSE; /* ???XXX??? */ + } + } + + amatch = FALSE; + for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; + ap += n) + { + n = dn_expand((u_char *) &answer, eom, ap, + (RES_UNC_T) nbuf, sizeof nbuf); + if (n < 0) + break; + ap += n; + GETSHORT(type, ap); + ap += INT16SZ + INT32SZ; + GETSHORT(n, ap); + 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. + */ + + 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]; + + snprintf(ebuf, sizeof ebuf, + "Deferred: DNS failure: CNAME loop for %.100s", + host); + CurEnv->e_message = newstr(ebuf); + } + h_errno = NO_RECOVERY; + *statp = EX_CONFIG; + return FALSE; + } + + /* value points at name */ + if ((ret = dn_expand((u_char *)&answer, + eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0) + break; + (void)strncpy(host, nbuf, hbsize); /* XXX */ + host[hbsize - 1] = '\0'; + + /* + ** 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_ANY query, we don't really know what + ** was returned -- it might have been a T_NS, + ** for example. Try T_A to be more specific + ** during the next pass. + ** If this was a T_A query and we haven't yet found a MX + ** match, try T_MX if allowed to do so. + ** Otherwise, try the next domain. + */ + + if (qtype == T_ANY) + qtype = T_A; + else if (qtype == T_A && !gotmx && trymx) + qtype = T_MX; + else + { + qtype = T_ANY; + dp++; + } + } + + /* if nothing was found, we are done */ + if (mxmatch == NULL) + { + *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) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, + *mxmatch == '\0' ? "" : ".", + MAXDNAME, mxmatch); + strncpy(host, nbuf, hbsize); + host[hbsize - 1] = '\0'; + if (tTd(8, 5)) + printf("dns_getcanonname: %s\n", host); + *statp = EX_OK; + return TRUE; +} + + + +char * +gethostalias(host) + char *host; +{ + char *fname; + FILE *fp; + register char *p = NULL; + int sff = SFF_REGONLY; + char buf[MAXLINE]; + static char hbuf[MAXDNAME]; + + if (DontLockReadFiles) + sff |= SFF_NOLOCK; + fname = getenv("HOSTALIASES"); + if (fname == NULL || + (fp = safefopen(fname, O_RDONLY, 0, sff)) == NULL) + return NULL; + while (fgets(buf, sizeof buf, fp) != NULL) + { + for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) + continue; + if (*p == 0) + { + /* syntax error */ + continue; + } + *p++ = '\0'; + if (strcasecmp(buf, host) == 0) + break; + } + + if (feof(fp)) + { + /* no match */ + fclose(fp); + return NULL; + } + fclose(fp); + + /* got a match; extract the equivalent name */ + while (*p != '\0' && isascii(*p) && isspace(*p)) + p++; + host = p; + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + *p = '\0'; + strncpy(hbuf, host, sizeof hbuf - 1); + hbuf[sizeof hbuf - 1] = '\0'; + return hbuf; +} + +#endif /* NAMED_BIND */ diff --git a/contrib/sendmail/src/envelope.c b/contrib/sendmail/src/envelope.c new file mode 100644 index 0000000..092148a --- /dev/null +++ b/contrib/sendmail/src/envelope.c @@ -0,0 +1,938 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)envelope.c 8.117 (Berkeley) 6/4/98"; +#endif /* not lint */ + +#include "sendmail.h" + +/* +** NEWENVELOPE -- allocate a new envelope +** +** Supports inheritance. +** +** Parameters: +** e -- the new envelope to fill in. +** parent -- the envelope to be the parent of e. +** +** Returns: +** e. +** +** Side Effects: +** none. +*/ + +ENVELOPE * +newenvelope(e, parent) + register ENVELOPE *e; + register ENVELOPE *parent; +{ + if (e == parent && e->e_parent != NULL) + parent = e->e_parent; + clearenvelope(e, TRUE); + if (e == CurEnv) + bcopy((char *) &NullAddress, (char *) &e->e_from, sizeof e->e_from); + else + bcopy((char *) &CurEnv->e_from, (char *) &e->e_from, sizeof e->e_from); + e->e_parent = parent; + e->e_ctime = curtime(); + if (parent != NULL) + e->e_msgpriority = parent->e_msgsize; + e->e_puthdr = putheader; + e->e_putbody = putbody; + if (CurEnv->e_xfp != NULL) + (void) fflush(CurEnv->e_xfp); + + return (e); +} + /* +** DROPENVELOPE -- deallocate an envelope. +** +** Parameters: +** e -- the envelope to deallocate. +** fulldrop -- if set, do return receipts. +** +** Returns: +** none. +** +** Side Effects: +** housekeeping necessary to dispose of an envelope. +** Unlocks this queue file. +*/ + +void +dropenvelope(e, fulldrop) + register ENVELOPE *e; + bool fulldrop; +{ + bool queueit = FALSE; + bool message_timeout = FALSE; + bool failure_return = FALSE; + bool delay_return = FALSE; + bool success_return = FALSE; + register ADDRESS *q; + char *id = e->e_id; + char buf[MAXLINE]; + + if (tTd(50, 1)) + { + extern void printenvflags __P((ENVELOPE *)); + + printf("dropenvelope %lx: id=", (u_long) e); + xputs(e->e_id); + printf(", flags="); + printenvflags(e); + if (tTd(50, 10)) + { + printf("sendq="); + printaddr(e->e_sendqueue, TRUE); + } + } + + if (LogLevel > 84) + sm_syslog(LOG_DEBUG, id, + "dropenvelope, e_flags=0x%x, OpMode=%c, pid=%d", + e->e_flags, OpMode, getpid()); + + /* 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. + */ + + if (curtime() > e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass]) + message_timeout = TRUE; + + e->e_flags &= ~EF_QUEUERUN; + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (bitset(QQUEUEUP, q->q_flags) && + bitset(QDONTSEND, q->q_flags)) + { + /* I'm not sure how this happens..... */ + if (tTd(50, 2)) + { + printf("Bogus flags: "); + printaddr(q, FALSE); + } + q->q_flags &= ~QDONTSEND; + } + if (!bitset(QBADADDR|QDONTSEND|QSENT, q->q_flags)) + queueit = TRUE; +#if XDEBUG + else if (bitset(QQUEUEUP, q->q_flags)) + sm_syslog(LOG_DEBUG, e->e_id, + "dropenvelope: q_flags = %x, paddr = %s", + q->q_flags, q->q_paddr); +#endif + + /* see if a notification is needed */ + if (bitset(QPINGONFAILURE, q->q_flags) && + ((message_timeout && bitset(QQUEUEUP, q->q_flags)) || + bitset(QBADADDR, q->q_flags))) + { + failure_return = TRUE; + if (q->q_owner == NULL && !emptyaddr(&e->e_from)) + (void) sendtolist(e->e_from.q_paddr, NULLADDR, + &e->e_errorqueue, 0, e); + } + else if (bitset(QPINGONSUCCESS, q->q_flags) && + ((bitset(QSENT, q->q_flags) && + bitnset(M_LOCALMAILER, q->q_mailer->m_flags)) || + bitset(QRELAYED|QEXPANDED|QDELIVERED, 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) + /* nothing to do */ ; + else if (message_timeout) + { + if (failure_return) + { + (void) snprintf(buf, sizeof buf, + "Cannot send message within %s", + pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE)); + if (e->e_message != NULL) + free(e->e_message); + e->e_message = newstr(buf); + message(buf); + e->e_flags |= EF_CLRQUEUE; + } + fprintf(e->e_xfp, "Message could not be delivered for %s\n", + pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE)); + fprintf(e->e_xfp, "Message will be deleted from queue\n"); + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (!bitset(QBADADDR|QDONTSEND|QSENT, q->q_flags)) + { + q->q_flags |= QBADADDR; + q->q_status = "4.4.7"; + } + } + } + else if (TimeOuts.to_q_warning[e->e_timeoutclass] > 0 && + curtime() > e->e_ctime + TimeOuts.to_q_warning[e->e_timeoutclass]) + { + if (!bitset(EF_WARNING|EF_RESPONSE, e->e_flags) && + e->e_class >= 0 && + e->e_from.q_paddr != NULL && + strcmp(e->e_from.q_paddr, "<>") != 0 && + strncasecmp(e->e_from.q_paddr, "owner-", 6) != 0 && + (strlen(e->e_from.q_paddr) <= (SIZE_T) 8 || + strcasecmp(&e->e_from.q_paddr[strlen(e->e_from.q_paddr) - 8], "-request") != 0)) + { + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (bitset(QQUEUEUP, q->q_flags) && + bitset(QPINGONDELAY, q->q_flags)) + { + q->q_flags |= QDELAYED; + delay_return = TRUE; + } + } + } + if (delay_return) + { + (void) snprintf(buf, sizeof buf, + "Warning: could not send message for past %s", + pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], FALSE)); + if (e->e_message != NULL) + free(e->e_message); + e->e_message = newstr(buf); + message(buf); + e->e_flags |= EF_WARNING; + } + fprintf(e->e_xfp, + "Warning: message still undelivered after %s\n", + pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], FALSE)); + fprintf(e->e_xfp, "Will keep trying until message is %s old\n", + pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE)); + } + + if (tTd(50, 2)) + printf("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 (!bitset(QDONTSEND, q->q_flags) && + bitset(QPINGONFAILURE, q->q_flags)) + { + failure_return = TRUE; + q->q_flags |= QBADADDR; + } + } + } + + /* + ** 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)) + printf("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) + { + extern void savemail __P((ENVELOPE *, bool)); + + if (tTd(50, 8)) + printf("dropenvelope(%s): saving mail\n", id); + savemail(e, !bitset(EF_NO_BODY_RETN, e->e_flags)); + } + + /* + ** Arrange to send warning messages to postmaster as requested. + */ + + if ((failure_return || bitset(EF_PM_NOTIFY, e->e_flags)) && + PostMasterCopy != NULL && + !bitset(EF_RESPONSE, e->e_flags) && e->e_class >= 0) + { + auto ADDRESS *rlist = NULL; + + if (tTd(50, 8)) + printf("dropenvelope(%s): sending postmaster copy\n", id); + (void) sendtolist(PostMasterCopy, NULLADDR, &rlist, 0, e); + (void) returntosender(e->e_message, rlist, RTSF_PM_BOUNCE, e); + } + + /* + ** Instantiate or deinstantiate the queue. + */ + +simpledrop: + if (tTd(50, 8)) + printf("dropenvelope(%s): at simpledrop, queueit=%d\n", + id, queueit); + if (!queueit || bitset(EF_CLRQUEUE, e->e_flags)) + { + if (tTd(50, 1)) + { + extern void printenvflags __P((ENVELOPE *)); + + printf("\n===== Dropping [dq]f%s... queueit=%d, e_flags=", + e->e_id, queueit); + printenvflags(e); + } + xunlink(queuename(e, 'd')); + xunlink(queuename(e, 'q')); + + if (LogLevel > 10) + sm_syslog(LOG_INFO, id, "done"); + } + else if (queueit || !bitset(EF_INQUEUE, e->e_flags)) + { +#if QUEUE + queueup(e, FALSE); +#else /* QUEUE */ + syserr("554 dropenvelope: queueup"); +#endif /* QUEUE */ + } + + /* now unlock the job */ + if (tTd(50, 8)) + printf("dropenvelope(%s): unlocking job\n", id); + closexscript(e); + unlockqueue(e); + + /* make sure that this envelope is marked unused */ + if (e->e_dfp != NULL) + (void) xfclose(e->e_dfp, "dropenvelope df", e->e_id); + 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. +** +** Returns: +** none. +** +** Side Effects: +** Closes files associated with the envelope. +** Marks the envelope as unallocated. +*/ + +void +clearenvelope(e, fullclear) + register ENVELOPE *e; + bool fullclear; +{ + register HDR *bh; + register HDR **nhp; + extern ENVELOPE BlankEnvelope; + + if (!fullclear) + { + /* clear out any file information */ + if (e->e_xfp != NULL) + (void) xfclose(e->e_xfp, "clearenvelope xfp", e->e_id); + if (e->e_dfp != NULL) + (void) xfclose(e->e_dfp, "clearenvelope dfp", e->e_id); + e->e_xfp = e->e_dfp = NULL; + } + + /* now clear out the data */ + STRUCTCOPY(BlankEnvelope, *e); + e->e_message = NULL; + if (Verbose) + e->e_sendmode = SM_DELIVER; + bh = BlankEnvelope.e_header; + nhp = &e->e_header; + while (bh != NULL) + { + *nhp = (HDR *) xalloc(sizeof *bh); + bcopy((char *) bh, (char *) *nhp, 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: +** none. +** +** 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 cbuf[5]; /* holds hop count */ + char pbuf[10]; /* holds pid */ +#ifdef TTYNAME + static char ybuf[60]; /* holds tty id */ + register char *p; + extern char *ttyname(); +#endif /* TTYNAME */ + extern void settime __P((ENVELOPE *)); + + /* + ** Give this envelope a reality. + ** I.e., an id, a transcript, and a creation time. + */ + + openxscript(e); + e->e_ctime = curtime(); + + /* + ** 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) snprintf(pbuf, sizeof pbuf, "%d", getpid()); + define('p', newstr(pbuf), e); + + /* hop count */ + (void) snprintf(cbuf, sizeof cbuf, "%d", e->e_hopcount); + define('c', newstr(cbuf), e); + + /* time as integer, unix time, arpa time */ + settime(e); + +#ifdef TTYNAME + /* tty name */ + if (macvalue('y', e) == NULL) + { + p = ttyname(2); + if (p != NULL) + { + if (strrchr(p, '/') != NULL) + p = strrchr(p, '/') + 1; + snprintf(ybuf, sizeof ybuf, "%s", p); + define('y', ybuf, e); + } + } +#endif /* TTYNAME */ +} + /* +** SETTIME -- set the current time. +** +** Parameters: +** none. +** +** 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 tbuf[20]; /* holds "current" time */ + char dbuf[30]; /* holds ctime(tbuf) */ + register struct tm *tm; + extern struct tm *gmtime(); + + now = curtime(); + tm = gmtime(&now); + (void) snprintf(tbuf, sizeof tbuf, "%04d%02d%02d%02d%02d", tm->tm_year + 1900, + tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + define('t', newstr(tbuf), e); + (void) strcpy(dbuf, ctime(&now)); + p = strchr(dbuf, '\n'); + if (p != NULL) + *p = '\0'; + define('d', newstr(dbuf), e); + p = arpadate(dbuf); + p = newstr(p); + if (macvalue('a', e) == NULL) + define('a', p, e); + define('b', p, 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 + +void +openxscript(e) + register ENVELOPE *e; +{ + register char *p; + int fd; + + if (e->e_xfp != NULL) + return; + p = queuename(e, 'x'); + fd = open(p, O_WRONLY|O_CREAT|O_APPEND, FileMode); + if (fd < 0) + { + syserr("Can't create transcript file %s", p); + fd = open("/dev/null", O_WRONLY, 0644); + if (fd < 0) + syserr("!Can't open /dev/null"); + } + e->e_xfp = fdopen(fd, "a"); + if (e->e_xfp == NULL) + syserr("!Can't create transcript stream %s", p); +#ifdef HASSETVBUF + setvbuf(e->e_xfp, NULL, _IOLBF, 0); +#else + setlinebuf(e->e_xfp); +#endif + if (tTd(46, 9)) + { + printf("openxscript(%s):\n ", p); + dumpfd(fileno(e->e_xfp), 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; + (void) xfclose(e->e_xfp, "closexscript", e->e_id); + 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; + register struct passwd *pw; + char *bp; + char buf[MAXNAME + 2]; + char pvpbuf[PSBUFSIZE]; + extern char *FullName; + + if (tTd(45, 1)) + printf("setsender(%s)\n", from == NULL ? "" : from); + + /* + ** 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; + + e->e_from.q_flags = QBADADDR; + if (from == NULL || + parseaddr(from, &e->e_from, RF_COPYALL|RF_SENDERADDR, + delimchar, delimptr, e) == NULL || + bitset(QBADADDR, e->e_from.q_flags) || + 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) snprintf(ebuf, sizeof ebuf, "%.*s@%.*s", + MAXNAME, realname, + MAXNAME, host); + p = ebuf; + } + sm_syslog(LOG_NOTICE, e->e_id, + "setsender: %s: invalid or unparseable, received from %s", + shortenstring(from, 83), p); + } + if (from != NULL) + { + if (!bitset(QBADADDR, e->e_from.q_flags)) + { + /* it was a bogus mailer in the from addr */ + e->e_status = "5.1.7"; + usrerr("553 Invalid sender address"); + } + SuprErrs = TRUE; + } + if (from == realname || + parseaddr(from = newstr(realname), &e->e_from, + RF_COPYALL|RF_SENDERADDR, ' ', NULL, e) == NULL) + { + char nbuf[100]; + + SuprErrs = TRUE; + expand("\201n", nbuf, sizeof nbuf, e); + if (parseaddr(from = newstr(nbuf), &e->e_from, + RF_COPYALL, ' ', NULL, e) == NULL && + parseaddr(from = "postmaster", &e->e_from, + RF_COPYALL, ' ', NULL, e) == NULL) + syserr("553 setsender: can't even parse postmaster!"); + } + } + else + FromFlag = TRUE; + e->e_from.q_flags |= QDONTSEND; + if (tTd(45, 5)) + { + printf("setsender: QDONTSEND "); + printaddr(&e->e_from, FALSE); + } + SuprErrs = FALSE; + +# if USERDB + if (bitnset(M_CHECKUDB, e->e_from.q_mailer->m_flags)) + { + register char *p; + extern char *udbsender __P((char *)); + + p = udbsender(e->e_from.q_user); + if (p != NULL) + from = p; + } +# endif /* USERDB */ + + if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) + { + if (!internal) + { + /* if the user already given fullname don't redefine */ + if (FullName == NULL) + FullName = macvalue('x', e); + if (FullName != NULL && FullName[0] == '\0') + FullName = NULL; + } + + if (e->e_from.q_user[0] != '\0' && + (pw = sm_getpwnam(e->e_from.q_user)) != NULL) + { + /* + ** Process passwd file entry. + */ + + /* extract home directory */ + if (strcmp(pw->pw_dir, "/") == 0) + e->e_from.q_home = newstr(""); + else + e->e_from.q_home = newstr(pw->pw_dir); + define('z', e->e_from.q_home, e); + + /* extract user and group id */ + e->e_from.q_uid = pw->pw_uid; + e->e_from.q_gid = pw->pw_gid; + e->e_from.q_flags |= QGOODUID; + + /* extract full name from passwd file */ + if (FullName == NULL && pw->pw_gecos != NULL && + strcmp(pw->pw_name, e->e_from.q_user) == 0 && + !internal) + { + buildfname(pw->pw_gecos, e->e_from.q_user, buf, sizeof buf); + if (buf[0] != '\0') + FullName = newstr(buf); + } + } + else + { + e->e_from.q_home = "/no/such/directory"; + } + if (FullName != NULL && !internal) + define('x', FullName, e); + } + else if (!internal && OpMode != MD_DAEMON) + { + if (e->e_from.q_home == NULL) + { + e->e_from.q_home = getenv("HOME"); + if (e->e_from.q_home != NULL && + 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, NULL); + 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(); + } + (void) rewrite(pvp, 3, 0, e); + (void) rewrite(pvp, 1, 0, e); + (void) rewrite(pvp, 4, 0, e); + bp = buf + 1; + cataddr(pvp, NULL, bp, sizeof buf - 2, '\0'); + if (*bp == '@' && !bitnset(M_NOBRACKET, e->e_from.q_mailer->m_flags)) + { + /* heuristic: route-addr: add angle brackets */ + strcat(bp, ">"); + *--bp = '<'; + } + e->e_sender = newstr(bp); + define('f', e->e_sender, e); + + /* 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; + extern char **copyplist __P((char **, bool)); + + /* get rid of any pesky angle brackets */ + (void) rewrite(pvp, 3, 0, e); + (void) rewrite(pvp, 1, 0, e); + (void) rewrite(pvp, 4, 0, e); + + /* 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); + if (tTd(45, 3)) + { + printf("Saving from domain: "); + printav(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; + u_long ef_bit; +}; + +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 }, + { NULL } +}; + +void +printenvflags(e) + register ENVELOPE *e; +{ + register struct eflags *ef; + bool first = TRUE; + + printf("%lx", e->e_flags); + for (ef = EnvelopeFlags; ef->ef_name != NULL; ef++) + { + if (!bitset(ef->ef_bit, e->e_flags)) + continue; + if (first) + printf("<%s", ef->ef_name); + else + printf(",%s", ef->ef_name); + first = FALSE; + } + if (!first) + printf(">\n"); +} diff --git a/contrib/sendmail/src/err.c b/contrib/sendmail/src/err.c new file mode 100644 index 0000000..0661395 --- /dev/null +++ b/contrib/sendmail/src/err.c @@ -0,0 +1,767 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)err.c 8.74 (Berkeley) 6/4/98"; +#endif /* not lint */ + +# include "sendmail.h" +# include + +/* +** SYSERR -- Print error message. +** +** Prints an error message via 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. +** +** Parameters: +** fmt -- the format string. If it does not begin with +** a three-digit SMTP reply code, either 554 or +** 451 is assumed depending on whether errno +** is set. +** (others) -- parameters +** +** Returns: +** none +** Through TopFrame if QuickAbort is set. +** +** Side Effects: +** increments Errors. +** sets ExitStat. +*/ + +char MsgBuf[BUFSIZ*2]; /* text of most recent message */ +char HeldMessageBuf[sizeof MsgBuf]; /* for held messages */ + +extern void putoutmsg __P((char *, bool, bool)); +extern void puterrmsg __P((char *)); +static void fmtmsg __P((char *, const char *, const char *, int, const char *, va_list)); + +#if NAMED_BIND && !defined(NO_DATA) +# define NO_DATA NO_ADDRESS +#endif + +void +/*VARARGS1*/ +#ifdef __STDC__ +syserr(const char *fmt, ...) +#else +syserr(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + register char *p; + int olderrno = errno; + bool panic; + char *uname; + struct passwd *pw; + char ubuf[80]; + VA_LOCAL_DECL + + panic = *fmt == '!'; + if (panic) + { + fmt++; + HoldErrs = FALSE; + } + + /* format and output the error message */ + if (olderrno == 0) + p = "554"; + else + p = "451"; + VA_START(fmt); + fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap); + VA_END; + puterrmsg(MsgBuf); + + /* save this message for mailq printing */ + if (!panic && CurEnv != NULL) + { + if (CurEnv->e_message != NULL) + free(CurEnv->e_message); + CurEnv->e_message = newstr(MsgBuf + 4); + } + + /* determine exit status if not already set */ + if (ExitStat == EX_OK) + { + if (olderrno == 0) + ExitStat = EX_SOFTWARE; + else + ExitStat = EX_OSERR; + if (tTd(54, 1)) + printf("syserr: ExitStat = %d\n", ExitStat); + } + + pw = sm_getpwuid(getuid()); + if (pw != NULL) + uname = pw->pw_name; + else + { + uname = ubuf; + snprintf(ubuf, sizeof ubuf, "UID%d", getuid()); + } + + if (LogLevel > 0) + sm_syslog(panic ? LOG_ALERT : LOG_CRIT, + CurEnv == NULL ? NOQID : CurEnv->e_id, + "SYSERR(%s): %.900s", + uname, &MsgBuf[4]); + switch (olderrno) + { + case EBADF: + case ENFILE: + case EMFILE: + case ENOTTY: +#ifdef EFBIG + case EFBIG: +#endif +#ifdef ESPIPE + case ESPIPE: +#endif +#ifdef EPIPE + case EPIPE: +#endif +#ifdef ENOBUFS + case ENOBUFS: +#endif +#ifdef ESTALE + case ESTALE: +#endif + printopenfds(TRUE); + mci_dump_all(TRUE); + break; + } + if (panic) + { +#ifdef XLA + xla_all_end(); +#endif + if (tTd(0, 1)) + abort(); + exit(EX_OSERR); + } + errno = 0; + if (QuickAbort) + longjmp(TopFrame, 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, 501 is assumed. +** (others) -- printf strings +** +** Returns: +** none +** Through TopFrame if QuickAbort is set. +** +** Side Effects: +** increments Errors. +*/ + +/*VARARGS1*/ +void +#ifdef __STDC__ +usrerr(const char *fmt, ...) +#else +usrerr(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + VA_LOCAL_DECL + + if (SuprErrs) + return; + + VA_START(fmt); + fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap); + VA_END; + + /* save this message for mailq printing */ + switch (MsgBuf[0]) + { + case '4': + case '8': + if (CurEnv->e_message != NULL) + break; + + /* fall through.... */ + + case '5': + case '6': + if (CurEnv->e_message != NULL) + free(CurEnv->e_message); + if (MsgBuf[0] == '6') + { + char buf[MAXLINE]; + + snprintf(buf, sizeof buf, "Postmaster warning: %.*s", + sizeof buf - 22, MsgBuf + 4); + CurEnv->e_message = newstr(buf); + } + else + { + CurEnv->e_message = newstr(MsgBuf + 4); + } + break; + } + + puterrmsg(MsgBuf); + + if (LogLevel > 3 && LogUsrErrs) + sm_syslog(LOG_NOTICE, CurEnv->e_id, + "%.900s", + &MsgBuf[4]); + + if (QuickAbort) + longjmp(TopFrame, 1); +} + /* +** MESSAGE -- print message (not necessarily an error) +** +** Parameters: +** msg -- the message (printf fmt) -- it can begin with +** an SMTP reply code. If not, 050 is assumed. +** (others) -- printf arguments +** +** Returns: +** none +** +** Side Effects: +** none. +*/ + +/*VARARGS1*/ +void +#ifdef __STDC__ +message(const char *msg, ...) +#else +message(msg, va_alist) + const char *msg; + va_dcl +#endif +{ + VA_LOCAL_DECL + + errno = 0; + VA_START(msg); + fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap); + VA_END; + putoutmsg(MsgBuf, FALSE, FALSE); + + /* save this message for mailq printing */ + switch (MsgBuf[0]) + { + case '4': + case '8': + if (CurEnv->e_message != NULL) + break; + /* fall through.... */ + + case '5': + if (CurEnv->e_message != NULL) + free(CurEnv->e_message); + CurEnv->e_message = newstr(MsgBuf + 4); + break; + } +} + /* +** NMESSAGE -- print message (not necessarily an error) +** +** Just like "message" except it never puts the to... tag on. +** +** Parameters: +** msg -- the message (printf fmt) -- if it begins +** with a three digit SMTP reply code, that is used, +** otherwise 050 is assumed. +** (others) -- printf arguments +** +** Returns: +** none +** +** Side Effects: +** none. +*/ + +/*VARARGS1*/ +void +#ifdef __STDC__ +nmessage(const char *msg, ...) +#else +nmessage(msg, va_alist) + const char *msg; + va_dcl +#endif +{ + VA_LOCAL_DECL + + errno = 0; + VA_START(msg); + fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap); + VA_END; + putoutmsg(MsgBuf, FALSE, FALSE); + + /* save this message for mailq printing */ + switch (MsgBuf[0]) + { + case '4': + case '8': + if (CurEnv->e_message != NULL) + break; + /* fall through.... */ + + case '5': + if (CurEnv->e_message != NULL) + free(CurEnv->e_message); + CurEnv->e_message = newstr(MsgBuf + 4); + 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. +*/ + +void +putoutmsg(msg, holdmsg, heldmsg) + char *msg; + bool holdmsg; + bool heldmsg; +{ + char msgcode = msg[0]; + + /* display for debugging */ + if (tTd(54, 8)) + printf("--- %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'; + + /* output to transcript if serious */ + if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL && + strchr("45", msg[0]) != NULL) + fprintf(CurEnv->e_xfp, "%s\n", msg); + + if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) + sm_syslog(LOG_INFO, CurEnv->e_id, + "--> %s%s", + msg, holdmsg ? " (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; + snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg); + return; + } + + (void) fflush(stdout); + + if (OutChannel == NULL) + return; + + /* if DisConnected, OutChannel now points to the transcript */ + if (!DisConnected && + (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP)) + fprintf(OutChannel, "%s\r\n", msg); + else + fprintf(OutChannel, "%s\n", &msg[4]); + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(), + (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]); + if (msg[3] == ' ') + (void) fflush(OutChannel); + if (!ferror(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 || feof(InChannel) || ferror(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, CurEnv->e_id, + "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", + CurHostName == NULL ? "NO-HOST" : CurHostName, + shortenstring(msg, MAXSHORTSTR), errstring(errno)); +} + /* +** 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. +*/ + +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; + } +} + /* +** FMTMSG -- format a message into buffer. +** +** Parameters: +** eb -- error buffer to get result. +** to -- the recipient tag for this message. +** num -- arpanet error number. +** en -- the error number to display. +** fmt -- format of string. +** a, b, c, d, e -- arguments. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +static void +fmtmsg(eb, to, num, eno, fmt, ap) + register char *eb; + const char *to; + const char *num; + int eno; + const char *fmt; + va_list ap; +{ + char del; + int l; + int spaceleft = sizeof MsgBuf; + + /* output the reply code */ + if (isascii(fmt[0]) && isdigit(fmt[0]) && + isascii(fmt[1]) && isdigit(fmt[1]) && + isascii(fmt[2]) && isdigit(fmt[2])) + { + num = fmt; + fmt += 4; + } + if (num[3] == '-') + del = '-'; + else + del = ' '; + (void) snprintf(eb, spaceleft, "%3.3s%c", num, del); + eb += 4; + spaceleft -= 4; + + /* output the file name and line number */ + if (FileName != NULL) + { + (void) snprintf(eb, spaceleft, "%s: line %d: ", + shortenstring(FileName, 83), LineNumber); + eb += (l = strlen(eb)); + spaceleft -= l; + } + + /* output the "to" person */ + if (to != NULL && to[0] != '\0' && + strncmp(num, "551", 3) != 0 && + strncmp(num, "251", 3) != 0) + { + (void) snprintf(eb, spaceleft, "%s... ", + shortenstring(to, MAXSHORTSTR)); + spaceleft -= strlen(eb); + while (*eb != '\0') + *eb++ &= 0177; + } + + /* output the message */ + (void) vsnprintf(eb, spaceleft, fmt, ap); + spaceleft -= strlen(eb); + while (*eb != '\0') + *eb++ &= 0177; + + /* output the error code, if any */ + if (eno != 0) + (void) snprintf(eb, spaceleft, ": %s", errstring(eno)); +} + /* +** 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; +} + /* +** 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 * +errstring(errnum) + int errnum; +{ + char *dnsmsg; + char *bp; + static char buf[MAXLINE]; +# if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) + extern char *sys_errlist[]; + extern int sys_nerr; +# endif +# if SMTP + extern char *SmtpPhase; +# endif /* SMTP */ + + /* + ** Handle special network error codes. + ** + ** These are 4.2/4.3bsd specific; they should be in daemon.c. + */ + + dnsmsg = NULL; + switch (errnum) + { +# if defined(DAEMON) && defined(ETIMEDOUT) + case ETIMEDOUT: + case ECONNRESET: + bp = buf; +#if HASSTRERROR + snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum)); +#else + if (errnum >= 0 && errnum < sys_nerr) + snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]); + else + snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum); +#endif + bp += strlen(bp); + if (CurHostName != NULL) + { + if (errnum == ETIMEDOUT) + { + snprintf(bp, SPACELEFT(buf, bp), " with "); + bp += strlen(bp); + } + else + { + bp = buf; + snprintf(bp, SPACELEFT(buf, bp), + "Connection reset by "); + bp += strlen(bp); + } + snprintf(bp, SPACELEFT(buf, bp), "%s", + shortenstring(CurHostName, MAXSHORTSTR)); + bp += strlen(buf); + } + if (SmtpPhase != NULL) + { + snprintf(bp, SPACELEFT(buf, bp), " during %s", + SmtpPhase); + } + return (buf); + + case EHOSTDOWN: + if (CurHostName == NULL) + break; + (void) snprintf(buf, sizeof buf, "Host %s is down", + shortenstring(CurHostName, MAXSHORTSTR)); + return (buf); + + case ECONNREFUSED: + if (CurHostName == NULL) + break; + (void) snprintf(buf, sizeof buf, "Connection refused by %s", + shortenstring(CurHostName, MAXSHORTSTR)); + return (buf); +# endif + +# 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 + + 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"; + } + + if (dnsmsg != NULL) + { + bp = buf; + strcpy(bp, "Name server: "); + bp += strlen(bp); + if (CurHostName != NULL) + { + snprintf(bp, SPACELEFT(buf, bp), "%s: ", + shortenstring(CurHostName, MAXSHORTSTR)); + bp += strlen(bp); + } + snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg); + return buf; + } + +#if HASSTRERROR + return strerror(errnum); +#else + if (errnum > 0 && errnum < sys_nerr) + return (sys_errlist[errnum]); + + (void) snprintf(buf, sizeof buf, "Error %d", errnum); + return (buf); +#endif +} diff --git a/contrib/sendmail/src/headers.c b/contrib/sendmail/src/headers.c new file mode 100644 index 0000000..a04f59e --- /dev/null +++ b/contrib/sendmail/src/headers.c @@ -0,0 +1,1570 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)headers.c 8.127 (Berkeley) 6/4/98"; +#endif /* not lint */ + +# include +# include "sendmail.h" + +/* +** 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; + } +} + /* +** CHOMPHEADER -- process and save a header line. +** +** Called by collect and by readcf to deal with header lines. +** +** Parameters: +** line -- header as a text line. +** def -- if set, this is a default value. +** 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. +*/ + +struct hdrinfo NormalHeader = { NULL, 0, NULL }; + +int +chompheader(line, def, hdrp, e) + char *line; + bool def; + HDR **hdrp; + register ENVELOPE *e; +{ + register char *p; + register HDR *h; + HDR **hp; + char *fname; + char *fvalue; + bool cond = FALSE; + bool headeronly; + STAB *s; + struct hdrinfo *hi; + BITMAP mopts; + + if (tTd(31, 6)) + { + printf("chompheader: "); + xputs(line); + printf("\n"); + } + + headeronly = hdrp != NULL; + if (!headeronly) + hdrp = &e->e_header; + + /* strip off options */ + clrbitmap(mopts); + p = line; + if (*p == '?') + { + /* have some */ + register char *q = strchr(p + 1, *p); + + if (q != NULL) + { + *q++ = '\0'; + while (*++p != '\0') + setbitn(*p, mopts); + p = q; + } + else + syserr("553 header syntax error, line \"%s\"", line); + cond = TRUE; + } + + /* find canonical name */ + fname = p; + while (isascii(*p) && isgraph(*p) && *p != ':') + p++; + fvalue = p; + while (isascii(*p) && isspace(*p)) + p++; + if (*p++ != ':' || fname == fvalue) + { + syserr("553 header syntax error, line \"%s\"", line); + return 0; + } + *fvalue = '\0'; + fvalue = p; + + /* strip field value on front */ + if (*fvalue == ' ') + fvalue++; + + /* 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 (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; + + if (strtorwset(p, &endp, ST_ENTER) > 0) + { + *endp = '\0'; + s = stab(fname, ST_HEADER, ST_ENTER); + s->s_header.hi_ruleset = newstr(p); + } + 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) + printf("no header flags match\n"); + else + printf("header match, flags=%x, ruleset=%s\n", + hi->hi_flags, + hi->hi_ruleset == NULL ? "" : hi->hi_ruleset); + } + + /* see if this is a resent message */ + if (!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 && !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 (!def && hi->hi_ruleset != NULL) + (void) rscheck(hi->hi_ruleset, fvalue, NULL, e); + + /* + ** 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. + */ + + p = "resent-from"; + if (!bitset(EF_RESENT, e->e_flags)) + p += 7; + if (!def && !headeronly && !bitset(EF_QUEUERUN, e->e_flags) && + strcasecmp(fname, p) == 0) + { + if (tTd(31, 2)) + { + printf("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 && + (strcmp(fvalue, e->e_from.q_paddr) == 0 || + strcmp(fvalue, e->e_from.q_user) == 0)) + return hi->hi_flags; + } + + /* delete default value for this header */ + for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link) + { + if (strcasecmp(fname, h->h_field) == 0 && + bitset(H_DEFAULT, h->h_flags) && + !bitset(H_FORCE, h->h_flags)) + { + h->h_value = NULL; + if (!cond) + { + /* copy conditions from default case */ + bcopy((char *)h->h_mflags, (char *)mopts, + sizeof mopts); + } + } + } + + /* create a new node */ + h = (HDR *) xalloc(sizeof *h); + h->h_field = newstr(fname); + h->h_value = newstr(fvalue); + h->h_link = NULL; + bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts); + *hp = h; + h->h_flags = hi->hi_flags; + + /* strip EOH flag if parsing MIME headers */ + if (headeronly) + h->h_flags &= ~H_EOH; + if (def) + h->h_flags |= H_DEFAULT; + if (cond) + h->h_flags |= H_CHECK; + + /* hack to see if this is a new format message */ + if (!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; +} + /* +** 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. +** value -- the value of the field. +** hp -- an indirect pointer to the header structure list. +** +** Returns: +** none. +** +** Side Effects: +** adds the field on the list of headers for this envelope. +*/ + +void +addheader(field, value, hdrlist) + char *field; + char *value; + HDR **hdrlist; +{ + register HDR *h; + STAB *s; + HDR **hp; + + /* find info struct */ + s = stab(field, ST_HEADER, ST_FIND); + + /* find current place in list -- keep back pointer? */ + for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link) + { + if (strcasecmp(field, h->h_field) == 0) + break; + } + + /* allocate space for new header */ + h = (HDR *) xalloc(sizeof *h); + h->h_field = field; + h->h_value = newstr(value); + h->h_link = *hp; + h->h_flags = H_DEFAULT; + if (s != NULL) + h->h_flags |= s->s_header.hi_flags; + clrbitmap(h->h_mflags); + *hp = 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. +** 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) && + 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; +{ + register char *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. +** +** Returns: +** none. +** +** Side Effects: +** Sets a bunch of global variables from information +** in the collected header. +** Aborts the message if the hop count is exceeded. +*/ + +void +eatheader(e, full) + register ENVELOPE *e; + bool full; +{ + register HDR *h; + register char *p; + int hopcnt = 0; + char *msgid; + char buf[MAXLINE]; + extern int priencode __P((char *)); + + /* + ** Set up macros for possible expansion in headers. + */ + + define('f', e->e_sender, e); + define('g', e->e_sender, e); + if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0') + define('u', e->e_origrcpt, e); + else + define('u', NULL, e); + + /* full name of from person */ + p = hvalue("full-name", e->e_header); + if (p != NULL) + { + extern bool rfc822_string __P((char *)); + + if (!rfc822_string(p)) + { + extern char *addquotes __P((char *)); + + /* + ** Quote a full name with special characters + ** as a comment so crackaddr() doesn't destroy + ** the name portion of the address. + */ + p = addquotes(p); + } + define('x', p, e); + } + + if (tTd(32, 1)) + printf("----- collected header -----\n"); + msgid = NULL; + for (h = e->e_header; h != NULL; h = h->h_link) + { + if (tTd(32, 1)) + printf("%s: ", h->h_field); + if (h->h_value == NULL) + { + if (tTd(32, 1)) + printf("\n"); + continue; + } + + /* do early binding */ + if (bitset(H_DEFAULT, h->h_flags)) + { + if (tTd(32, 1)) + { + printf("("); + xputs(h->h_value); + printf(") "); + } + expand(h->h_value, buf, sizeof buf, e); + if (buf[0] != '\0') + { + if (bitset(H_FROM, h->h_flags)) + { + extern char *crackaddr __P((char *)); + + expand(crackaddr(buf), buf, sizeof buf, e); + } + h->h_value = newstr(buf); + h->h_flags &= ~H_DEFAULT; + } + } + + if (tTd(32, 1)) + { + xputs(h->h_value); + printf("\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 + + (void) sendtolist(h->h_value, 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 + } + + /* save the message-id for logging */ + p = "resent-message-id"; + if (!bitset(EF_RESENT, e->e_flags)) + p += 7; + if (strcasecmp(h->h_field, p) == 0) + { + msgid = h->h_value; + while (isascii(*msgid) && isspace(*msgid)) + msgid++; + } + } + if (tTd(32, 1)) + printf("----------------------------\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; + + /* 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; + } + + /* message timeout priority */ + p = hvalue("priority", e->e_header); + if (p != NULL) + { + /* (this should be in the configuration file) */ + if (strcasecmp(p, "urgent") == 0) + e->e_timeoutclass = TOC_URGENT; + else if (strcasecmp(p, "normal") == 0) + e->e_timeoutclass = TOC_NORMAL; + else if (strcasecmp(p, "non-urgent") == 0) + e->e_timeoutclass = TOC_NONURGENT; + } + + /* date message originated */ + p = hvalue("posted-date", e->e_header); + if (p == NULL) + p = hvalue("date", e->e_header); + if (p != NULL) + define('a', p, e); + + /* check to see if this is a MIME message */ + if ((e->e_bodytype != NULL && + 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)) + printf("eatheader: setsender(*%s == %s)\n", + hi->hi_field, p); + setsender(p, e, NULL, '\0', TRUE); + } + } + + /* + ** Log collection information. + */ + + if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) + logsender(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; + int l; + char hbuf[MAXNAME + 1]; + char sbuf[MAXLINE + 1]; + char mbuf[MAXNAME + 1]; + + /* don't allow newlines in the message-id */ + if (msgid != NULL) + { + l = strlen(msgid); + if (l > sizeof mbuf - 1) + l = sizeof mbuf - 1; + bcopy(msgid, mbuf, 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) + ; + else if (RealHostName == NULL) + name = "localhost"; + else if (RealHostName[0] == '[') + name = RealHostName; + else + { + name = hbuf; + (void) snprintf(hbuf, sizeof hbuf, "%.80s", RealHostName); + if (RealHostAddr.sa.sa_family != 0) + { + p = &hbuf[strlen(hbuf)]; + (void) snprintf(p, SPACELEFT(hbuf, p), " (%.100s)", + anynet_ntoa(&RealHostAddr)); + } + } + + /* some versions of syslog only take 5 printf args */ +# if (SYSLOG_BUFSIZE) >= 256 + sbp = sbuf; + snprintf(sbp, SPACELEFT(sbuf, sbp), + "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d", + e->e_from.q_paddr == NULL ? "" : e->e_from.q_paddr, + e->e_msgsize, e->e_class, e->e_msgpriority, e->e_nrcpts); + sbp += strlen(sbp); + if (msgid != NULL) + { + snprintf(sbp, SPACELEFT(sbuf, sbp), ", msgid=%.100s", mbuf); + sbp += strlen(sbp); + } + if (e->e_bodytype != NULL) + { + (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", bodytype=%.20s", + e->e_bodytype); + sbp += strlen(sbp); + } + p = macvalue('r', e); + if (p != NULL) + (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", proto=%.20s", p); + sm_syslog(LOG_INFO, e->e_id, + "%.850s, relay=%.100s", + sbuf, name); + +# else /* short syslog buffer */ + + 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, pri=%ld, nrcpts=%d", + e->e_msgsize, e->e_class, e->e_msgpriority, 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) + { + snprintf(sbp, SPACELEFT(sbuf, sbp), "bodytype=%.20s, ", e->e_bodytype); + sbp += strlen(sbp); + } + p = macvalue('r', e); + if (p != NULL) + { + snprintf(sbp, SPACELEFT(sbuf, sbp), "proto=%.20s, ", p); + sbp += strlen(sbp); + } + sm_syslog(LOG_INFO, e->e_id, + "%.400srelay=%.100s", sbuf, name); +# endif +} + /* +** PRIENCODE -- encode external priority names into internal values. +** +** Parameters: +** p -- priority in ascii. +** +** Returns: +** priority as a numeric level. +** +** Side Effects: +** none. +*/ + +int +priencode(p) + char *p; +{ + register int i; + + for (i = 0; i < NumPriorities; i++) + { + if (!strcasecmp(p, Priorities[i].pri_name)) + 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. +** +** 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. +** +** 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. +*/ + +char * +crackaddr(addr) + register char *addr; +{ + register char *p; + register char c; + int cmtlev; + int realcmtlev; + int anglelev, realanglelev; + int copylev; + int bracklev; + bool qmode; + bool realqmode; + bool skipping; + bool putgmac = FALSE; + bool quoteit = FALSE; + bool gotangle = FALSE; + bool gotcolon = FALSE; + register char *bp; + char *buflim; + char *bufhead; + char *addrhead; + static char buf[MAXNAME + 1]; + + if (tTd(33, 1)) + printf("crackaddr(%s)\n", addr); + + /* strip leading spaces */ + while (*addr != '\0' && isascii(*addr) && isspace(*addr)) + addr++; + + /* + ** Start by assuming we have no angle brackets. This will be + ** adjusted later if we find them. + */ + + bp = bufhead = buf; + buflim = &buf[sizeof buf - 7]; + p = addrhead = addr; + copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0; + bracklev = 0; + qmode = realqmode = FALSE; + + while ((c = *p++) != '\0') + { + /* + ** If the buffer is overful, go into a special "skipping" + ** mode that tries to keep legal syntax but doesn't actually + ** output things. + */ + + skipping = bp >= buflim; + + if (copylev > 0 && !skipping) + *bp++ = 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 && !skipping) + *bp++ = c; + goto putg; + } + + /* check for quoted strings */ + if (c == '"' && cmtlev <= 0) + { + qmode = !qmode; + if (copylev > 0 && !skipping) + realqmode = !realqmode; + continue; + } + if (qmode) + goto putg; + + /* check for comments */ + if (c == '(') + { + cmtlev++; + + /* allow space for closing paren */ + if (!skipping) + { + buflim--; + realcmtlev++; + if (copylev++ <= 0) + { + if (bp != bufhead) + *bp++ = ' '; + *bp++ = c; + } + } + } + if (cmtlev > 0) + { + if (c == ')') + { + cmtlev--; + copylev--; + if (!skipping) + { + realcmtlev--; + buflim++; + } + } + continue; + } + else if (c == ')') + { + /* syntax error: unmatched ) */ + if (copylev > 0 && !skipping) + 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 && !skipping) + { + *bp++ = c; + *bp++ = *p; + } + p++; + goto putg; + } + + gotcolon = TRUE; + + bp = bufhead; + if (quoteit) + { + *bp++ = '"'; + + /* back up over the ':' and any spaces */ + --p; + while (isascii(*--p) && isspace(*p)) + continue; + p++; + } + for (q = addrhead; q < p; ) + { + c = *q++; + if (bp < buflim) + { + if (quoteit && c == '"') + *bp++ = '\\'; + *bp++ = c; + } + } + if (quoteit) + { + if (bp == &bufhead[1]) + bp--; + else + *bp++ = '"'; + while ((c = *p++) != ':') + { + if (bp < buflim) + *bp++ = c; + } + *bp++ = c; + } + + /* any trailing white space is part of group: */ + while (isascii(*p) && isspace(*p) && bp < buflim) + *bp++ = *p++; + copylev = 0; + putgmac = quoteit = FALSE; + bufhead = bp; + addrhead = p; + continue; + } + + if (c == ';' && copylev <= 0 && !ColonOkInAddr) + { + if (bp < buflim) + *bp++ = 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 (!skipping) + realanglelev = 1; + + bp = bufhead; + if (quoteit) + { + *bp++ = '"'; + + /* back up over the '<' and any spaces */ + --p; + while (isascii(*--p) && isspace(*p)) + continue; + p++; + } + for (q = addrhead; q < p; ) + { + c = *q++; + if (bp < buflim) + { + if (quoteit && c == '"') + *bp++ = '\\'; + *bp++ = c; + } + } + if (quoteit) + { + if (bp == &buf[1]) + bp--; + else + *bp++ = '"'; + while ((c = *p++) != '<') + { + if (bp < buflim) + *bp++ = c; + } + *bp++ = c; + } + copylev = 0; + putgmac = quoteit = FALSE; + continue; + } + + if (c == '>') + { + if (anglelev > 0) + { + anglelev--; + if (!skipping) + { + realanglelev--; + buflim++; + } + } + else if (!skipping) + { + /* syntax error: unmatched > */ + if (copylev > 0) + bp--; + quoteit = TRUE; + continue; + } + if (copylev++ <= 0) + *bp++ = c; + continue; + } + + /* must be a real address character */ + putg: + if (copylev <= 0 && !putgmac) + { + if (bp > bufhead && bp[-1] == ')') + *bp++ = ' '; + *bp++ = MACROEXPAND; + *bp++ = 'g'; + putgmac = TRUE; + } + } + + /* repair any syntactic damage */ + if (realqmode) + *bp++ = '"'; + while (realcmtlev-- > 0) + *bp++ = ')'; + while (realanglelev-- > 0) + *bp++ = '>'; + *bp++ = '\0'; + + if (tTd(33, 1)) + { + printf("crackaddr=>`"); + xputs(buf); + printf("'\n"); + } + + return (buf); +} + /* +** PUTHEADER -- put the header part of a message from the in-core copy +** +** Parameters: +** mci -- the connection information. +** h -- the header to put. +** e -- envelope to use. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +/* + * Macro for fast max (not available in e.g. DG/UX, 386/ix). + */ +#ifndef MAX +# define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +void +putheader(mci, hdr, e) + register MCI *mci; + HDR *hdr; + register ENVELOPE *e; +{ + register HDR *h; + char buf[MAX(MAXLINE,BUFSIZ)]; + char obuf[MAXLINE]; + + if (tTd(34, 1)) + printf("--- 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; + extern bool bitintersect __P((BITMAP, BITMAP)); + + if (tTd(34, 11)) + { + printf(" %s: ", h->h_field); + xputs(p); + } + + /* suppress Content-Transfer-Encoding: if we are MIMEing */ + if (bitset(H_CTE, h->h_flags) && + bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags)) + { + if (tTd(34, 11)) + printf(" (skipped (content-transfer-encoding))\n"); + continue; + } + + if (bitset(MCIF_INMIME, mci->mci_flags)) + { + if (tTd(34, 11)) + printf("\n"); + put_vanilla_header(h, p, mci); + continue; + } + + if (bitset(H_CHECK|H_ACHECK, h->h_flags) && + !bitintersect(h->h_mflags, mci->mci_mailer->m_flags)) + { + if (tTd(34, 11)) + printf(" (skipped)\n"); + continue; + } + + /* handle Resent-... headers specially */ + if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) + { + if (tTd(34, 11)) + printf(" (skipped (resent))\n"); + continue; + } + + /* suppress return receipts if requested */ + if (bitset(H_RECEIPTTO, h->h_flags) && +#if _FFR_DSN_RRT_OPTION + (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags))) +#else + bitset(EF_NORECEIPT, e->e_flags)) +#endif + { + if (tTd(34, 11)) + printf(" (skipped (receipt))\n"); + continue; + } + + /* macro expand value if generated internally */ + if (bitset(H_DEFAULT, h->h_flags)) + { + expand(p, buf, sizeof buf, e); + p = buf; + if (*p == '\0') + { + if (tTd(34, 11)) + printf(" (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)) + printf(" (skipped -- bcc)\n"); + } + else + { + /* no other recipient headers: truncate value */ + (void) snprintf(obuf, sizeof obuf, "%s:", + h->h_field); + putline(obuf, mci); + } + continue; + } + + if (tTd(34, 11)) + printf("\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); + } + else + { + put_vanilla_header(h, p, mci); + } + } + + /* + ** If we are converting this to a MIME message, add the + ** MIME headers. + */ + +#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, mci->mci_flags)) + { + if (hvalue("MIME-Version", e->e_header) == NULL) + putline("MIME-Version: 1.0", mci); + if (hvalue("Content-Type", e->e_header) == NULL) + { + snprintf(obuf, sizeof obuf, + "Content-Type: text/plain; charset=%s", + defcharset(e)); + putline(obuf, mci); + } + if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL) + putline("Content-Transfer-Encoding: 8bit", mci); + } +#endif +} + /* +** 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: +** none. +*/ + +void +put_vanilla_header(h, v, mci) + HDR *h; + char *v; + MCI *mci; +{ + register char *nlp; + register char *obp; + int putflags; + char obuf[MAXLINE]; + + putflags = PXLF_HEADER; +#if _FFR_7BITHDRS + if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) + putflags |= PXLF_STRIP8BIT; +#endif + (void) snprintf(obuf, sizeof obuf, "%.200s: ", h->h_field); + obp = obuf + strlen(obuf); + while ((nlp = strchr(v, '\n')) != NULL) + { + int l; + + l = nlp - v; + if (SPACELEFT(obuf, obp) - 1 < l) + l = SPACELEFT(obuf, obp) - 1; + + snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v); + putxline(obuf, strlen(obuf), mci, putflags); + v += l + 1; + obp = obuf; + if (*v != ' ' && *v != '\t') + *obp++ = ' '; + } + snprintf(obp, SPACELEFT(obuf, obp), "%.*s", + sizeof obuf - (obp - obuf) - 1, v); + putxline(obuf, strlen(obuf), mci, putflags); +} + /* +** 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. +** +** Returns: +** none. +** +** Side Effects: +** outputs "p" to file "fp". +*/ + +void +commaize(h, p, oldstyle, mci, e) + register HDR *h; + register char *p; + bool oldstyle; + register MCI *mci; + register ENVELOPE *e; +{ + register char *obp; + int opos; + int omax; + bool firstone = TRUE; + int putflags = PXLF_HEADER; + char obuf[MAXLINE + 3]; + + /* + ** Output the address list translated by the + ** mailer and with commas. + */ + + if (tTd(14, 2)) + printf("commaize(%s: %s)\n", h->h_field, p); + +#if _FFR_7BITHDRS + if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) + putflags |= PXLF_STRIP8BIT; +#endif + + obp = obuf; + (void) snprintf(obp, SPACELEFT(obuf, obp), "%.200s: ", h->h_field); + opos = strlen(h->h_field) + 2; + if (opos > 202) + opos = 202; + obp += opos; + 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 stat; + + /* + ** 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; + for (;;) + { + auto char *oldp; + char pvpbuf[PSBUFSIZE]; + + (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf, + sizeof pvpbuf, &oldp, NULL); + p = oldp; + + /* look to see if we have an at sign */ + while (*p != '\0' && isascii(*p) && isspace(*p)) + p++; + + if (*p != '@') + { + p = oldp; + break; + } + p += *p == '@' ? 1 : 2; + 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; + 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)) + { + extern char *udbsender __P((char *)); + char *q; + + q = udbsender(name); + if (q != NULL) + name = q; + } +#endif + stat = EX_OK; + name = remotename(name, mci->mci_mailer, flags, &stat, 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) + { + snprintf(obp, SPACELEFT(obuf, obp), ",\n"); + putxline(obuf, strlen(obuf), mci, putflags); + obp = obuf; + (void) strcpy(obp, " "); + opos = strlen(obp); + obp += opos; + opos += strlen(name); + } + else if (!firstone) + { + snprintf(obp, SPACELEFT(obuf, obp), ", "); + obp += 2; + } + + while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) + *obp++ = c; + firstone = FALSE; + *p = savechar; + } + *obp = '\0'; + putxline(obuf, strlen(obuf), mci, putflags); +} + /* +** COPYHEADER -- copy header list +** +** This routine is the equivalent of newstr for header lists +** +** Parameters: +** header -- list of header structures to copy. +** +** Returns: +** a copy of 'header'. +** +** Side Effects: +** none. +*/ + +HDR * +copyheader(header) + register HDR *header; +{ + register HDR *newhdr; + HDR *ret; + register HDR **tail = &ret; + + while (header != NULL) + { + newhdr = (HDR *) xalloc(sizeof(HDR)); + STRUCTCOPY(*header, *newhdr); + *tail = newhdr; + tail = &newhdr->h_link; + header = header->h_link; + } + *tail = NULL; + + return ret; +} diff --git a/contrib/sendmail/src/ldap_map.h b/contrib/sendmail/src/ldap_map.h new file mode 100644 index 0000000..9f6a679 --- /dev/null +++ b/contrib/sendmail/src/ldap_map.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +/* +** Support for LDAP. +** +** Contributed by Booker C. Bense . +** Please go to him for support -- since I (Eric) don't run LDAP, I +** can't help you at all. +** +** @(#)ldap_map.h 8.9 (Berkeley) 5/19/98 +*/ + +#ifndef _LDAP_MAP_H +#define _LDAP_MAP_H + +#include + +struct ldap_map_struct +{ + /* needed for ldap_open */ + char *ldaphost; + int ldapport; + + /* Options set in ld struct before ldap_bind_s */ + int deref; + int timelimit; + int sizelimit; + int ldap_options; + + /* args for ldap_bind_s */ + LDAP *ld; + char *binddn; + char *passwd; + int method; + + /* args for ldap_search_st */ + char *base; + int scope; + char *filter; + char *attr[2]; + int attrsonly; + struct timeval timeout; + LDAPMessage *res; +}; + +typedef struct ldap_map_struct LDAP_MAP_STRUCT; + +#define DEFAULT_LDAP_MAP_PORT LDAP_PORT +#define DEFAULT_LDAP_MAP_SCOPE LDAP_SCOPE_SUBTREE +#define DEFAULT_LDAP_MAP_BINDDN NULL +#define DEFAULT_LDAP_MAP_PASSWD NULL +#define DEFAULT_LDAP_MAP_METHOD LDAP_AUTH_SIMPLE +#define DEFAULT_LDAP_MAP_TIMELIMIT 5 +#define DEFAULT_LDAP_MAP_DEREF LDAP_DEREF_NEVER +#define DEFAULT_LDAP_MAP_SIZELIMIT 0 +#define DEFAULT_LDAP_MAP_ATTRSONLY 0 +#define LDAP_MAP_MAX_FILTER 256 +#ifdef LDAP_REFERRALS +# define DEFAULT_LDAP_MAP_LDAP_OPTIONS LDAP_OPT_REFERRALS +#else /* LDAP_REFERRALS */ +# define DEFAULT_LDAP_MAP_LDAP_OPTIONS 0 +#endif /* LDAP_REFERRALS */ + +#endif /* _LDAP_MAP_H */ diff --git a/contrib/sendmail/src/macro.c b/contrib/sendmail/src/macro.c new file mode 100644 index 0000000..c1f9f7b --- /dev/null +++ b/contrib/sendmail/src/macro.c @@ -0,0 +1,437 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)macro.c 8.25 (Berkeley) 5/19/98"; +#endif /* not lint */ + +# include "sendmail.h" + +char *MacroName[256]; /* macro id to name table */ +int NextMacroId = 0240; /* codes for long named macros */ + + +/* +** EXPAND -- macro expand a string using $x escapes. +** +** Parameters: +** s -- the string to expand. +** buf -- the place to put the expansion. +** bufsize -- the size of the buffer. +** e -- envelope in which to work. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +void +expand(s, buf, bufsize, e) + register char *s; + register char *buf; + size_t bufsize; + register ENVELOPE *e; +{ + register char *xp; + register char *q; + bool skipping; /* set if conditionally skipping output */ + bool recurse = FALSE; /* set if recursion required */ + int i; + int skiplev; /* skipping nesting level */ + int iflev; /* if nesting level */ + char xbuf[MACBUFSIZE]; + static int explevel = 0; + + if (tTd(35, 24)) + { + printf("expand("); + xputs(s); + printf(")\n"); + } + + skipping = FALSE; + skiplev = 0; + iflev = 0; + 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; + switch (c & 0377) + { + case CONDIF: /* see if var set */ + iflev++; + c = *++s; + if (skipping) + skiplev++; + else + skipping = macvalue(c, e) == NULL; + continue; + + case CONDELSE: /* change state of skipping */ + if (iflev == 0) + break; + if (skiplev == 0) + skipping = !skipping; + continue; + + case CONDFI: /* stop skipping */ + if (iflev == 0) + break; + iflev--; + if (skiplev == 0) + skipping = FALSE; + if (skipping) + skiplev--; + continue; + + case MACROEXPAND: /* macro interpolation */ + c = *++s & 0377; + if (c != '\0') + q = macvalue(c, e); + else + { + s--; + q = NULL; + } + if (q == NULL) + continue; + break; + } + + /* + ** Interpolate q or output one character + */ + + 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 */ + while ((c = *q++) != '\0' && xp < &xbuf[sizeof xbuf - 1]) + { + /* check for any sendmail metacharacters */ + if ((c & 0340) == 0200) + recurse = TRUE; + *xp++ = c; + } + } + } + *xp = '\0'; + + if (tTd(35, 24)) + { + printf("expand ==> "); + xputs(xbuf); + printf("\n"); + } + + /* recurse as appropriate */ + if (recurse) + { + if (explevel < MaxMacroRecursion) + { + explevel++; + expand(xbuf, buf, bufsize, e); + explevel--; + return; + } + syserr("expand: recursion too deep (%d max)", + MaxMacroRecursion); + } + + /* copy results out */ + i = xp - xbuf; + if (i >= bufsize) + i = bufsize - 1; + bcopy(xbuf, buf, i); + buf[i] = '\0'; +} + /* +** DEFINE -- define a macro. +** +** this would be better done using a #define macro. +** +** Parameters: +** n -- the macro name. +** v -- the macro value. +** e -- the envelope to store the definition in. +** +** Returns: +** none. +** +** Side Effects: +** e->e_macro[n] is defined. +** +** Notes: +** There is one macro for each ASCII character, +** although they are not all used. The currently +** defined macros are: +** +** $a date in ARPANET format (preferring the Date: line +** of the message) +** $b the current date (as opposed to the date as found +** the message) in ARPANET format +** $c hop count +** $d (current) date in UNIX (ctime) format +** $e the SMTP entry message+ +** $f raw from address +** $g translated from address +** $h to host +** $i queue id +** $j official SMTP hostname, used in messages+ +** $k UUCP node name +** $l UNIX-style from line+ +** $m The domain part of our full name. +** $n name of sendmail ("MAILER-DAEMON" on local +** net typically)+ +** $o delimiters ("operators") for address tokens+ +** (set via OperatorChars option in V6 or later +** sendmail.cf files) +** $p my process id in decimal +** $q the string that becomes an address -- this is +** normally used to combine $g & $x. +** $r protocol used to talk to sender +** $s sender's host name +** $t the current time in seconds since 1/1/1970 +** $u to user +** $v version number of sendmail +** $w our host name (if it can be determined) +** $x signature (full name) of from person +** $y the tty id of our terminal +** $z home directory of to person +** $_ RFC1413 authenticated sender address +** +** Macros marked with + must be defined in the +** configuration file and are used internally, but +** are not set. +** +** There are also some macros that can be used +** arbitrarily to make the configuration file +** cleaner. In general all upper-case letters +** are available. +*/ + +void +define(n, v, e) + int n; + char *v; + register ENVELOPE *e; +{ + if (tTd(35, 9)) + { + printf("%sdefine(%s as ", + (e->e_macro[n & 0377] == NULL) ? "" : "re", macname(n)); + xputs(v); + printf(")\n"); + } + e->e_macro[n & 0377] = v; +} + /* +** MACVALUE -- return uninterpreted value of a macro. +** +** Parameters: +** n -- the name of the macro. +** +** Returns: +** The value of n. +** +** Side Effects: +** none. +*/ + +char * +macvalue(n, e) + int n; + register ENVELOPE *e; +{ + n &= 0377; + while (e != NULL) + { + register char *p = e->e_macro[n]; + + if (p != NULL) + return (p); + e = e->e_parent; + } + return (NULL); +} + /* +** 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. +*/ + +char * +macname(n) + int n; +{ + static char mbuf[2]; + + n &= 0377; + if (bitset(0200, n)) + { + char *p = MacroName[n]; + + if (p != NULL) + return p; + return "***UNDEFINED MACRO***"; + } + mbuf[0] = n; + mbuf[1] = '\0'; + return mbuf; +} + /* +** MACID -- 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: +** The internal id code for this macro. This will +** fit into a single byte. +** +** Side Effects: +** If this is a new macro name, a new id is allocated. +*/ + +int +macid(p, ep) + register char *p; + char **ep; +{ + int mid; + register char *bp; + char mbuf[21]; + + if (tTd(35, 14)) + { + printf("macid("); + xputs(p); + printf(") => "); + } + + if (*p == '\0' || (p[0] == '{' && p[1] == '}')) + { + syserr("Name required for macro/class"); + if (ep != NULL) + *ep = p; + if (tTd(35, 14)) + printf("NULL\n"); + return '\0'; + } + if (*p != '{') + { + /* the macro is its own code */ + if (ep != NULL) + *ep = p + 1; + if (tTd(35, 14)) + printf("%c\n", *p); + return *p; + } + bp = mbuf; + while (*++p != '\0' && *p != '}' && bp < &mbuf[sizeof mbuf]) + { + 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, sizeof mbuf - 1); + } + else if (mbuf[1] == '\0') + { + /* ${x} == $x */ + mid = mbuf[0]; + p++; + } + else + { + register STAB *s; + + s = stab(mbuf, ST_MACRO, ST_ENTER); + if (s->s_macro != 0) + mid = s->s_macro; + else + { + if (NextMacroId > 0377) + { + syserr("Macro/class {%s}: too many long names", mbuf); + s->s_macro = -1; + } + else + { + MacroName[NextMacroId] = s->s_name; + s->s_macro = mid = NextMacroId++; + } + } + p++; + } + if (ep != NULL) + *ep = p; + if (tTd(35, 14)) + printf("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; +{ + register STAB *s; + + s = stab(str, ST_CLASS, ST_FIND); + return s != NULL && bitnset(cl & 0xff, s->s_class); +} diff --git a/contrib/sendmail/src/mailq.1 b/contrib/sendmail/src/mailq.1 new file mode 100644 index 0000000..fa1d0d5 --- /dev/null +++ b/contrib/sendmail/src/mailq.1 @@ -0,0 +1,67 @@ +.\" Copyright (c) 1998 Sendmail, Inc. 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. +.\" +.\" +.\" @(#)mailq.1 8.10 (Berkeley) 5/19/98 +.\" +.Dd May 19, 1998 +.Dt MAILQ 1 +.Os BSD 4 +.Sh NAME +.Nm mailq +.Nd print the mail queue +.Sh SYNOPSIS +.Nm mailq +.Op Fl v +.Sh DESCRIPTION +.Nm 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, +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 following lines show message recipients, +one per line. +.Pp +.Nm Mailq +is identical to +.Dq Li "sendmail -bp" . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 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. +.El +.Pp +The +.Nm mailq +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr sendmail 8 +.Sh HISTORY +The +.Nm mailq +command appeared in +.Bx 4.0 . diff --git a/contrib/sendmail/src/mailstats.h b/contrib/sendmail/src/mailstats.h new file mode 100644 index 0000000..2121e14 --- /dev/null +++ b/contrib/sendmail/src/mailstats.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + * + * @(#)mailstats.h 8.8 (Berkeley) 5/19/98 + */ + +#define STAT_VERSION 2 +#define STAT_MAGIC 0x1B1DE + +/* +** Statistics structure. +*/ + +struct statistics +{ + int stat_magic; /* magic number */ + int stat_version; /* stat file version */ + time_t stat_itime; /* file initialization time */ + short stat_size; /* size of this structure */ + long stat_nf[MAXMAILERS]; /* # msgs from each mailer */ + long stat_bf[MAXMAILERS]; /* kbytes from each mailer */ + long stat_nt[MAXMAILERS]; /* # msgs to each mailer */ + long stat_bt[MAXMAILERS]; /* kbytes to each mailer */ + long stat_nr[MAXMAILERS]; /* # rejects by each mailer */ + long stat_nd[MAXMAILERS]; /* # discards by each mailer */ +}; diff --git a/contrib/sendmail/src/main.c b/contrib/sendmail/src/main.c new file mode 100644 index 0000000..f014d83 --- /dev/null +++ b/contrib/sendmail/src/main.c @@ -0,0 +1,2701 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1998 Sendmail, Inc. All rights reserved.\n\ + Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\ + Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.302 (Berkeley) 6/4/98"; +#endif /* not lint */ + +#define _DEFINE + +#include "sendmail.h" +#include +#include +#if NAMED_BIND +#include +#endif + +/* +** 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/sendmail.cf +** (read by readcf.c). +** +** Usage: +** /usr/lib/sendmail [flags] addr ... +** +** See the associated documentation for details. +** +** Author: +** 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 the my employers is gratefully acknowledged. +** Few of them (Britton-Lee in particular) have had +** anything to gain from my involvement in this project. +*/ + + +int NextMailer; /* "free" index into Mailer struct */ +char *FullName; /* sender's full name */ +ENVELOPE BlankEnvelope; /* a "blank" envelope */ +ENVELOPE MainEnvelope; /* the envelope around the basic letter */ +ADDRESS NullAddress = /* a null address */ + { "", "", NULL, "" }; +char *CommandLineArgs; /* command line args for pid file */ +bool Warn_Q_option = FALSE; /* warn about Q option use */ +char **SaveArgv; /* argument vector for re-execing */ +int MissingFds = 0; /* bit map of fds missing on startup */ + +#ifdef NGROUPS_MAX +GIDSET_T InitialGidSet[NGROUPS_MAX]; +#endif + +static void obsolete __P((char **)); +extern void printmailer __P((MAILER *)); +extern void tTflag __P((char *)); + +#if DAEMON && !SMTP +ERROR %%%% Cannot have DAEMON mode without SMTP %%%% ERROR +#endif /* DAEMON && !SMTP */ +#if SMTP && !QUEUE +ERROR %%%% Cannot have SMTP mode without QUEUE %%%% ERROR +#endif /* DAEMON && !SMTP */ + +#define MAXCONFIGLEVEL 8 /* highest config version level known */ + +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; + bool queuemode = FALSE; /* process queue requests */ + bool safecf = TRUE; + bool warn_C_flag = FALSE; + char warn_f_flag = '\0'; + bool run_in_foreground = FALSE; /* -bD mode */ + static bool reenter = FALSE; + struct passwd *pw; + struct hostent *hp; + char *nullserver = NULL; + bool forged; + char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ + static char rnamebuf[MAXNAME]; /* holds RealUserName */ + char *emptyenviron[1]; + QUEUE_CHAR *new; + extern int DtableSize; + extern int optind; + extern int opterr; + extern char *optarg; + extern char **environ; + extern time_t convtime __P((char *, char)); + extern SIGFUNC_DECL intsig __P((int)); + extern struct hostent *myhostname __P((char *, int)); + extern char *getauthinfo __P((int, bool *)); + extern char *getcfname __P((void)); + extern SIGFUNC_DECL sigusr1 __P((int)); + extern SIGFUNC_DECL sighup __P((int)); + extern void initmacros __P((ENVELOPE *)); + extern void init_md __P((int, char **)); + extern int getdtsize __P((void)); + extern void tTsetup __P((u_char *, int, char *)); + extern void setdefaults __P((ENVELOPE *)); + extern void initsetproctitle __P((int, char **, char **)); + extern void init_vendor_macros __P((ENVELOPE *)); + extern void load_if_names __P((void)); + extern void vendor_pre_defaults __P((ENVELOPE *)); + extern void vendor_post_defaults __P((ENVELOPE *)); + extern void readcf __P((char *, bool, ENVELOPE *)); + extern void printqueue __P((void)); + extern void sendtoargv __P((char **, ENVELOPE *)); + extern void resetlimits __P((void)); +#ifndef HASUNSETENV + extern void unsetenv __P((char *)); +#endif + + /* + ** Check to see if we reentered. + ** This would normally happen if e_putheader or e_putbody + ** were NULL when invoked. + */ + + if (reenter) + { + syserr("main: reentered!"); + abort(); + } + reenter = TRUE; + + /* avoid null pointer dereferences */ + TermEscape.te_rv_on = TermEscape.te_rv_off = ""; + + /* do machine-dependent initializations */ + init_md(argc, argv); + + /* in 4.4BSD, the table can be huge; impose a reasonable limit */ + DtableSize = getdtsize(); + if (DtableSize > 256) + DtableSize = 256; + + /* + ** Be sure we have enough file descriptors. + ** But also be sure that 0, 1, & 2 are open. + */ + + fill_fd(STDIN_FILENO, NULL); + fill_fd(STDOUT_FILENO, NULL); + fill_fd(STDERR_FILENO, NULL); + + i = DtableSize; + while (--i > 0) + { + if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) + (void) close(i); + } + errno = 0; + +#if LOG +# ifdef LOG_MAIL + openlog("sendmail", LOG_PID, LOG_MAIL); +# else + openlog("sendmail", LOG_PID); +# endif +#endif + + if (MissingFds != 0) + { + char mbuf[MAXLINE]; + + mbuf[0] = '\0'; + if (bitset(1 << STDIN_FILENO, MissingFds)) + strcat(mbuf, ", stdin"); + if (bitset(1 << STDOUT_FILENO, MissingFds)) + strcat(mbuf, ", stdout"); + if (bitset(1 << STDERR_FILENO, MissingFds)) + strcat(mbuf, ", stderr"); + syserr("File descriptors missing on startup: %s", &mbuf[2]); + } + + /* reset status from syserr() calls for missing file descriptors */ + Errors = 0; + ExitStat = EX_OK; + +#if XDEBUG + checkfd012("after openlog"); +#endif + + tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); + +#ifdef NGROUPS_MAX + /* save initial group set for future checks */ + i = getgroups(NGROUPS_MAX, InitialGidSet); + if (i == 0) + InitialGidSet[0] = (GID_T) -1; + while (i < NGROUPS_MAX) + InitialGidSet[i++] = InitialGidSet[0]; +#endif + + /* drop group id privileges (RunAsUser not yet set) */ + (void) drop_privileges(FALSE); + +#ifdef SIGUSR1 + /* arrange to dump state on user-1 signal */ + setsignal(SIGUSR1, sigusr1); +#endif + + /* initialize for setproctitle */ + initsetproctitle(argc, argv, envp); + + /* Handle any non-getoptable constructions. */ + obsolete(argv); + + /* + ** Do a quick prescan of the argument list. + */ + +#if defined(__osf__) || defined(_AIX3) +# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x" +#endif +#if defined(sony_news) +# define OPTIONS "B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:" +#endif +#ifndef OPTIONS +# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:" +#endif + opterr = 0; + while ((j = getopt(argc, argv, OPTIONS)) != -1) + { + switch (j) + { + case 'd': + /* hack attack -- see if should use ANSI mode */ + if (strcmp(optarg, "ANSI") == 0) + { + TermEscape.te_rv_on = "\033[7m"; + TermEscape.te_rv_off = "\033[0m"; + break; + } + tTflag(optarg); + setbuf(stdout, (char *) NULL); + break; + } + } + opterr = 1; + + /* 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); + + RealUid = getuid(); + RealGid = getgid(); + + pw = sm_getpwuid(RealUid); + if (pw != NULL) + (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); + else + (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid); + RealUserName = rnamebuf; + + if (tTd(0, 101)) + { + printf("Version %s\n", Version); + endpwent(); + setuid(RealUid); + exit(EX_OK); + } + + /* + ** if running non-setuid binary as non-root, pretend + ** we are the RunAsUid + */ + if (RealUid != 0 && geteuid() == RealUid) + { + if (tTd(47, 1)) + printf("Non-setuid binary: RunAsUid = RealUid = %d\n", + (int)RealUid); + RunAsUid = RealUid; + } + else if (geteuid() != 0) + RunAsUid = geteuid(); + + if (RealUid != 0 && getegid() == RealGid) + RunAsGid = RealGid; + + if (tTd(47, 5)) + { + printf("main: e/ruid = %d/%d e/rgid = %d/%d\n", + (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid()); + printf("main: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid); + } + + /* save command line arguments */ + i = 0; + for (av = argv; *av != NULL; ) + i += strlen(*av++) + 1; + SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1)); + CommandLineArgs = xalloc(i); + p = CommandLineArgs; + for (av = argv, i = 0; *av != NULL; ) + { + SaveArgv[i++] = newstr(*av); + if (av != argv) + *p++ = ' '; + strcpy(p, *av++); + p += strlen(p); + } + SaveArgv[i] = NULL; + + if (tTd(0, 1)) + { + int ll; + extern char *CompileOptions[]; + + printf("Version %s\n Compiled with:", Version); + av = CompileOptions; + ll = 7; + while (*av != NULL) + { + if (ll + strlen(*av) > 63) + { + putchar('\n'); + ll = 0; + } + if (ll == 0) + { + putchar('\t'); + putchar('\t'); + } + else + putchar(' '); + printf("%s", *av); + ll += strlen(*av++) + 1; + } + putchar('\n'); + } + if (tTd(0, 10)) + { + int ll; + extern char *OsCompileOptions[]; + + printf(" OS Defines:"); + av = OsCompileOptions; + ll = 7; + while (*av != NULL) + { + if (ll + strlen(*av) > 63) + { + putchar('\n'); + ll = 0; + } + if (ll == 0) + { + putchar('\t'); + putchar('\t'); + } + else + putchar(' '); + printf("%s", *av); + ll += strlen(*av++) + 1; + } + putchar('\n'); +#ifdef _PATH_UNIX + printf("Kernel symbols:\t%s\n", _PATH_UNIX); +#endif + printf(" Def Conf file:\t%s\n", getcfname()); + printf(" Pid file:\t%s\n", PidFile); + } + + InChannel = stdin; + OutChannel = stdout; + + /* 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; + + tzlen = strlen(p) + 4; + tz = xalloc(tzlen); + snprintf(tz, tzlen, "TZ=%s", p); + putenv(tz); + } + + /* prime the child environment */ + setuserenv("AGENT", "sendmail"); + + if (setsignal(SIGINT, SIG_IGN) != SIG_IGN) + (void) setsignal(SIGINT, intsig); + (void) setsignal(SIGTERM, intsig); + (void) setsignal(SIGPIPE, SIG_IGN); + OldUmask = umask(022); + OpMode = MD_DELIVER; + FullName = getextenv("NAME"); + + /* + ** Initialize name server if it is going to be used. + */ + +#if NAMED_BIND + if (!bitset(RES_INIT, _res.options)) + res_init(); + if (tTd(8, 8)) + _res.options |= RES_DEBUG; + else + _res.options &= ~RES_DEBUG; +# ifdef RES_NOALIASES + _res.options |= RES_NOALIASES; +# endif +#endif + + errno = 0; + from = NULL; + + /* initialize some macros, etc. */ + initmacros(CurEnv); + init_vendor_macros(CurEnv); + + /* version */ + define('v', Version, CurEnv); + + /* hostname */ + hp = myhostname(jbuf, sizeof jbuf); + if (jbuf[0] != '\0') + { + struct utsname utsname; + + if (tTd(0, 4)) + printf("canonical name: %s\n", jbuf); + define('w', newstr(jbuf), CurEnv); /* must be new string */ + define('j', newstr(jbuf), CurEnv); + setclass('w', jbuf); + + p = strchr(jbuf, '.'); + if (p != NULL) + { + if (p[1] != '\0') + { + define('m', newstr(&p[1]), CurEnv); + } + while (p != NULL && strchr(&p[1], '.') != NULL) + { + *p = '\0'; + if (tTd(0, 4)) + printf("\ta.k.a.: %s\n", jbuf); + setclass('w', jbuf); + *p++ = '.'; + p = strchr(p, '.'); + } + } + + if (uname(&utsname) >= 0) + p = utsname.nodename; + else + { + if (tTd(0, 22)) + printf("uname failed (%s)\n", errstring(errno)); + makelower(jbuf); + p = jbuf; + } + if (tTd(0, 4)) + printf(" UUCP nodename: %s\n", p); + p = newstr(p); + define('k', p, CurEnv); + setclass('k', p); + setclass('w', p); + } + if (hp != NULL) + { + for (av = hp->h_aliases; av != NULL && *av != NULL; av++) + { + if (tTd(0, 4)) + printf("\ta.k.a.: %s\n", *av); + setclass('w', *av); + } +#if NETINET + if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ) + { + for (i = 0; hp->h_addr_list[i] != NULL; i++) + { + char ipbuf[103]; + + snprintf(ipbuf, sizeof ipbuf, "[%.100s]", + inet_ntoa(*((struct in_addr *) hp->h_addr_list[i]))); + if (tTd(0, 4)) + printf("\ta.k.a.: %s\n", ipbuf); + setclass('w', ipbuf); + } + } +#endif + } + + /* current time */ + define('b', arpadate((char *) NULL), CurEnv); + + QueueLimitRecipient = (QUEUE_CHAR *) NULL; + QueueLimitSender = (QUEUE_CHAR *) NULL; + QueueLimitId = (QUEUE_CHAR *) NULL; + + /* + ** Crack argv. + */ + + av = argv; + p = strrchr(*av, '/'); + if (p++ == NULL) + p = *av; + if (strcmp(p, "newaliases") == 0) + OpMode = MD_INITALIAS; + else if (strcmp(p, "mailq") == 0) + OpMode = MD_PRINT; + else if (strcmp(p, "smtpd") == 0) + OpMode = MD_DAEMON; + else if (strcmp(p, "hoststat") == 0) + OpMode = MD_HOSTSTAT; + else if (strcmp(p, "purgestat") == 0) + OpMode = MD_PURGESTAT; + + optind = 1; + while ((j = getopt(argc, argv, OPTIONS)) != -1) + { + switch (j) + { + case 'b': /* operations mode */ + switch (j = *optarg) + { + case MD_DAEMON: + case MD_FGDAEMON: +# if !DAEMON + usrerr("Daemon mode not implemented"); + ExitStat = EX_USAGE; + break; +# endif /* DAEMON */ + case MD_SMTP: +# if !SMTP + usrerr("I don't speak SMTP"); + ExitStat = EX_USAGE; + break; +# endif /* SMTP */ + + case MD_INITALIAS: + case MD_DELIVER: + case MD_VERIFY: + case MD_TEST: + case MD_PRINT: + case MD_HOSTSTAT: + case MD_PURGESTAT: + case MD_ARPAFTP: + OpMode = j; + break; + + case MD_FREEZE: + usrerr("Frozen configurations unsupported"); + ExitStat = EX_USAGE; + break; + + default: + usrerr("Invalid operation mode %c", j); + ExitStat = EX_USAGE; + break; + } + break; + + case 'B': /* body type */ + CurEnv->e_bodytype = optarg; + break; + + case 'C': /* select configuration file (already done) */ + if (RealUid != 0) + warn_C_flag = TRUE; + ConfFile = optarg; + (void) drop_privileges(TRUE); + safecf = FALSE; + break; + + case 'd': /* debugging -- already done */ + break; + + case 'f': /* from address */ + case 'r': /* obsolete -f flag */ + if (from != NULL) + { + usrerr("More than one \"from\" person"); + ExitStat = EX_USAGE; + break; + } + from = newstr(denlstring(optarg, TRUE, TRUE)); + if (strcmp(RealUserName, from) != 0) + warn_f_flag = j; + break; + + case 'F': /* set full name */ + FullName = newstr(optarg); + break; + + case 'h': /* hop count */ + CurEnv->e_hopcount = strtol(optarg, &ep, 10); + if (*ep) + { + usrerr("Bad hop count (%s)", optarg); + ExitStat = EX_USAGE; + } + break; + + case 'n': /* don't alias */ + NoAlias = TRUE; + break; + + case 'N': /* delivery status notifications */ + DefaultNotify |= QHASNOTIFY; + if (strcasecmp(optarg, "never") == 0) + break; + for (p = optarg; p != NULL; optarg = p) + { + p = strchr(p, ','); + if (p != NULL) + *p++ = '\0'; + if (strcasecmp(optarg, "success") == 0) + DefaultNotify |= QPINGONSUCCESS; + else if (strcasecmp(optarg, "failure") == 0) + DefaultNotify |= QPINGONFAILURE; + else if (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, CurEnv); + break; + + case 'O': /* set option (long form) */ + setoption(' ', optarg, FALSE, TRUE, CurEnv); + break; + + case 'p': /* set protocol */ + p = strchr(optarg, ':'); + if (p != NULL) + { + *p++ = '\0'; + if (*p != '\0') + { + ep = xalloc(strlen(p) + 1); + cleanstrcpy(ep, p, MAXNAME); + define('s', ep, CurEnv); + } + } + if (*optarg != '\0') + { + ep = xalloc(strlen(optarg) + 1); + cleanstrcpy(ep, optarg, MAXNAME); + define('r', ep, CurEnv); + } + break; + + case 'q': /* run queue files at intervals */ +# if QUEUE + FullName = NULL; + queuemode = TRUE; + switch (optarg[0]) + { + case 'I': + if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL) + syserr("!Out of memory!!"); + new->queue_match = newstr(&optarg[1]); + new->queue_next = QueueLimitId; + QueueLimitId = new; + break; + + case 'R': + if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL) + syserr("!Out of memory!!"); + new->queue_match = newstr(&optarg[1]); + new->queue_next = QueueLimitRecipient; + QueueLimitRecipient = new; + break; + + case 'S': + if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL) + syserr("!Out of memory!!"); + new->queue_match = newstr(&optarg[1]); + new->queue_next = QueueLimitSender; + QueueLimitSender = new; + break; + + default: + QueueIntvl = convtime(optarg, 'm'); + break; + } +# else /* QUEUE */ + usrerr("I don't know about queues"); + ExitStat = EX_USAGE; +# endif /* QUEUE */ + break; + + case 'R': /* DSN RET: what to return */ + if (bitset(EF_RET_PARAM, CurEnv->e_flags)) + { + usrerr("Duplicate -R flag"); + ExitStat = EX_USAGE; + break; + } + CurEnv->e_flags |= EF_RET_PARAM; + if (strcasecmp(optarg, "hdrs") == 0) + CurEnv->e_flags |= EF_NO_BODY_RETN; + else if (strcasecmp(optarg, "full") != 0) + { + usrerr("Invalid -R value"); + ExitStat = EX_USAGE; + } + break; + + case 't': /* read recipients from message */ + GrabTo = TRUE; + break; + + case 'U': /* initial (user) submission */ + UserSubmission = TRUE; + break; + + case 'V': /* DSN ENVID: set "original" envelope id */ + if (!xtextok(optarg)) + { + usrerr("Invalid syntax in -V flag"); + ExitStat = EX_USAGE; + } + else + CurEnv->e_envid = newstr(optarg); + break; + + case 'X': /* traffic log file */ + (void) drop_privileges(TRUE); + TrafficLogFile = fopen(optarg, "a"); + if (TrafficLogFile == NULL) + { + syserr("cannot open %s", optarg); + ExitStat = EX_CANTCREAT; + break; + } +#ifdef HASSETVBUF + setvbuf(TrafficLogFile, NULL, _IOLBF, 0); +#else + setlinebuf(TrafficLogFile); +#endif + 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, CurEnv); + break; + + case 'e': /* error message disposition */ + case 'M': /* define macro */ + setoption(j, optarg, FALSE, TRUE, CurEnv); + break; + + case 's': /* save From lines in headers */ + setoption('f', "T", FALSE, TRUE, CurEnv); + break; + +# ifdef DBM + case 'I': /* initialize alias DBM file */ + OpMode = MD_INITALIAS; + break; +# endif /* DBM */ + +# if defined(__osf__) || defined(_AIX3) + case 'x': /* random flag that OSF/1 & AIX mailx passes */ + break; +# endif +# if defined(sony_news) + case 'E': + case 'J': /* ignore flags for Japanese code conversion + impremented on Sony NEWS */ + break; +# endif + + default: + ExitStat = EX_USAGE; + finis(); + break; + } + } + av += optind; + + /* + ** Do basic initialization. + ** Read system control file. + ** Extract special fields for local use. + */ + + /* set up ${opMode} for use in config file */ + { + char mbuf[2]; + + mbuf[0] = OpMode; + mbuf[1] = '\0'; + define(MID_OPMODE, newstr(mbuf), CurEnv); + } + +#if XDEBUG + checkfd012("before readcf"); +#endif + vendor_pre_defaults(CurEnv); + readcf(getcfname(), safecf, CurEnv); + ConfigFileRead = TRUE; + vendor_post_defaults(CurEnv); + + /* Enforce use of local time (null string overrides this) */ + if (TimeZoneSpec == NULL) + unsetenv("TZ"); + else if (TimeZoneSpec[0] != '\0') + setuserenv("TZ", TimeZoneSpec); + else + setuserenv("TZ", NULL); + tzset(); + + /* avoid denial-of-service attacks */ + resetlimits(); + + if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) + { + /* drop privileges -- daemon mode done after socket/bind */ + (void) drop_privileges(FALSE); + } + + /* + ** Find our real host name for future logging. + */ + + p = getauthinfo(STDIN_FILENO, &forged); + define('_', p, CurEnv); + + /* suppress error printing if errors mailed back or whatever */ + if (CurEnv->e_errormode != EM_PRINT) + HoldErrs = TRUE; + + /* set up the $=m class now, after .cf has a chance to redefine $m */ + expand("\201m", jbuf, sizeof jbuf, CurEnv); + setclass('m', jbuf); + + /* probe interfaces and locate any additional names */ + if (!DontProbeInterfaces) + load_if_names(); + + if (tTd(0, 1)) + { + printf("\n============ SYSTEM IDENTITY (after readcf) ============"); + printf("\n (short domain name) $w = "); + xputs(macvalue('w', CurEnv)); + printf("\n (canonical domain name) $j = "); + xputs(macvalue('j', CurEnv)); + printf("\n (subdomain name) $m = "); + xputs(macvalue('m', CurEnv)); + printf("\n (node name) $k = "); + xputs(macvalue('k', CurEnv)); + printf("\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(CurEnv, "Processed by %s with -C %s", + RealUserName, ConfFile); + if (Warn_Q_option) + auth_warning(CurEnv, "Processed from queue %s", QueueDir); + + /* check body type for legality */ + if (CurEnv->e_bodytype == NULL) + /* nothing */ ; + else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0) + SevenBitInput = TRUE; + else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0) + SevenBitInput = FALSE; + else + { + usrerr("Illegal body type %s", CurEnv->e_bodytype); + CurEnv->e_bodytype = NULL; + } + + /* tweak default DSN notifications */ + if (DefaultNotify == 0) + DefaultNotify = QPINGONFAILURE|QPINGONDELAY; + + /* be sure we don't pick up bogus HOSTALIASES environment variable */ + if (queuemode && RealUid != 0) + (void) unsetenv("HOSTALIASES"); + + /* 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; + printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n"); + } + + /* need HostStatusDir in order to have SingleThreadDelivery */ + if (SingleThreadDelivery && HostStatDir == NULL) + { + SingleThreadDelivery = FALSE; + printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n"); + } + + /* check for permissions */ + if ((OpMode == MD_DAEMON || OpMode == MD_FGDAEMON || + OpMode == MD_PURGESTAT) && RealUid != 0) + { + if (LogLevel > 1) + sm_syslog(LOG_ALERT, NOQID, + "user %d attempted to %s", + RealUid, + OpMode != MD_PURGESTAT ? "run daemon" + : "purge host status"); + usrerr("Permission denied"); + exit(EX_USAGE); + } + + 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; + CurEnv->e_errormode = EM_PRINT; + HoldErrs = FALSE; + break; + + case MD_VERIFY: + CurEnv->e_errormode = EM_PRINT; + HoldErrs = FALSE; + /* arrange to exit cleanly on hangup signal */ + if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) + setsignal(SIGHUP, intsig); + break; + + case MD_FGDAEMON: + run_in_foreground = TRUE; + OpMode = MD_DAEMON; + /* fall through ... */ + + case MD_DAEMON: + vendor_daemon_setup(CurEnv); + + /* 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"); + setsignal(SIGHUP, sighup); + + /* workaround: can't seem to release the signal in the parent */ + releasesignal(SIGHUP); + break; + + case MD_INITALIAS: + Verbose = 2; + CurEnv->e_errormode = EM_PRINT; + HoldErrs = FALSE; + /* fall through... */ + + case MD_PRINT: + /* to handle sendmail -bp -qSfoobar properly */ + queuemode = FALSE; + /* fall through... */ + + default: + /* arrange to exit cleanly on hangup signal */ + if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) + setsignal(SIGHUP, intsig); + break; + } + + /* special considerations for FullName */ + if (FullName != NULL) + { + char *full = NULL; + extern bool rfc822_string __P((char *)); + + /* full names can't have newlines */ + if (strchr(FullName, '\n') != NULL) + { + FullName = full = newstr(denlstring(FullName, TRUE, TRUE)); + } + /* check for characters that may have to be quoted */ + if (!rfc822_string(FullName)) + { + extern char *addquotes __P((char *)); + + /* + ** Quote a full name with special characters + ** as a comment so crackaddr() doesn't destroy + ** the name portion of the address. + */ + FullName = addquotes(FullName); + if (full != NULL) + free(full); + } + } + + /* do heuristic mode adjustment */ + if (Verbose) + { + /* turn off noconnect option */ + setoption('c', "F", TRUE, FALSE, CurEnv); + + /* turn on interactive delivery */ + setoption('d', "", TRUE, FALSE, CurEnv); + } + + /* 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', CurEnv)) != NULL) + SmtpGreeting = newstr(p); + else + SmtpGreeting = "\201j Sendmail \201v ready at \201b"; + } + if (UnixFromLine == NULL) + { + if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL) + UnixFromLine = newstr(p); + else + UnixFromLine = "From \201g \201d"; + } + + /* our name for SMTP codes */ + expand("\201j", jbuf, sizeof jbuf, CurEnv); + MyHostName = jbuf; + if (strchr(jbuf, '.') == NULL) + message("WARNING: local host name (%s) is not qualified; fix $j in config file", + jbuf); + + /* make certain that this name is part of the $=w class */ + setclass('w', MyHostName); + + /* 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 + + /* operate in queue directory */ + if (QueueDir == NULL) + { + if (OpMode != MD_TEST) + { + syserr("QueueDirectory (Q) option must be set"); + ExitStat = EX_CONFIG; + } + } + else + { + /* test path to get warning messages */ + (void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE); + if (OpMode != MD_TEST && chdir(QueueDir) < 0) + { + syserr("cannot chdir(%s)", QueueDir); + ExitStat = EX_CONFIG; + } + } + + /* check host status directory for validity */ + if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE)) + { + /* cannot use this value */ + if (tTd(0, 2)) + printf("Cannot use HostStatusDirectory = %s: %s\n", + HostStatDir, errstring(errno)); + HostStatDir = NULL; + } + +# if QUEUE + if (queuemode && 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 */ + usrerr("You do not have permission to process the queue"); + exit (EX_NOPERM); + } + } +# endif /* QUEUE */ + + /* if we've had errors so far, exit now */ + if (ExitStat != EX_OK && OpMode != MD_TEST) + { + endpwent(); + setuid(RealUid); + exit(ExitStat); + } + +#if XDEBUG + checkfd012("before main() initmaps"); +#endif + + /* + ** Do operation-mode-dependent initialization. + */ + + switch (OpMode) + { + case MD_PRINT: + /* print the queue */ +#if QUEUE + dropenvelope(CurEnv, TRUE); + printqueue(); + endpwent(); + setuid(RealUid); + exit(EX_OK); +#else /* QUEUE */ + usrerr("No queue to print"); + finis(); +#endif /* QUEUE */ + + case MD_HOSTSTAT: + mci_traverse_persistent(mci_print_persistent, NULL); + exit(EX_OK); + break; + + case MD_PURGESTAT: + mci_traverse_persistent(mci_purge_persistent, NULL); + exit(EX_OK); + break; + + case MD_INITALIAS: + /* initialize alias database */ + initmaps(TRUE, CurEnv); + endpwent(); + setuid(RealUid); + exit(ExitStat); + + case MD_SMTP: + case MD_DAEMON: + /* reset DSN parameters */ + DefaultNotify = QPINGONFAILURE|QPINGONDELAY; + CurEnv->e_envid = NULL; + CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); + + /* don't open alias database -- done in srvrsmtp */ + break; + + default: + /* open the alias database */ + initmaps(FALSE, CurEnv); + break; + } + + if (tTd(0, 15)) + { + extern void printrules __P((void)); + + /* print configuration table (or at least part of it) */ + if (tTd(0, 90)) + printrules(); + for (i = 0; i < MAXMAILERS; i++) + { + if (Mailer[i] != NULL) + printmailer(Mailer[i]); + } + } + + /* + ** Switch to the main envelope. + */ + + CurEnv = newenvelope(&MainEnvelope, CurEnv); + MainEnvelope.e_flags = BlankEnvelope.e_flags; + + /* + ** If test mode, read addresses from stdin and process. + */ + + if (OpMode == MD_TEST) + { + char buf[MAXLINE]; + SIGFUNC_DECL intindebug __P((int)); + + if (isatty(fileno(stdin))) + Verbose = 2; + + if (Verbose) + { + printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); + printf("Enter
\n"); + } + if (setjmp(TopFrame) > 0) + printf("\n"); + (void) setsignal(SIGINT, intindebug); + for (;;) + { + extern void testmodeline __P((char *, ENVELOPE *)); + + if (Verbose == 2) + printf("> "); + (void) fflush(stdout); + if (fgets(buf, sizeof buf, stdin) == NULL) + finis(); + p = strchr(buf, '\n'); + if (p != NULL) + *p = '\0'; + if (Verbose < 2) + printf("> %s\n", buf); + testmodeline(buf, CurEnv); + } + } + +# if QUEUE + /* + ** If collecting stuff from the queue, go start doing that. + */ + + if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0) + { + (void) runqueue(FALSE, Verbose); + finis(); + } +# endif /* QUEUE */ + + /* + ** 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]; + extern void getrequests __P((ENVELOPE *)); + + if (!run_in_foreground && !tTd(99, 100)) + { + /* put us in background */ + i = fork(); + if (i < 0) + syserr("daemon: cannot fork"); + if (i != 0) + exit(EX_OK); + + /* disconnect from our controlling tty */ + disconnect(2, CurEnv); + } + + dtype[0] = '\0'; + if (OpMode == MD_DAEMON) + strcat(dtype, "+SMTP"); + if (QueueIntvl != 0) + { + strcat(dtype, "+queueing@"); + strcat(dtype, pintvl(QueueIntvl, TRUE)); + } + if (tTd(0, 1)) + strcat(dtype, "+debugging"); + + sm_syslog(LOG_INFO, NOQID, + "starting daemon (%s): %s", Version, dtype + 1); +#ifdef XLA + xla_create_file(); +#endif + +# if QUEUE + if (queuemode) + { + (void) runqueue(TRUE, FALSE); + if (OpMode != MD_DAEMON) + { + for (;;) + { + pause(); + if (DoQueueRun) + (void) runqueue(TRUE, FALSE); + } + } + } +# endif /* QUEUE */ + dropenvelope(CurEnv, TRUE); + +#if DAEMON + getrequests(CurEnv); + + /* drop privileges */ + (void) drop_privileges(FALSE); + + /* at this point we are in a child: reset state */ + (void) newenvelope(CurEnv, CurEnv); + + /* + ** Get authentication data + */ + + p = getauthinfo(fileno(InChannel), &forged); + define('_', p, &BlankEnvelope); +#endif /* DAEMON */ + } + +# if SMTP + /* + ** If running SMTP protocol, start collecting and executing + ** commands. This will never return. + */ + + if (OpMode == MD_SMTP || OpMode == MD_DAEMON) + { + char pbuf[20]; + extern void smtp __P((char *, ENVELOPE *)); + + /* + ** Save some macros for check_* rulesets. + */ + + if (forged) + { + char ipbuf[103]; + + snprintf(ipbuf, sizeof ipbuf, "[%.100s]", + inet_ntoa(RealHostAddr.sin.sin_addr)); + + define(macid("{client_name}", NULL), + newstr(ipbuf), &BlankEnvelope); + } + else + define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope); + define(macid("{client_addr}", NULL), + newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope); + if (RealHostAddr.sa.sa_family == AF_INET) + snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port); + else + snprintf(pbuf, sizeof pbuf, "0"); + define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope); + + if (OpMode == MD_DAEMON) + { + /* validate the connection */ + HoldErrs = TRUE; + nullserver = validate_connection(&RealHostAddr, + RealHostName, CurEnv); + HoldErrs = FALSE; + } + smtp(nullserver, CurEnv); + } +# endif /* SMTP */ + + clearenvelope(CurEnv, FALSE); + if (OpMode == MD_VERIFY) + { + CurEnv->e_sendmode = SM_VERIFY; + PostMasterCopy = NULL; + } + else + { + /* interactive -- all errors are global */ + CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER; + } + + /* + ** Do basic system initialization and set the sender + */ + + initsys(CurEnv); + if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't')) + auth_warning(CurEnv, "%s set sender to %s using -%c", + RealUserName, from, warn_f_flag); + setsender(from, CurEnv, NULL, '\0', FALSE); + if (macvalue('s', CurEnv) == NULL) + define('s', RealHostName, CurEnv); + + if (*av == NULL && !GrabTo) + { + CurEnv->e_flags |= EF_GLOBALERRS; + usrerr("Recipient names must be specified"); + + /* collect body for UUCP return */ + if (OpMode != MD_VERIFY) + collect(InChannel, FALSE, NULL, CurEnv); + finis(); + } + + /* + ** Scan argv and deliver the message to everyone. + */ + + sendtoargv(av, CurEnv); + + /* 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 = CurEnv->e_sendqueue; q != NULL; q = q->q_next) + q->q_flags |= QDONTSEND; + } +#endif + + /* + ** Read the input mail. + */ + + CurEnv->e_to = NULL; + if (OpMode != MD_VERIFY || GrabTo) + { + long savedflags = CurEnv->e_flags & EF_FATALERRS; + + CurEnv->e_flags |= EF_GLOBALERRS; + CurEnv->e_flags &= ~EF_FATALERRS; + collect(InChannel, FALSE, NULL, CurEnv); + + /* bail out if message too large */ + if (bitset(EF_CLRQUEUE, CurEnv->e_flags)) + { + finis(); + /*NOTREACHED*/ + return -1; + } + CurEnv->e_flags |= savedflags; + } + errno = 0; + + if (tTd(1, 1)) + printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr); + + /* + ** Actually send everything. + ** If verifying, just ack. + */ + + CurEnv->e_from.q_flags |= QDONTSEND; + if (tTd(1, 5)) + { + printf("main: QDONTSEND "); + printaddr(&CurEnv->e_from, FALSE); + } + CurEnv->e_to = NULL; + CurrentLA = getla(); + GrabTo = FALSE; + sendall(CurEnv, SM_DEFAULT); + + /* + ** All done. + ** Don't send return error message if in VERIFY mode. + */ + + finis(); + /*NOTREACHED*/ + return -1; +} + + +/* ARGSUSED */ +SIGFUNC_DECL +intindebug(sig) + int sig; +{ + longjmp(TopFrame, 1); + return SIGFUNC_RETURN; +} + + + /* +** FINIS -- Clean up and exit. +** +** Parameters: +** none +** +** Returns: +** never +** +** Side Effects: +** exits sendmail +*/ + +void +finis() +{ + if (tTd(2, 1)) + { + extern void printenvflags __P((ENVELOPE *)); + + printf("\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); + + /* if we fail in finis(), just exit */ + if (setjmp(TopFrame) != 0) + { + /* failed -- just give it up */ + goto forceexit; + } + + /* clean up temp files */ + CurEnv->e_to = NULL; + if (CurEnv->e_id != NULL) + dropenvelope(CurEnv, TRUE); + + /* flush any cached connections */ + mci_flush(TRUE, NULL); + +# ifdef XLA + /* clean up extended load average stuff */ + xla_all_end(); +# endif + + /* and exit */ + forceexit: + if (LogLevel > 78) + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "finis, pid=%d", + getpid()); + if (ExitStat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET) + ExitStat = EX_OK; + + /* reset uid for process accounting */ + endpwent(); + setuid(RealUid); + + exit(ExitStat); +} + /* +** INTSIG -- clean up on interrupt +** +** This just arranges to exit. It pessimises in that it +** may resend a message. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** Unlocks the current job. +*/ + +/* ARGSUSED */ +SIGFUNC_DECL +intsig(sig) + int sig; +{ + if (LogLevel > 79) + sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt"); + FileName = NULL; + unlockqueue(CurEnv); +#ifdef XLA + xla_all_end(); +#endif + + /* reset uid for process accounting */ + endpwent(); + setuid(RealUid); + + exit(EX_OK); +} + /* +** INITMACROS -- initialize the macro system +** +** This just involves defining some macros that are actually +** used internally as metasymbols to be themselves. +** +** Parameters: +** none. +** +** Returns: +** none. +** +** Side Effects: +** initializes several macros to be themselves. +*/ + +struct metamac MetaMacros[] = +{ + /* LHS pattern matching characters */ + { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE }, + { '=', MATCHCLASS }, { '~', MATCHNCLASS }, + + /* these are RHS metasymbols */ + { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER }, + { '>', CALLSUBR }, + + /* the conditional operations */ + { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI }, + + /* the hostname lookup characters */ + { '[', HOSTBEGIN }, { ']', HOSTEND }, + { '(', LOOKUPBEGIN }, { ')', LOOKUPEND }, + + /* miscellaneous control characters */ + { '&', MACRODEXPAND }, + + { '\0' } +}; + +#define MACBINDING(name, mid) \ + stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \ + MacroName[mid] = name; + +void +initmacros(e) + register ENVELOPE *e; +{ + register struct metamac *m; + register int c; + char buf[5]; + extern char *MacroName[256]; + + for (m = MetaMacros; m->metaname != '\0'; m++) + { + buf[0] = m->metaval; + buf[1] = '\0'; + define(m->metaname, newstr(buf), e); + } + buf[0] = MATCHREPL; + buf[2] = '\0'; + for (c = '0'; c <= '9'; c++) + { + buf[1] = c; + define(c, newstr(buf), e); + } + + /* set defaults for some macros sendmail will use later */ + define('n', "MAILER-DAEMON", e); + + /* set up external names for some internal macros */ + MACBINDING("opMode", MID_OPMODE); + /*XXX should probably add equivalents for all short macros here XXX*/ +} + /* +** 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 transcript. +** 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)) + printf("disconnect: In %d Out %d, e=%lx\n", + fileno(InChannel), fileno(OutChannel), (u_long) e); + if (tTd(52, 100)) + { + printf("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) setsignal(SIGINT, SIG_IGN); + (void) setsignal(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 != stdin) + { + (void) fclose(InChannel); + InChannel = stdin; + } + if (freopen("/dev/null", "r", stdin) == NULL) + sm_syslog(LOG_ERR, e->e_id, + "disconnect: freopen(\"/dev/null\") failed: %s", + errstring(errno)); + + /* output to the transcript */ + if (OutChannel != stdout) + { + (void) fclose(OutChannel); + OutChannel = stdout; + } + if (droplev > 0) + { + if (e->e_xfp == NULL) + { + fd = open("/dev/null", O_WRONLY, 0666); + if (fd == -1) + sm_syslog(LOG_ERR, e->e_id, + "disconnect: open(\"/dev/null\") failed: %s", + errstring(errno)); + } + else + { + fd = fileno(e->e_xfp); + if (fd == -1) + sm_syslog(LOG_ERR, e->e_id, + "disconnect: fileno(e->e_xfp) failed: %s", + errstring(errno)); + } + (void) fflush(stdout); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (e->e_xfp == NULL) + close(fd); + } + + /* drop our controlling TTY completely if possible */ + if (droplev > 1) + { + (void) setsid(); + errno = 0; + } + +#if XDEBUG + checkfd012("disconnect"); +#endif + + if (LogLevel > 71) + sm_syslog(LOG_DEBUG, e->e_id, + "in background, pid=%d", + getpid()); + + 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; + + /* 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 + 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); + argv[0][0] = '-'; + argv[0][1] = 'C'; + (void)strcpy(&argv[0][2], __DEFPATH); + } + + /* If -q doesn't have an argument, run it once. */ + if (ap[1] == 'q' && ap[2] == '\0') + *argv = "-q0"; + + /* 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 + } +} + /* +** 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 +auth_warning(e, msg, va_alist) + register ENVELOPE *e; + const char *msg; + va_dcl +#endif +{ + char buf[MAXLINE]; + VA_LOCAL_DECL + + if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) + { + register char *p; + static char hostbuf[48]; + extern struct hostent *myhostname __P((char *, int)); + + if (hostbuf[0] == '\0') + (void) myhostname(hostbuf, sizeof hostbuf); + + (void) snprintf(buf, sizeof buf, "%s: ", hostbuf); + p = &buf[strlen(buf)]; + VA_START(msg); + vsnprintf(p, SPACELEFT(buf, p), msg, ap); + VA_END; + addheader("X-Authentication-Warning", buf, &e->e_header); + 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. +*/ + +char * +getextenv(envar) + const char *envar; +{ + char **envp; + int l; + + l = strlen(envar); + for (envp = ExternalEnviron; *envp != NULL; envp++) + { + if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=') + return &(*envp)[l + 1]; + } + return NULL; +} + /* +** SETUSERENV -- set an environment in the propogated 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 setuserenv is ignored. +** +** Returns: +** none. +*/ + +void +setuserenv(envar, value) + const char *envar; + const char *value; +{ + int i; + char **evp = UserEnviron; + char *p; + + if (value == NULL) + { + value = getextenv(envar); + if (value == NULL) + return; + } + + i = strlen(envar); + p = (char *) xalloc(strlen(value) + i + 2); + strcpy(p, envar); + p[i++] = '='; + strcpy(&p[i], 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("setuserenv: putenv(%s) failed", p); +} + /* +** DUMPSTATE -- dump state +** +** For debugging. +*/ + +void +dumpstate(when) + char *when; +{ + register char *j = macvalue('j', CurEnv); + int rs; + + 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, "--- open file descriptors: ---"); + printopenfds(TRUE); + sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---"); + mci_dump_all(TRUE); + rs = strtorwset("debug_dumpstate", NULL, ST_FIND); + if (rs > 0) + { + int stat; + register char **pvp; + char *pv[MAXATOM + 1]; + + pv[0] = NULL; + stat = rewrite(pv, rs, 0, CurEnv); + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "--- ruleset debug_dumpstate returns stat %d, pv: ---", + stat); + 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 ---"); +} + + +/* ARGSUSED */ +SIGFUNC_DECL +sigusr1(sig) + int sig; +{ + dumpstate("user signal"); + return SIGFUNC_RETURN; +} + + +/* ARGSUSED */ +SIGFUNC_DECL +sighup(sig) + int sig; +{ + if (SaveArgv[0][0] != '/') + { + if (LogLevel > 3) + sm_syslog(LOG_INFO, NOQID, "could not restart: need full path"); + exit(EX_OSFILE); + } + if (LogLevel > 3) + sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]); + alarm(0); + releasesignal(SIGHUP); + if (drop_privileges(TRUE) != EX_OK) + { + if (LogLevel > 0) + sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m", + RunAsUid, RunAsGid); + exit(EX_OSERR); + } + execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron); + if (LogLevel > 0) + sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]); + exit(EX_OSFILE); +} + /* +** 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)) + printf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n", + (int)to_real_uid, (int)RealUid, (int)RealGid, (int)RunAsUid, (int)RunAsGid); + + if (to_real_uid) + { + RunAsUserName = RealUserName; + RunAsUid = RealUid; + RunAsGid = RealGid; + } + + /* make sure no one can grab open descriptors for secret files */ + endpwent(); + + /* reset group permissions; these can be set later */ + emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid(); + if (setgroups(1, emptygidset) == -1 && geteuid() == 0) + rval = EX_OSERR; + + /* reset primary group and user id */ + if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0) + rval = EX_OSERR; + if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0) + rval = EX_OSERR; + if (tTd(47, 5)) + { + printf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n", + (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid()); + printf("drop_privileges: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid); + } + 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 +*/ + +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("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666); + if (i < 0) + { + syserr("!fill_fd: %s: cannot open /dev/null", + where == NULL ? "startup" : where); + } + if (fd != i) + { + (void) dup2(i, fd); + (void) close(i); + } +} + /* +** 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 +*/ + +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; + static int tryflags = RF_COPYNONE; + char exbuf[MAXLINE]; + extern bool invalidaddr __P((char *, char *)); + extern char *crackaddr __P((char *)); + extern void dump_class __P((STAB *, int)); + extern void translate_dollars __P((char *)); + extern void help __P((char *)); + + switch (line[0]) + { + case '#': + case 0: + return; + + case '?': + help("-bt"); + return; + + case '.': /* config-style settings */ + switch (line[1]) + { + case 'D': + mid = macid(&line[2], &delimptr); + if (mid == '\0') + return; + translate_dollars(delimptr); + define(mid, newstr(delimptr), e); + break; + + case 'C': + if (line[2] == '\0') /* not to call syserr() */ + return; + + mid = macid(&line[2], &delimptr); + if (mid == '\0') + return; + translate_dollars(delimptr); + expand(delimptr, exbuf, sizeof exbuf, e); + 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': + printf("Usage: .[DC]macro value(s)\n"); + break; + + default: + printf("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) + { + printf("Undefined ruleset %s\n", &line[2]); + return; + } + rw = RewriteRules[rs]; + if (rw == NULL) + return; + do + { + putchar('R'); + s = rw->r_lhs; + while (*s != NULL) + { + xputs(*s++); + putchar(' '); + } + putchar('\t'); + putchar('\t'); + s = rw->r_rhs; + while (*s != NULL) + { + xputs(*s++); + putchar(' '); + } + putchar('\n'); + } while ((rw = rw->r_next) != NULL); + break; + + case 'M': + for (i = 0; i < MAXMAILERS; i++) + { + if (Mailer[i] != NULL) + printmailer(Mailer[i]); + } + break; + + case '\0': + printf("Usage: =Sruleset or =M\n"); + break; + + default: + printf("Unknown \"=\" command %s\n", line); + break; + } + return; + + case '-': /* set command-line-like opts */ + switch (line[1]) + { + case 'd': + tTflag(&line[2]); + break; + + case '\0': + printf("Usage: -d{debug arguments}\n"); + break; + + default: + printf("Unknown \"-\" command %s\n", line); + break; + } + return; + + case '$': + if (line[1] == '=') + { + mid = macid(&line[2], NULL); + if (mid != '\0') + stabapply(dump_class, mid); + return; + } + mid = macid(&line[1], NULL); + if (mid == '\0') + return; + p = macvalue(mid, e); + if (p == NULL) + printf("Undefined\n"); + else + { + xputs(p); + printf("\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') + { + printf("Usage: /[canon|map|mx|parse|try|tryflags]\n"); + return; + } + if (strcasecmp(&line[1], "mx") == 0) + { +#if NAMED_BIND + /* look up MX records */ + int nmx; + auto int rcode; + char *mxhosts[MAXMXHOSTS + 1]; + + if (*p == '\0') + { + printf("Usage: /mx address\n"); + return; + } + nmx = getmxrr(p, mxhosts, FALSE, &rcode); + printf("getmxrr(%s) returns %d value(s):\n", p, nmx); + for (i = 0; i < nmx; i++) + printf("\t%s\n", mxhosts[i]); +#else + printf("No MX code compiled in\n"); +#endif + } + else if (strcasecmp(&line[1], "canon") == 0) + { + char host[MAXHOSTNAMELEN]; + + if (*p == '\0') + { + printf("Usage: /canon address\n"); + return; + } + else if (strlen(p) >= sizeof host) + { + printf("Name too long\n"); + return; + } + strcpy(host, p); + (void) getcanonname(host, sizeof(host), HasWildcardMX); + printf("getcanonname(%s) returns %s\n", p, host); + } + else if (strcasecmp(&line[1], "map") == 0) + { + auto int rcode = EX_OK; + char *av[2]; + + if (*p == '\0') + { + printf("Usage: /map mapname key\n"); + return; + } + for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++) + continue; + if (*q == '\0') + { + printf("No key specified\n"); + return; + } + *q++ = '\0'; + map = stab(p, ST_MAP, ST_FIND); + if (map == NULL) + { + printf("Map named \"%s\" not found\n", p); + return; + } + if (!bitset(MF_OPEN, map->s_map.map_mflags)) + { + printf("Map named \"%s\" not open\n", p); + return; + } + printf("map_lookup: %s (%s) ", p, q); + av[0] = q; + av[1] = NULL; + p = (*map->s_map.map_class->map_lookup) + (&map->s_map, q, av, &rcode); + if (p == NULL) + printf("no match (%d)\n", rcode); + else + printf("returns %s (%d)\n", p, rcode); + } + else if (strcasecmp(&line[1], "try") == 0) + { + MAILER *m; + STAB *s; + auto int rcode = EX_OK; + + q = strpbrk(p, " \t"); + if (q != NULL) + { + while (isascii(*q) && isspace(*q)) + *q++ = '\0'; + } + if (q == NULL || *q == '\0') + { + printf("Usage: /try mailer address\n"); + return; + } + s = stab(p, ST_MAILER, ST_FIND); + if (s == NULL) + { + printf("Unknown mailer %s\n", p); + return; + } + m = s->s_mailer; + printf("Trying %s %s address %s for mailer %s\n", + bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", + bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient", + q, p); + p = remotename(q, m, tryflags, &rcode, CurEnv); + printf("Rcode = %d, addr = %s\n", + rcode, p == NULL ? "" : p); + e->e_to = NULL; + } + else if (strcasecmp(&line[1], "tryflags") == 0) + { + if (*p == '\0') + { + printf("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; + } + } + } + else if (strcasecmp(&line[1], "parse") == 0) + { + if (*p == '\0') + { + printf("Usage: /parse address\n"); + return; + } + q = crackaddr(p); + printf("Cracked address = "); + xputs(q); + printf("\nParsing %s %s address\n", + bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", + bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient"); + if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL) + printf("Cannot parse\n"); + else if (a.q_host != NULL && a.q_host[0] != '\0') + printf("mailer %s, host %s, user %s\n", + a.q_mailer->m_name, a.q_host, a.q_user); + else + printf("mailer %s, user %s\n", + a.q_mailer->m_name, a.q_user); + e->e_to = NULL; + } + else + { + printf("Unknown \"/\" command %s\n", line); + } + return; + } + + for (p = line; isascii(*p) && isspace(*p); p++) + continue; + q = p; + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p == '\0') + { + printf("No address!\n"); + return; + } + *p = '\0'; + if (invalidaddr(p + 1, NULL)) + return; + do + { + register char **pvp; + char pvpbuf[PSBUFSIZE]; + + pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf, + &delimptr, NULL); + if (pvp == NULL) + continue; + p = q; + while (*p != '\0') + { + int stat; + + rs = strtorwset(p, NULL, ST_FIND); + if (rs < 0) + { + printf("Undefined ruleset %s\n", p); + break; + } + stat = rewrite(pvp, rs, 0, e); + if (stat != EX_OK) + printf("== Ruleset %s (%d) status %d\n", + p, rs, stat); + while (*p != '\0' && *p++ != ',') + continue; + } + } while (*(p = delimptr) != '\0'); +} + + +void +dump_class(s, id) + register STAB *s; + int id; +{ + if (s->s_type != ST_CLASS) + return; + if (bitnset(id & 0xff, s->s_class)) + printf("%s\n", s->s_name); +} diff --git a/contrib/sendmail/src/makesendmail b/contrib/sendmail/src/makesendmail new file mode 100755 index 0000000..ab8a49d --- /dev/null +++ b/contrib/sendmail/src/makesendmail @@ -0,0 +1,513 @@ +#!/bin/sh + +# Copyright (c) 1998 Sendmail, Inc. All rights reserved. +# Copyright (c) 1993, 1996-1997 Eric P. Allman. All rights reserved. +# Copyright (c) 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. +# +# +# @(#)Build 8.93 (Berkeley) 6/24/98 +# + +# +# A quick-and-dirty script to compile sendmail and related programs +# in the presence of multiple architectures. To use, just use +# "sh Build". +# + +trap "rm -f $obj/.settings$$; exit" 1 2 3 15 + +cflag="" +mflag="" +sflag="" +makeargs="" +libdirs="" +incdirs="" +libsrch="" +siteconfig="" +EX_USAGE=64 +EX_NOINPUT=66 +EX_UNAVAILABLE=69 + +while [ ! -z "$1" ] +do + case $1 + in + -c) # clean out existing $obj tree + cflag=1 + shift + ;; + + -m) # show Makefile name only + mflag=1 + shift + ;; + + -E*) # environment variables to pass into Build + arg=`echo $1 | sed 's/^-E//'` + if [ -z "$arg" ] + then + shift # move to argument + arg=$1 + fi + if [ -z "$arg" ] + then + echo "Empty -E flag" >&2 + exit $EX_USAGE + else + case $arg + in + *=*) # check format + eval $arg + export `echo $arg | sed 's;=.*;;'` + ;; + *) # bad format + echo "Bad format for -E argument ($arg)" >&2 + exit $EX_USAGE + ;; + esac + shift + fi + ;; + + -L*) # set up LIBDIRS + libdirs="$libdirs $1" + shift + ;; + + -I*) # set up INCDIRS + incdirs="$incdirs $1" + shift + ;; + + -f*) # select site config file + arg=`echo $1 | sed 's/^-f//'` + if [ -z "$arg" ] + then + shift # move to argument + arg=$1 + fi + if [ "$siteconfig" ] + then + echo "Only one -f flag allowed" >&2 + exit $EX_USAGE + else + siteconfig=$arg + if [ -z "$siteconfig" ] + then + echo "Missing argument for -f flag" >&2 + exit $EX_USAGE + elif [ ! -f "$siteconfig" ] + then + echo "${siteconfig}: File not found" + exit $EX_NOINPUT + else + shift # move past argument + fi + fi + ;; + + -S) # skip auto-configure + sflag="-s" + shift + ;; + + *) # pass argument to make + makeargs="$makeargs \"$1\"" + shift + ;; + esac +done + +# +# Do heuristic guesses !ONLY! for machines that do not have uname +# +if [ -d /NextApps -a ! -f /bin/uname -a ! -f /usr/bin/uname ] +then + # probably a NeXT box + arch=`hostinfo | sed -n 's/.*Processor type: \([^ ]*\).*/\1/p'` + os=NeXT + rel=`hostinfo | sed -n 's/.*NeXT Mach \([0-9\.]*\).*/\1/p'` +elif [ -f /usr/sony/bin/machine -a -f /etc/osversion ] +then + # probably a Sony NEWS 4.x + os=NEWS-OS + rel=`awk '{ print $3}' /etc/osversion` + arch=`/usr/sony/bin/machine` +elif [ -d /usr/omron -a -f /bin/luna ] +then + # probably a Omron LUNA + os=LUNA + if [ -f /bin/luna1 ] && /bin/luna1 + then + rel=unios-b + arch=luna1 + elif [ -f /bin/luna2 ] && /bin/luna2 + then + rel=Mach + arch=luna2 + elif [ -f /bin/luna88k ] && /bin/luna88k + then + rel=Mach + arch=luna88k + fi +elif [ -d /usr/apollo -a -d \`node_data ] +then + # probably a Apollo/DOMAIN + os=DomainOS + arch=$ISP + rel=`/usr/apollo/bin/bldt | grep Domain | awk '{ print $4 }' | sed -e 's/,//g'` +fi + +if [ ! "$arch" -a ! "$os" -a ! "$rel" ] +then + arch=`uname -m | sed -e 's/ //g'` + os=`uname -s | sed -e 's/\//-/g' -e 's/ //g'` + rel=`uname -r | sed -e 's/(/-/g' -e 's/)//g'` +fi + +# +# Tweak the values we have already got. PLEASE LIMIT THESE to +# tweaks that are absolutely necessary because your system uname +# routine doesn't return something sufficiently unique. Don't do +# it just because you don't like the name that is returned. You +# can combine the architecture name with the os name to create a +# unique Makefile name. +# + +# tweak machine architecture +case $arch +in + sun4*) arch=sun4;; + + 9000/*) arch=`echo $arch | sed -e 's/9000.//' -e 's/..$/xx/'`;; + + DS/907000) arch=ds90;; + + NILE*) arch=NILE + os=`uname -v`;; +esac + +# tweak operating system type and release +node=`uname -n | sed -e 's/\//-/g' -e 's/ //g'` +if [ "$os" = "$node" -a "$arch" = "i386" -a "$rel" = 3.2 -a "`uname -v`" = 2 ] +then + # old versions of SCO UNIX set uname -s the same as uname -n + os=SCO_SV +fi +if [ "$rel" = 4.0 ] +then + case $arch in + 3[34]??|3[34]??,*) + if [ -d /usr/sadm/sysadm/add-ons/WIN-TCP ] + then + os=NCR.MP-RAS.2.x + elif [ -d /usr/sadm/sysadm/add-ons/inet ] + then + os=NCR.MP-RAS.3.x + fi + ;; + esac +fi + +case $os +in + DYNIX-ptx) os=PTX;; + Paragon*) os=Paragon;; + HP-UX) rel=`echo $rel | sed -e 's/^[^.]*\.0*//'`;; + AIX) rela=$rel + rel=`uname -v` + case $rel in + 2) arch="" + ;; + 4) if [ "$rela" = "3" ] + then + arch=$rela + fi + ;; + esac + rel=$rel.$rela + ;; + BSD-386) os=BSD-OS;; + SCO_SV) os=SCO; rel=`uname -X | sed -n 's/Release = 3.2v//p'`;; + UNIX_System_V) if [ "$arch" = "ds90" ] + then + os="UXPDS" + rel=`uname -v | sed -e 's/\(V.*\)L.*/\1/'` + fi;; + SINIX-?) os=SINIX;; + DomainOS) case $rel in + 10.4*) rel=10.4;; + esac + ;; +esac + +# get "base part" of operating system release +rroot=`echo $rel | sed -e 's/\.[^.]*$//'` +rbase=`echo $rel | sed -e 's/\..*//'` +if [ "$rroot" = "$rbase" ] +then + rroot=$rel +fi + +# heuristic tweaks to clean up names -- PLEASE LIMIT THESE! +if [ "$os" = "unix" ] +then + # might be Altos System V + case $rel + in + 5.3*) os=Altos;; + esac +elif [ -r /unix -a -r /usr/lib/libseq.a -a -r /lib/cpp ] +then + # might be a DYNIX/ptx 2.x system, which has a broken uname + if strings /lib/cpp | grep _SEQUENT_ > /dev/null + then + os=PTX + fi +elif [ -d /usr/nec ] +then + # NEC machine -- what is it running? + if [ "$os" = "UNIX_System_V" ] + then + os=EWS-UX_V + elif [ "$os" = "UNIX_SV" ] + then + os=UX4800 + fi +elif [ "$arch" = "mips" ] +then + case $rel + in + 4_*) + if [ `uname -v` = "UMIPS" ] + then + os=RISCos + fi;; + esac +fi + +# see if there is a "user suffix" specified +if [ "${SENDMAIL_SUFFIX-}x" = "x" ] +then + sfx="" +else + sfx=".${SENDMAIL_SUFFIX}" +fi + +echo "Configuration: os=$os, rel=$rel, rbase=$rbase, rroot=$rroot, arch=$arch, sfx=$sfx" + + +SMROOT=${SMROOT-..} +BUILDTOOLS=${BUILDTOOLS-$SMROOT/BuildTools} +export SMROOT BUILDTOOLS + +# see if we are in a Build-able directory +if [ ! -f Makefile.m4 ]; then + echo "Makefile.m4 not found. Build can only be run from a source directory." + exit $EX_UNAVAILABLE +fi + +# now try to find a reasonable object directory +if [ -r obj.$os.$rel.$arch$sfx ]; then + obj=obj.$os.$rel.$arch$sfx +elif [ -r obj.$os.$rroot.$arch$sfx ]; then + obj=obj.$os.$rroot.$arch$sfx +elif [ -r obj.$os.$rbase.x.$arch$sfx ]; then + obj=obj.$os.$rbase.x.$arch$sfx +elif [ -r obj.$os.$rel$sfx ]; then + obj=obj.$os.$rel$sfx +elif [ -r obj.$os.$rbase.x$sfx ]; then + obj=obj.$os.$rbase.x$sfx +elif [ -r obj.$os.$arch$sfx ]; then + obj=obj.$os.$arch$sfx +elif [ -r obj.$rel.$arch$sfx ]; then + obj=obj.$rel.$arch$sfx +elif [ -r obj.$rbase.x.$arch$sfx ]; then + obj=obj.$rbase.x.$arch$sfx +elif [ -r obj.$os$sfx ]; then + obj=obj.$os$sfx +elif [ -r obj.$arch$sfx ]; then + obj=obj.$arch$sfx +elif [ -r obj.$rel$sfx ]; then + obj=obj.$rel$sfx +elif [ -r obj$sfx ]; then + obj=obj$sfx +fi +if [ -z "$obj" -o "$cflag" ] +then + if [ -n "$obj" ] + then + echo "Clearing out existing $obj tree" + rm -rf $obj + else + # no existing obj directory -- try to create one if Makefile found + obj=obj.$os.$rel.$arch$sfx + fi + if [ -r $BUILDTOOLS/OS/$os.$rel.$arch$sfx ]; then + oscf=$os.$rel.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rel.$arch ]; then + oscf=$os.$rel.$arch + elif [ -r $BUILDTOOLS/OS/$os.$rroot.$arch$sfx ]; then + oscf=$os.$rroot.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rroot.$arch ]; then + oscf=$os.$rroot.$arch + elif [ -r $BUILDTOOLS/OS/$os.$rbase.x.$arch$sfx ]; then + oscf=$os.$rbase.x.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rbase.x.$arch ]; then + oscf=$os.$rbase.x.$arch + elif [ -r $BUILDTOOLS/OS/$os.$rel$sfx ]; then + oscf=$os.$rel$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rel ]; then + oscf=$os.$rel + elif [ -r $BUILDTOOLS/OS/$os.$rroot$sfx ]; then + oscf=$os.$rroot$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rroot ]; then + oscf=$os.$rroot + elif [ -r $BUILDTOOLS/OS/$os.$rbase.x$sfx ]; then + oscf=$os.$rbase.x$sfx + elif [ -r $BUILDTOOLS/OS/$os.$rbase.x ]; then + oscf=$os.$rbase.x + elif [ -r $BUILDTOOLS/OS/$os.$arch$sfx ]; then + oscf=$os.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$os.$arch ]; then + oscf=$os.$arch + elif [ -r $BUILDTOOLS/OS/$rel.$arch$sfx ]; then + oscf=$rel.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$rel.$arch ]; then + oscf=$rel.$arch + elif [ -r $BUILDTOOLS/OS/$rroot.$arch$sfx ]; then + oscf=$rroot.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$rroot.$arch ]; then + oscf=$rroot.$arch + elif [ -r $BUILDTOOLS/OS/$rbase.x.$arch$sfx ]; then + oscf=$rbase.x.$arch$sfx + elif [ -r $BUILDTOOLS/OS/$rbase.x.$arch ]; then + oscf=$rbase.x.$arch + elif [ -r $BUILDTOOLS/OS/$os$sfx ]; then + oscf=$os$sfx + elif [ -r $BUILDTOOLS/OS/$os ]; then + oscf=$os + elif [ -r $BUILDTOOLS/OS/$arch$sfx ]; then + oscf=$arch$sfx + elif [ -r $BUILDTOOLS/OS/$arch ]; then + oscf=$arch + elif [ -r $BUILDTOOLS/OS/$rel$sfx ]; then + oscf=$rel$sfx + elif [ -r $BUILDTOOLS/OS/$rel ]; then + oscf=$rel + elif [ -r $BUILDTOOLS/OS/$rel$sfx ]; then + oscf=$rel$sfx + else + echo "Cannot determine how to support $arch.$os.$rel" >&2 + exit $EX_UNAVAILABLE + fi + M4=`sh $BUILDTOOLS/bin/find_m4.sh` + ret=$? + if [ $ret -ne 0 ] + then + exit $ret + fi + echo "Using M4=$M4" + export M4 + if [ "$mflag" ] + then + echo "Will run in virgin $obj using $BUILDTOOLS/OS/$oscf" + exit 0 + fi + if [ "$ABI" ] + then + echo "Using ABI $ABI" + fi + echo "Creating $obj using $BUILDTOOLS/OS/$oscf" + mkdir $obj + (cd $obj; ln -s ../*.[ch158] .) + if [ -f sendmail.hf ] + then + (cd $obj; ln -s ../sendmail.hf .) + fi + + rm -f $obj/.settings$$ + echo 'divert(-1)' > $obj/.settings$$ + cat $BUILDTOOLS/M4/header.m4 >> $obj/.settings$$ + if [ "$ABI" ] + then + echo "define(\`confABI', \`$ABI')" >> $obj/.settings$$ + fi + cat $BUILDTOOLS/OS/$oscf >> $obj/.settings$$ + + if [ -z "$siteconfig" ] + then + # none specified, use defaults + if [ -f $BUILDTOOLS/Site/site.$oscf$sfx.m4 ] + then + siteconfig=$BUILDTOOLS/Site/site.$oscf$sfx.m4 + elif [ -f $BUILDTOOLS/Site/site.$oscf.m4 ] + then + siteconfig=$BUILDTOOLS/Site/site.$oscf.m4 + fi + if [ -f $BUILDTOOLS/Site/site.config.m4 ] + then + siteconfig="$BUILDTOOLS/Site/site.config.m4 $siteconfig" + fi + fi + if [ ! -z "$siteconfig" ] + then + echo "Including $siteconfig" + cat $siteconfig >> $obj/.settings$$ + fi + if [ "$libdirs" ] + then + echo "define(\`confLIBDIRS', confLIBDIRS \`\`$libdirs'')" >> $obj/.settings$$ + fi + if [ "$incdirs" ] + then + echo "define(\`confINCDIRS', confINCDIRS \`\`$incdirs'')" >> $obj/.settings$$ + fi + echo 'divert(0)dnl' >> $obj/.settings$$ + libdirs=`(cat $obj/.settings$$; echo "_SRIDBIL_= confLIBDIRS" ) | \ + sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' | \ + ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - | \ + grep "^_SRIDBIL_=" | \ + sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' -e "s/^_SRIDBIL_=//"` + libsrch=`(cat $obj/.settings$$; echo "_HCRSBIL_= confLIBSEARCH" ) | \ + sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' | \ + ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - | \ + grep "^_HCRSBIL_=" | \ + sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' -e "s/^_HCRSBIL_=//"` + echo 'divert(-1)' >> $obj/.settings$$ + LIBDIRS="$libdirs" LIBSRCH="$libsrch" SITECONFIG="$siteconfig" sh $BUILDTOOLS/bin/configure.sh $sflag $oscf >> $obj/.settings$$ + echo 'divert(0)dnl' >> $obj/.settings$$ + sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' $obj/.settings$$ | \ + ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - Makefile.m4 | \ + sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' > $obj/Makefile + if [ $? -ne 0 -o ! -s $obj/Makefile ] + then + echo "ERROR: ${M4} failed; You may need a newer version of M4, at least as new as System V or GNU" 1>&2 + rm -rf $obj + exit $EX_UNAVAILABLE + fi + rm -f $obj/.settings$$ + echo "Making dependencies in $obj" + (cd $obj; ${MAKE-make} depend) +fi + +if [ "$mflag" ] +then + makefile=`ls -l $obj/Makefile | sed 's/.* //'` + if [ -z "$makefile" ] + then + echo "ERROR: $obj exists but has no Makefile" >&2 + exit $EX_NOINPUT + fi + echo "Will run in existing $obj using $makefile" + exit 0 +fi + +echo "Making in $obj" +cd $obj +eval exec ${MAKE-make} $makeargs diff --git a/contrib/sendmail/src/map.c b/contrib/sendmail/src/map.c new file mode 100644 index 0000000..4c95b7f --- /dev/null +++ b/contrib/sendmail/src/map.c @@ -0,0 +1,5065 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)map.c 8.239 (Berkeley) 6/5/98"; +#endif /* not lint */ + +#include "sendmail.h" + +#ifdef 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 +#endif +#ifdef NEWDB +# include +# ifndef DB_VERSION_MAJOR +# define DB_VERSION_MAJOR 1 +# endif +#endif +#ifdef NIS + struct dom_binding; /* forward reference needed on IRIX */ +# include +# ifdef NDBM +# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */ +# endif +#endif + +/* +** 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 an 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. +*/ + +#define DBMMODE 0644 + +#ifndef EX_NOTFOUND +# define EX_NOTFOUND EX_NOHOST +#endif + +extern bool aliaswait __P((MAP *, char *, int)); +extern bool extract_canonname __P((char *, char *, char[], int)); + +#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL +# define LOCK_ON_OPEN 1 /* we can open/create a locked file */ +#else +# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */ +#endif + +#ifndef O_ACCMODE +# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) +#endif + /* +** 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; + + 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 '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; + +#ifdef RESERVED_FOR_SUN + case 'd': + map->map_mflags |= MF_DOMAIN_WIDE; + break; + + case 's': + /* info type */ + break; +#endif + } + 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! +** +** Side Effects: +** none. +*/ + +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)) + { + printf("map_rewrite(%.*s), av =", (int)slen, s); + if (av == NULL) + printf(" (nullv)"); + else + { + for (avp = av; *avp != NULL; avp++) + printf("\n\t%s", *avp); + } + printf("\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) + free(buf); + buf = xalloc(buflen); + } + + bp = buf; + if (av == NULL) + { + bcopy(s, bp, slen); + bp += slen; + } + else + { + while (slen-- > 0 && (c = *s++) != '\0') + { + if (c != '%') + { + pushc: + *bp++ = c; + continue; + } + if (slen-- <= 0 || (c = *s++) == '\0') + c = '%'; + if (c == '%') + goto pushc; + if (!(isascii(c) && isdigit(c))) + { + *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'; ) + *bp++ = c; + } + } + if (map->map_app != NULL) + strcpy(bp, map->map_app); + else + *bp = '\0'; + if (tTd(39, 1)) + printf("map_rewrite => %s\n", buf); + return buf; +} + /* +** INITMAPS -- initialize for aliasing +** +** Parameters: +** rebuild -- if TRUE, this rebuilds the cached versions. +** e -- current envelope. +** +** Returns: +** none. +** +** Side Effects: +** initializes aliases: +** if alias database: opens the database. +** if no database available: reads aliases into the symbol table. +*/ + +void +initmaps(rebuild, e) + bool rebuild; + register ENVELOPE *e; +{ + extern void map_init __P((STAB *, int)); + +#if XDEBUG + checkfd012("entering initmaps"); +#endif + CurEnv = e; + + stabapply(map_init, 0); + stabapply(map_init, rebuild ? 2 : 1); +#if XDEBUG + checkfd012("exiting initmaps"); +#endif +} + +void +map_init(s, pass) + register STAB *s; + int pass; +{ + bool rebuildable; + register MAP *map; + + /* has to be a map */ + if (s->s_type != ST_MAP) + return; + + map = &s->s_map; + if (!bitset(MF_VALID, map->map_mflags)) + return; + + if (tTd(38, 2)) + printf("map_init(%s:%s, %s, %d)\n", + map->map_class->map_cname == NULL ? "NULL" : + map->map_class->map_cname, + map->map_mname == NULL ? "NULL" : map->map_mname, + map->map_file == NULL ? "NULL" : map->map_file, + pass); + + /* + ** Pass 0 opens all non-rebuildable maps. + ** Pass 1 opens all rebuildable maps for read. + ** Pass 2 rebuilds all rebuildable maps. + */ + + rebuildable = (bitset(MF_ALIAS, map->map_mflags) && + bitset(MCF_REBUILDABLE, map->map_class->map_cflags)); + + if ((pass == 0 && rebuildable) || + ((pass == 1 || pass == 2) && !rebuildable)) + { + if (tTd(38, 3)) + printf("\twrong pass (pass = %d, rebuildable = %d)\n", + pass, rebuildable); + return; + } + + /* if already open, close it (for nested open) */ + if (bitset(MF_OPEN, map->map_mflags)) + { + map->map_class->map_close(map); + map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); + } + + if (pass == 2) + { + (void) rebuildaliases(map, FALSE); + return; + } + + if (map->map_class->map_open(map, O_RDONLY)) + { + if (tTd(38, 4)) + printf("\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; + } + else + { + if (tTd(38, 4)) + printf("\t%s:%s %s: invalid: %s\n", + map->map_class->map_cname == NULL ? "NULL" : + map->map_class->map_cname, + map->map_mname == NULL ? "NULL" : + map->map_mname, + map->map_file == NULL ? "NULL" : + map->map_file, + errstring(errno)); + if (!bitset(MF_OPTIONAL, map->map_mflags)) + { + extern MAPCLASS BogusMapClass; + + map->map_class = &BogusMapClass; + map->map_mflags |= MF_OPEN; + } + } +} + /* +** 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. +** +** Returns: +** TRUE -- if the host was found. +** FALSE -- otherwise. +*/ + +bool +getcanonname(host, hbsize, trymx) + char *host; + int hbsize; + bool trymx; +{ + int nmaps; + int mapno; + bool found = FALSE; + bool got_tempfail = FALSE; + auto int stat; + char *maptype[MAXMAPSTACK]; + short mapreturn[MAXMAPACTIONS]; + + nmaps = switch_map_find("hosts", maptype, mapreturn); + for (mapno = 0; mapno < nmaps; mapno++) + { + int i; + + if (tTd(38, 20)) + printf("getcanonname(%s), trying %s\n", + host, maptype[mapno]); + if (strcmp("files", maptype[mapno]) == 0) + { + extern bool text_getcanonname __P((char *, int, int *)); + + found = text_getcanonname(host, hbsize, &stat); + } +#ifdef NIS + else if (strcmp("nis", maptype[mapno]) == 0) + { + extern bool nis_getcanonname __P((char *, int, int *)); + + found = nis_getcanonname(host, hbsize, &stat); + } +#endif +#ifdef NISPLUS + else if (strcmp("nisplus", maptype[mapno]) == 0) + { + extern bool nisplus_getcanonname __P((char *, int, int *)); + + found = nisplus_getcanonname(host, hbsize, &stat); + } +#endif +#if NAMED_BIND + else if (strcmp("dns", maptype[mapno]) == 0) + { + extern bool dns_getcanonname __P((char *, int, bool, int *)); + + found = dns_getcanonname(host, hbsize, trymx, &stat); + } +#endif +#if NETINFO + else if (strcmp("netinfo", maptype[mapno]) == 0) + { + extern bool ni_getcanonname __P((char *, int, int *)); + + found = ni_getcanonname(host, hbsize, &stat); + } +#endif + else + { + found = FALSE; + stat = 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; + + /* see if we should continue */ + if (stat == EX_TEMPFAIL) + { + i = MA_TRYAGAIN; + got_tempfail = TRUE; + } + else if (stat == EX_NOTFOUND) + i = MA_NOTFOUND; + else + i = MA_UNAVAIL; + if (bitset(1 << mapno, mapreturn[i])) + break; + } + + if (found) + { + char *d; + + if (tTd(38, 20)) + printf("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] != '.') + strcat(host, "."); + strcat(host, d); + } + else + { + return FALSE; + } + } + return TRUE; + } + + if (tTd(38, 20)) + printf("getcanonname(%s), failed, stat=%d\n", host, stat); + +#if NAMED_BIND + if (got_tempfail) + h_errno = TRY_AGAIN; + else + h_errno = HOST_NOT_FOUND; +#endif + + return FALSE; +} + /* +** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry +** +** Parameters: +** name -- the name against which to match. +** 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. +*/ + +bool +extract_canonname(name, line, cbuf, cbuflen) + char *name; + char *line; + char cbuf[]; + int cbuflen; +{ + int i; + char *p; + bool found = FALSE; + extern char *get_column __P((char *, int, char, char *, int)); + + 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)) + { + snprintf(cbuf, cbuflen, "%s", p); + } + if (strcasecmp(name, p) == 0) + found = TRUE; + } + 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) + strlen(cbuf) + 1 < cbuflen) + { + p = &cbuf[strlen(cbuf)]; + *p++ = '.'; + strcpy(p, domain); + } + } + return found; +} + /* +** NDBM modules +*/ + +#ifdef NDBM + +/* +** NDBM_MAP_OPEN -- DBM-style map open +*/ + +bool +ndbm_map_open(map, mode) + MAP *map; + int mode; +{ + register DBM *dbm; + struct stat st; + int dfd; + int pfd; + int sff; + int ret; + int smode = S_IREAD; + char dirfile[MAXNAME + 1]; + char pagfile[MAXNAME + 1]; + struct stat std, stp; + + if (tTd(38, 2)) + printf("ndbm_map_open(%s, %s, %d)\n", + map->map_mname, map->map_file, mode); + map->map_lockfd = -1; + mode &= O_ACCMODE; + + /* do initial file and directory checks */ + snprintf(dirfile, sizeof dirfile, "%s.dir", map->map_file); + snprintf(pagfile, sizeof pagfile, "%s.pag", map->map_file); + sff = SFF_ROOTOK|SFF_REGONLY; + if (mode == O_RDWR) + { + sff |= SFF_CREAT; + if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) + sff |= SFF_NOSLINK; + if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) + sff |= SFF_NOHLINK; + smode = S_IWRITE; + } + else + { + if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) + sff |= SFF_NOWLINK; + } + if (!bitset(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 == ENOENT && AutoRebuild && + bitset(MCF_REBUILDABLE, map->map_class->map_cflags) && + (bitset(MF_IMPL_NDBM, map->map_mflags) || + bitset(MF_ALIAS, map->map_mflags)) && + mode == O_RDONLY) + { + bool impl = bitset(MF_IMPL_NDBM, map->map_mflags); + extern bool impl_map_open __P((MAP *, int)); + + /* may be able to rebuild */ + map->map_mflags &= ~MF_IMPL_NDBM; + if (!rebuildaliases(map, TRUE)) + return FALSE; + if (impl) + return impl_map_open(map, O_RDONLY); + else + return ndbm_map_open(map, O_RDONLY); + } + if (ret != 0) + { + char *prob = "unsafe"; + + /* cannot open this map */ + if (ret == ENOENT) + prob = "missing"; + if (tTd(38, 2)) + printf("\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 + 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 + /* + ** This ugly code opens the map without truncating it, + ** locks the file, then truncates it. Necessary to + ** avoid race conditions. + */ + + int dirfd; + int pagfd; + int sff = SFF_CREAT|SFF_OPENASROOT; + + if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) + sff |= SFF_NOSLINK; + if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) + sff |= SFF_NOHLINK; + + dirfd = safeopen(dirfile, mode, DBMMODE, sff); + pagfd = safeopen(pagfile, mode, DBMMODE, sff); + + if (dirfd < 0 || pagfd < 0) + { + int 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) + { + int 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)) + { + int 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; + close(pagfd); + + /* twiddle bits for dbm_open */ + mode &= ~(O_CREAT|O_EXCL); +# endif + } +#endif + + /* open the database */ + dbm = dbm_open(map->map_file, mode, DBMMODE); + if (dbm == NULL) + { + int 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) + close(map->map_lockfd); +#endif + 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) + close(map->map_lockfd); +#endif + errno = 0; + syserr("dbm map \"%s\": cannot support GDBM", + map->map_mname); + return FALSE; + } + + if (filechanged(dirfile, dfd, &std) || + filechanged(pagfile, pfd, &stp)) + { + int save_errno = errno; + + dbm_close(dbm); +#if !LOCK_ON_OPEN && !NOFTRUNCATE + if (map->map_lockfd >= 0) + close(map->map_lockfd); +#endif + errno = save_errno; + syserr("ndbm_map_open(%s): file changed after open", + map->map_file); + return FALSE; + } + + map->map_db1 = (ARBPTR_T) dbm; + 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 + if (bitset(MF_ALIAS, map->map_mflags) && + !aliaswait(map, ".pag", TRUE)) + return FALSE; + } + else + { + map->map_mflags |= MF_LOCKED; + if (geteuid() == 0 && TrustedFileUid != 0) + { + if (fchown(dfd, TrustedFileUid, -1) < 0 || + fchown(pfd, TrustedFileUid, -1) < 0) + { + int err = errno; + + sm_syslog(LOG_ALERT, NOQID, + "ownership change on %s failed: %s", + map->map_file, errstring(err)); + message("050 ownership change on %s failed: %s", + map->map_file, errstring(err)); + } + } + } + if (fstat(dfd, &st) >= 0) + map->map_mtime = st.st_mtime; + 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 fd; + char keybuf[MAXNAME + 1]; + struct stat stbuf; + + if (tTd(38, 20)) + printf("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; + bcopy(key.dptr, keybuf, key.dsize); + keybuf[key.dsize] = '\0'; + makelower(keybuf); + key.dptr = keybuf; + } +lockdbm: + fd = dbm_dirfno((DBM *) map->map_db1); + if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) + (void) lockfile(fd, map->map_file, ".dir", LOCK_SH); + if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime) + { + /* Reopen the database to sync the cache */ + int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR + : O_RDONLY; + + map->map_class->map_close(map); + map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); + if (map->map_class->map_open(map, omode)) + { + map->map_mflags |= MF_OPEN; + 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_class = &BogusMapClass; + map->map_mflags |= MF_OPEN; + 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 (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) + (void) lockfile(fd, 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 stat; + char keybuf[MAXNAME + 1]; + + if (tTd(38, 12)) + printf("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; + bcopy(key.dptr, keybuf, 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++; + } + + stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT); + if (stat > 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) free(buf); + bufsiz = data.dsize + old.dsize + 2; + buf = xalloc(bufsiz); + } + snprintf(buf, bufsiz, "%s,%s", + data.dptr, old.dptr); + data.dsize = data.dsize + old.dsize + 1; + data.dptr = buf; + if (tTd(38, 9)) + printf("ndbm_map_store append=%s\n", data.dptr); + } + } + stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE); + } + if (stat != 0) + syserr("readaliases: dbm put (%s)", lhs); +} + + +/* +** NDBM_MAP_CLOSE -- close the database +*/ + +void +ndbm_map_close(map) + register MAP *map; +{ + if (tTd(38, 9)) + printf("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[200]; + + 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) 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 + + /* 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 +} + +#endif + /* +** NEWDB (Hash and BTree) Modules +*/ + +#ifdef 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. +*/ + +#if DB_VERSION_MAJOR < 2 +extern bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *)); +#else +extern bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *)); +#endif + +/* these should be K line arguments */ +#if DB_VERSION_MAJOR < 2 +# define db_cachesize cachesize +# define h_nelem nelem +# ifndef DB_CACHE_SIZE +# define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */ +# endif +# ifndef DB_HASH_NELEM +# define DB_HASH_NELEM 4096 /* (starting) size of hash table */ +# endif +#endif + +bool +bt_map_open(map, mode) + MAP *map; + int mode; +{ +#if DB_VERSION_MAJOR < 2 + BTREEINFO btinfo; +#else + DB_INFO btinfo; +#endif + + if (tTd(38, 2)) + printf("bt_map_open(%s, %s, %d)\n", + map->map_mname, map->map_file, mode); + + bzero(&btinfo, sizeof btinfo); +#ifdef DB_CACHE_SIZE + btinfo.db_cachesize = DB_CACHE_SIZE; +#endif + 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; +#else + DB_INFO hinfo; +#endif + + if (tTd(38, 2)) + printf("hash_map_open(%s, %s, %d)\n", + map->map_mname, map->map_file, mode); + + bzero(&hinfo, sizeof hinfo); +#ifdef DB_HASH_NELEM + hinfo.h_nelem = DB_HASH_NELEM; +#endif +#ifdef DB_CACHE_SIZE + hinfo.db_cachesize = DB_CACHE_SIZE; +#endif + return db_map_open(map, mode, "hash", DB_HASH, &hinfo); +} + +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; +#else + DB_INFO *openinfo; +#endif +{ + DB *db = NULL; + int i; + int omode; + int smode = S_IREAD; + int fd; + int sff; + int saveerrno; + struct stat st; + char buf[MAXNAME + 1]; + + /* do initial file and directory checks */ + snprintf(buf, sizeof buf - 3, "%s", map->map_file); + i = strlen(buf); + if (i < 3 || strcmp(&buf[i - 3], ".db") != 0) + (void) strcat(buf, ".db"); + + mode &= O_ACCMODE; + omode = mode; + + sff = SFF_ROOTOK|SFF_REGONLY; + if (mode == O_RDWR) + { + sff |= SFF_CREAT; + if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) + sff |= SFF_NOSLINK; + if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) + sff |= SFF_NOHLINK; + smode = S_IWRITE; + } + else + { + if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) + sff |= SFF_NOWLINK; + } + if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) + sff |= SFF_SAFEDIRPATH; + i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st); + if (i == ENOENT && AutoRebuild && + bitset(MCF_REBUILDABLE, map->map_class->map_cflags) && + (bitset(MF_IMPL_HASH, map->map_mflags) || + bitset(MF_ALIAS, map->map_mflags)) && + mode == O_RDONLY) + { + bool impl = bitset(MF_IMPL_HASH, map->map_mflags); + extern bool impl_map_open __P((MAP *, int)); + + /* may be able to rebuild */ + map->map_mflags &= ~MF_IMPL_HASH; + if (!rebuildaliases(map, TRUE)) + return FALSE; + if (impl) + return impl_map_open(map, O_RDONLY); + else + return db_map_open(map, O_RDONLY, mapclassname, + dbtype, openinfo); + } + + if (i != 0) + { + char *prob = "unsafe"; + + /* cannot open this map */ + if (i == ENOENT) + prob = "missing"; + if (tTd(38, 2)) + printf("\t%s map file: %s\n", prob, errstring(i)); + errno = i; + if (!bitset(MF_OPTIONAL, map->map_mflags)) + syserr("%s map \"%s\": %s map file %s", + 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 + /* + ** 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)) + { + int 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) + { + int 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 + +#if DB_VERSION_MAJOR < 2 + db = dbopen(buf, omode, DBMMODE, dbtype, openinfo); +#else + { + int flags = 0; + + if (mode == O_RDONLY) + flags |= DB_RDONLY; + if (bitset(O_CREAT, omode)) + flags |= DB_CREATE; + if (bitset(O_TRUNC, omode)) + flags |= DB_TRUNCATE; + + errno = db_open(buf, dbtype, flags, DBMMODE, + NULL, openinfo, &db); + } +#endif + saveerrno = errno; + +#if !LOCK_ON_OPEN + if (mode == O_RDWR) + map->map_lockfd = fd; + else + (void) close(fd); +#endif + + 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 + errno = saveerrno; + 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 + fd = -1; + errno = db->fd(db, &fd); +#endif + if (filechanged(buf, fd, &st)) + { + int save_errno = errno; + +#if DB_VERSION_MAJOR < 2 + db->close(db); +#else + errno = db->close(db, 0); +#endif +#if !LOCK_ON_OPEN + if (map->map_lockfd >= 0) + close(map->map_lockfd); +#endif + 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 + + /* 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 && TrustedFileUid != 0) + { + if (fchown(fd, TrustedFileUid, -1) < 0) + { + int err = errno; + + sm_syslog(LOG_ALERT, NOQID, + "ownership change on %s failed: %s", + buf, errstring(err)); + message("050 ownership change on %s failed: %s", + buf, errstring(err)); + } + } + } + + if (fd >= 0 && fstat(fd, &st) >= 0) + map->map_mtime = st.st_mtime; + + map->map_db2 = (ARBPTR_T) db; + if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && + !aliaswait(map, ".db", TRUE)) + return FALSE; + 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 saveerrno; + int fd; + struct stat stbuf; + char keybuf[MAXNAME + 1]; + char buf[MAXNAME + 1]; + + bzero(&key, sizeof key); + bzero(&val, sizeof val); + + if (tTd(38, 20)) + printf("db_map_lookup(%s, %s)\n", + map->map_mname, name); + + i = strlen(map->map_file); + if (i > MAXNAME) + i = MAXNAME; + strncpy(buf, map->map_file, i); + buf[i] = '\0'; + 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; + bcopy(name, keybuf, 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 + fd = -1; + errno = db->fd(db, &fd); +#endif + 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; + + map->map_class->map_close(map); + map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); + if (map->map_class->map_open(map, omode)) + { + map->map_mflags |= MF_OPEN; + 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_class = &BogusMapClass; + map->map_mflags |= MF_OPEN; + 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 + 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 + 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 + 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 + if (st == 0) + map->map_mflags &= ~MF_TRY0NULL; + } + saveerrno = errno; + if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) + (void) lockfile(fd, buf, ".db", LOCK_UN); + if (st != 0) + { + errno = saveerrno; + 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 stat; + DBT key; + DBT data; + register DB *db = map->map_db2; + char keybuf[MAXNAME + 1]; + + bzero(&key, sizeof key); + bzero(&data, sizeof data); + + if (tTd(38, 12)) + printf("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; + bcopy(key.data, keybuf, 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 + stat = db->put(db, &key, &data, R_NOOVERWRITE); +#else + errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE); + switch (errno) + { + case DB_KEYEXIST: + stat = 1; + break; + + case 0: + stat = 0; + break; + + default: + stat = -1; + break; + } +#endif + if (stat > 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; + + bzero(&old, sizeof old); + + old.data = db_map_lookup(map, key.data, + (char **)NULL, &stat); + if (old.data != NULL) + { + old.size = strlen(old.data); + if (data.size + old.size + 2 > bufsiz) + { + if (buf != NULL) + (void) free(buf); + bufsiz = data.size + old.size + 2; + buf = xalloc(bufsiz); + } + snprintf(buf, bufsiz, "%s,%s", + (char *) data.data, (char *) old.data); + data.size = data.size + old.size + 1; + data.data = buf; + if (tTd(38, 9)) + printf("db_map_store append=%s\n", + (char *) data.data); + } + } +#if DB_VERSION_MAJOR < 2 + stat = db->put(db, &key, &data, 0); +#else + stat = errno = db->put(db, NULL, &key, &data, 0); +#endif + } + if (stat != 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)) + printf("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 + +#if DB_VERSION_MAJOR < 2 + if (db->close(db) != 0) +#else + if ((errno = db->close(db, 0)) != 0) +#endif + syserr("readaliases: db close failure"); +} + +#endif + /* +** NIS Modules +*/ + +# ifdef NIS + +# ifndef YPERR_BUSY +# define YPERR_BUSY 16 +# endif + +/* +** 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)) + printf("nis_map_open(%s, %s, %d)\n", + map->map_mname, map->map_file, mode); + + mode &= O_ACCMODE; + if (mode != O_RDONLY) + { + /* issue a pseudo-error message */ +#ifdef ENOSYS + errno = ENOSYS; +#else +# ifdef EFTYPE + errno = EFTYPE; +# else + errno = ENXIO; +# endif +#endif + 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("421 NIS map %s specified, but NIS not running", + map->map_file); + return FALSE; + } + } + + /* check to see if this map actually exists */ + yperr = yp_match(map->map_domain, map->map_file, "@", 1, + &vp, &vsize); + if (tTd(38, 10)) + printf("nis_map_open: yp_match(@, %s, %s) => %s\n", + map->map_domain, map->map_file, yperr_string(yperr)); + 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 + return TRUE; + } + + if (!bitset(MF_OPTIONAL, map->map_mflags)) + { + syserr("421 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]; + + if (tTd(38, 20)) + printf("nis_map_lookup(%s, %s)\n", + map->map_mname, name); + + buflen = strlen(name); + if (buflen > sizeof keybuf - 1) + buflen = sizeof keybuf - 1; + bcopy(name, keybuf, buflen); + keybuf[buflen] = '\0'; + if (!bitset(MF_NOFOLDCASE, map->map_mflags)) + makelower(keybuf); + yperr = YPERR_KEY; + 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)) + { + 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); + return NULL; + } + if (bitset(MF_MATCHONLY, map->map_mflags)) + return map_rewrite(map, name, strlen(name), NULL); + else + return map_rewrite(map, vp, vsize, av); +} + + +/* +** NIS_GETCANONNAME -- look up canonical name in NIS +*/ + +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)) + printf("nis_getcanonname(%s)\n", name); + + if (strlen(name) >= sizeof nbuf) + { + *statp = EX_UNAVAILABLE; + return FALSE; + } + (void) strcpy(nbuf, name); + shorten_hostname(nbuf); + keylen = strlen(nbuf); + + if (yp_domain == NULL) + yp_get_default_domain(&yp_domain); + makelower(nbuf); + yperr = YPERR_KEY; + if (try0null) + { + yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, + &vp, &vsize); + if (yperr == 0) + try1null = FALSE; + } + if (yperr == YPERR_KEY && try1null) + { + 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; + return FALSE; + } + if (vsize >= sizeof host_record) + vsize = sizeof host_record - 1; + strncpy(host_record, vp, vsize); + host_record[vsize] = '\0'; + if (tTd(38, 44)) + printf("got record `%s'\n", host_record); + if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf)) + { + /* this should not happen, but.... */ + *statp = EX_NOHOST; + return FALSE; + } + if (hbsize < strlen(cbuf)) + { + *statp = EX_UNAVAILABLE; + return FALSE; + } + strcpy(name, cbuf); + *statp = EX_OK; + return TRUE; +} + +#endif + /* +** NISPLUS Modules +** +** This code donated by Sun Microsystems. +*/ + +#ifdef 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)) + printf("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 */ + extern char *nisplus_default_domain __P((void)); + + map->map_domain = newstr(nisplus_default_domain()); + if (tTd(38, 2)) + printf("nisplus_map_open(%s): using domain %s\n", + map->map_file, map->map_domain); + } + if (!PARTIAL_NAME(map->map_file)) + { + map->map_domain = newstr(""); + snprintf(qbuf, sizeof qbuf, "%s", map->map_file); + } + else + { + /* check to see if this map actually exists */ + snprintf(qbuf, sizeof qbuf, "%s.%s", + 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("421 Cannot find table %s.%s: %s", + map->map_file, map->map_domain, + nis_sperrno(res->status)); +#endif + errno = EAGAIN; + return FALSE; + } + } + + if (NIS_RES_NUMOBJ(res) != 1 || + (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ)) + { + if (tTd(38, 10)) + printf("nisplus_map_open: %s is not a table\n", qbuf); +#if 0 + if (!bitset(MF_OPTIONAL, map->map_mflags)) + syserr("421 %s.%s: %s is not a table", + map->map_file, map->map_domain, + nis_sperrno(res->status)); +#endif + 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))) + break; + } + if (i == max_col) + { + if (tTd(38, 2)) + printf("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)) + printf("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)) + printf("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; + 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--; + /* fall through... */ + + 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)) + snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s", + map->map_keycolnm, search_key, map->map_file, + map->map_domain); + else + snprintf(qbuf, sizeof qbuf, "[%s=%s],%s", + map->map_keycolnm, search_key, map->map_file); + + if (tTd(38, 20)) + printf("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)) + printf("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)) + printf("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)) + printf("nisplus_map_lookup(%s), failed\n", name); + nis_freeresult(result); + return NULL; +} + + + +/* +** NISPLUS_GETCANONNAME -- look up canonical name in NIS+ +*/ + +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 (strlen(name) >= sizeof nbuf) + { + *statp = EX_UNAVAILABLE; + return FALSE; + } + (void) strcpy(nbuf, name); + shorten_hostname(nbuf); + + p = strchr(nbuf, '.'); + if (p == NULL) + { + /* single token */ + snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf); + } + else if (p[1] != '\0') + { + /* multi token -- take only first token in nbuf */ + *p = '\0'; + snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s", + nbuf, &p[1]); + } + else + { + *statp = EX_NOHOST; + return FALSE; + } + + if (tTd(38, 20)) + printf("\nnisplus_getcanoname(%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)) + printf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n", + name, count); + } + + if (tTd(38, 20)) + printf("nisplus_getcanoname(%s), found in directory \"%s\"\n", + name, (NIS_RES_OBJECT(result))->zo_domain); + + + vp = ((NIS_RES_OBJECT(result))->EN_col(0)); + vsize = strlen(vp); + if (tTd(38, 20)) + printf("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') + strcpy(name, vp); + else + 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)) + printf("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(); + snprintf(default_domain, sizeof default_domain, "%s", p); + return default_domain; +} + +#endif /* NISPLUS */ + /* +** LDAP Modules +** +** Contributed by Booker C. Bense . +** Get your support from him. +*/ + +#ifdef LDAPMAP + +# undef NEEDGETOPT /* used for something else in LDAP */ + +# include +# include +# include "ldap_map.h" + +/* +** LDAP_MAP_OPEN -- open LDAP map +** +** Since LDAP is TCP-based there is not much we can or should do +** here. It might be a good idea to attempt an open/close here. +*/ + +bool +ldap_map_open(map, mode) + MAP *map; + int mode; +{ + if (tTd(38, 2)) + printf("ldap_map_open(%s, %d)\n", map->map_mname, mode); + + mode &= O_ACCMODE; + if (mode != O_RDONLY) + { + /* issue a pseudo-error message */ +#ifdef ENOSYS + errno = ENOSYS; +#else +# ifdef EFTYPE + errno = EFTYPE; +# else + errno = ENXIO; +# endif +#endif + return FALSE; + } + return TRUE; +} + + +/* +** LDAP_MAP_START -- actually open LDAP map +** +** Caching should be investigated. +*/ + +static jmp_buf LDAPTimeout; + +static void +ldaptimeout(sig_no) + int sig_no; +{ + longjmp(LDAPTimeout, 1); +} + +bool +ldap_map_start(map) + MAP *map; +{ + LDAP_MAP_STRUCT *lmap; + LDAP *ld; + register EVENT *ev = NULL; + + if (tTd(38, 2)) + printf("ldap_map_start(%s)\n", map->map_mname); + + lmap = (LDAP_MAP_STRUCT *) map->map_db1; + + if (tTd(38,9)) + printf("ldap_open(%s, %d)\n", lmap->ldaphost, lmap->ldapport); + + /* Need to set an alarm here, ldap_open is hopelessly broken. */ + + /* set the timeout */ + if (lmap->timeout.tv_sec != 0) + { + if (setjmp(LDAPTimeout) != 0) + { + if (LogLevel > 1) + sm_syslog(LOG_NOTICE, CurEnv->e_id, + "timeout waiting for ldap_open to %.100s", + lmap->ldaphost); + return (FALSE); + } + ev = setevent(lmap->timeout.tv_sec, ldaptimeout, 0); + } + + if ((ld = ldap_open(lmap->ldaphost,lmap->ldapport)) == NULL) + { + if (!bitset(MF_OPTIONAL, map->map_mflags)) + { + syserr("ldapopen failed to %s in map %s", + lmap->ldaphost, map->map_mname); + } + return FALSE; + } + + /* clear the event if it has not sprung */ + clrevent(ev); + /* From here on in we can use ldap internal timelimits */ + ld->ld_deref = lmap->deref; + ld->ld_timelimit = lmap->timelimit; + ld->ld_sizelimit = lmap->sizelimit; + ld->ld_options = lmap->ldap_options; + + if (ldap_bind_s(ld, lmap->binddn,lmap->passwd,lmap->method) != LDAP_SUCCESS) + { + if (!bitset(MF_OPTIONAL, map->map_mflags)) + { + syserr("421 Cannot bind to map %s in ldap server %s", + map->map_mname, lmap->ldaphost); + } + } + else + { + /* We need to cast ld into the map structure */ + lmap->ld = ld; + return TRUE; + } + + return FALSE; +} + + +/* +** LDAP_MAP_CLOSE -- close ldap map +*/ + +void +ldap_map_close(map) + MAP *map; +{ + LDAP_MAP_STRUCT *lmap ; + lmap = (LDAP_MAP_STRUCT *) map->map_db1; + if (lmap->ld != NULL) + ldap_unbind(lmap->ld); +} + + +#ifdef SUNET_ID +/* +** SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form +** This only makes sense at Stanford University. +*/ + +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); +} + + + +#endif /* SUNET_ID */ +/* +** LDAP_MAP_LOOKUP -- look up a datum in a LDAP map +*/ + +char * +ldap_map_lookup(map, name, av, statp) + MAP *map; + char *name; + char **av; + int *statp; +{ + LDAP_MAP_STRUCT *lmap = NULL; + LDAPMessage *entry; + char *vp; + auto int vsize; + char keybuf[MAXNAME + 1]; + char filter[LDAP_MAP_MAX_FILTER + 1]; + char **attr_values = NULL; + char *result; + int name_len; + + if (tTd(38, 20)) + printf("ldap_map_lookup(%s, %s)\n", map->map_mname, name); + + /* actually open the map */ + if (!ldap_map_start(map)) + { + result = NULL; + *statp = EX_TEMPFAIL; + goto quick_exit; + } + + /* Get ldap struct pointer from map */ + lmap = (LDAP_MAP_STRUCT *) map->map_db1; + + name_len = strlen(name); + if (name_len > MAXNAME) + name_len = MAXNAME; + strncpy(keybuf, name, name_len); + keybuf[name_len] = '\0'; + + if (!bitset(MF_NOFOLDCASE, map->map_mflags)) +#ifdef SUNET_ID + sunet_id_hash(keybuf); +#else + makelower(keybuf); +#endif /*SUNET_ID */ + + /* sprintf keybuf into filter */ + snprintf(filter, sizeof filter, lmap->filter, keybuf); + + if (ldap_search_st(lmap->ld, lmap->base,lmap->scope,filter, + lmap->attr, lmap->attrsonly, &(lmap->timeout), + &(lmap->res)) != LDAP_SUCCESS) + { + /* try close/opening map */ + ldap_map_close(map); + if (!ldap_map_start(map)) + { + result = NULL; + *statp = EX_TEMPFAIL; + goto quick_exit; + } + if (ldap_search_st(lmap->ld, lmap->base, lmap->scope, filter, + lmap->attr, lmap->attrsonly, + &(lmap->timeout), &(lmap->res)) + != LDAP_SUCCESS) + { + if (!bitset(MF_OPTIONAL, map->map_mflags)) + { + syserr("Error in ldap_search_st using %s in map %s", + filter, map->map_mname); + } + result = NULL; + *statp = EX_TEMPFAIL; + goto quick_exit; + } + } + + entry = ldap_first_entry(lmap->ld,lmap->res); + if (entry == NULL) + { + result = NULL; + *statp = EX_NOTFOUND; + goto quick_exit; + } + + /* Need to build the args for map_rewrite here */ + attr_values = ldap_get_values(lmap->ld,entry,lmap->attr[0]); + if (attr_values == NULL) + { + /* bad things happened */ + result = NULL; + *statp = EX_NOTFOUND; + goto quick_exit; + } + + *statp = EX_OK; + + /* If there is more that one use the first */ + vp = attr_values[0]; + vsize = strlen(vp); + + if (LogLevel > 9) + sm_syslog(LOG_INFO, CurEnv->e_id, + "ldap %.100s => %s", + name, vp); + if (bitset(MF_MATCHONLY, map->map_mflags)) + result = map_rewrite(map, name, strlen(name), NULL); + else + result = map_rewrite(map, vp, vsize, av); + + quick_exit: + if (attr_values != NULL) + ldap_value_free(attr_values); + if (lmap != NULL) + ldap_msgfree(lmap->res); + ldap_map_close(map); + return result ; +} + + +/* +** LDAP_MAP_DEQUOTE - helper routine for ldap_map_parseargs +*/ + +char * +ldap_map_dequote(str) + char *str; +{ + char *p; + char *start; + p = str; + + if (*p == '"') + { + start = ++p; + /* Should probably swallow initial whitespace here */ + } + else + { + return(str); + } + while (*p != '"' && *p != '\0') + { + p++; + } + if (*p != '\0') + *p = '\0'; + return start; +} + +/* +** LDAP_MAP_PARSEARGS -- parse ldap map definition args. +*/ + +bool +ldap_map_parseargs(map,args) + MAP *map; + char *args; +{ + register char *p = args; + register int done; + LDAP_MAP_STRUCT *lmap; + + /* We need to alloc an LDAP_MAP_STRUCT struct */ + lmap = (LDAP_MAP_STRUCT *) xalloc(sizeof(LDAP_MAP_STRUCT)); + + /* Set default int's here , default strings below */ + lmap->ldapport = DEFAULT_LDAP_MAP_PORT; + lmap->deref = DEFAULT_LDAP_MAP_DEREF; + lmap->timelimit = DEFAULT_LDAP_MAP_TIMELIMIT; + lmap->sizelimit = DEFAULT_LDAP_MAP_SIZELIMIT; + lmap->ldap_options = DEFAULT_LDAP_MAP_LDAP_OPTIONS; + lmap->method = DEFAULT_LDAP_MAP_METHOD; + lmap->scope = DEFAULT_LDAP_MAP_SCOPE; + lmap->attrsonly = DEFAULT_LDAP_MAP_ATTRSONLY; + lmap->timeout.tv_sec = DEFAULT_LDAP_MAP_TIMELIMIT; + lmap->timeout.tv_usec = 0; + + /* Default char ptrs to NULL */ + lmap->binddn = NULL; + lmap->passwd = NULL; + lmap->base = NULL; + lmap->ldaphost = NULL; + + /* Default general ptrs to NULL */ + lmap->ld = NULL; + lmap->res = NULL; + + 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; + + /* Start of ldap_map specific args */ + case 'k': /* search field */ + while (isascii(*++p) && isspace(*p)) + continue; + lmap->filter = p; + break; + + case 'v': /* attr to return */ + while (isascii(*++p) && isspace(*p)) + continue; + lmap->attr[0] = p; + lmap->attr[1] = NULL; + 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\n"); +#endif /* LDAP_REFERRALS */ + break; + + case 'n': /* retrieve attribute names only -- no values */ + lmap->attrsonly += 1; + break; + + case 's': /* search scope */ + if (strncasecmp(++p, "base", 4) == 0) + { + lmap->scope = LDAP_SCOPE_BASE; + } + else if (strncasecmp(p, "one", 3) == 0) + { + lmap->scope = LDAP_SCOPE_ONELEVEL; + } + else if (strncasecmp(p, "sub", 3) == 0) + { + lmap->scope = LDAP_SCOPE_SUBTREE; + } + 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 'h': /* ldap host */ + while (isascii(*++p) && isspace(*p)) + continue; + map->map_domain = p; + lmap->ldaphost = p; + break; + + case 'b': /* search base */ + while (isascii(*++p) && isspace(*p)) + continue; + lmap->base = p; + break; + + case 'p': /* ldap port */ + while (isascii(*++p) && isspace(*p)) + continue; + lmap->ldapport = atoi(p); + break; + + case 'l': /* time limit */ + while (isascii(*++p) && isspace(*p)) + continue; + lmap->timelimit = atoi(p); + lmap->timeout.tv_sec = lmap->timelimit; + break; + + } + + /* need to account for quoted strings here arggg... */ + 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(ldap_map_dequote(map->map_app)); + if (map->map_tapp != NULL) + map->map_tapp = newstr(ldap_map_dequote(map->map_tapp)); + if (map->map_domain != NULL) + map->map_domain = newstr(ldap_map_dequote(map->map_domain)); + + /* + ** We need to swallow up all the stuff into a struct + ** and dump it into map->map_dbptr1 + */ + + if (lmap->ldaphost != NULL) + lmap->ldaphost = newstr(ldap_map_dequote(lmap->ldaphost)); + else + { + syserr("LDAP map: -h flag is required"); + return FALSE; + } + + if (lmap->binddn != NULL) + lmap->binddn = newstr(ldap_map_dequote(lmap->binddn)); + else + lmap->binddn = DEFAULT_LDAP_MAP_BINDDN; + + + if (lmap->passwd != NULL) + lmap->passwd = newstr(ldap_map_dequote(lmap->passwd)); + else + lmap->passwd = DEFAULT_LDAP_MAP_PASSWD; + + if (lmap->base != NULL) + lmap->base = newstr(ldap_map_dequote(lmap->base)); + else + { + syserr("LDAP map: -b flag is required"); + return FALSE; + } + + + if (lmap->filter != NULL) + lmap->filter = newstr(ldap_map_dequote(lmap->filter)); + else + { + if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) + { + syserr("No filter given in map %s", map->map_mname); + return FALSE; + } + } + if (lmap->attr[0] != NULL) + lmap->attr[0] = newstr(ldap_map_dequote(lmap->attr[0])); + else + { + if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) + { + syserr("No return attribute in %s", map->map_mname); + return FALSE; + } + } + + map->map_db1 = (ARBPTR_T) lmap; + return TRUE; +} + +#endif /* LDAP Modules */ + /* +** syslog map +*/ + +#if _FFR_MAP_SYSLOG + +#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; + + for (;;) + { + while (isascii(*p) && isspace(*p)) + p++; + if (*p != '-') + break; + if (*++p == 'L') + priority = ++p; + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p != '\0') + *p++ = '\0'; + } + + if (priority == NULL) + map->map_prio = LOG_INFO; + else + { + if (strncasecmp("LOG_", priority, 4) == 0) + priority += 4; + +#ifdef LOG_EMERG + if (strcasecmp("EMERG", priority) == 0) + map->map_prio = LOG_EMERG; + else +#endif +#ifdef LOG_ALERT + if (strcasecmp("ALERT", priority) == 0) + map->map_prio = LOG_ALERT; + else +#endif +#ifdef LOG_CRIT + if (strcasecmp("CRIT", priority) == 0) + map->map_prio = LOG_CRIT; + else +#endif +#ifdef LOG_ERR + if (strcasecmp("ERR", priority) == 0) + map->map_prio = LOG_ERR; + else +#endif +#ifdef LOG_WARNING + if (strcasecmp("WARNING", priority) == 0) + map->map_prio = LOG_WARNING; + else +#endif +#ifdef LOG_NOTICE + if (strcasecmp("NOTICE", priority) == 0) + map->map_prio = LOG_NOTICE; + else +#endif +#ifdef LOG_INFO + if (strcasecmp("INFO", priority) == 0) + map->map_prio = LOG_INFO; + else +#endif +#ifdef LOG_DEBUG + if (strcasecmp("DEBUG", priority) == 0) + map->map_prio = LOG_DEBUG; + else +#endif + { + syserr("syslog_map_parseargs: Unknown priority %s\n", + 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)) + printf("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 ""; +} + +#endif /* _FFR_MAP_SYSLOG */ + /* +** HESIOD Modules +*/ + +#ifdef HESIOD + +bool +hes_map_open(map, mode) + MAP *map; + int mode; +{ + if (tTd(38, 2)) + printf("hes_map_open(%s, %s, %d)\n", + map->map_mname, map->map_file, mode); + + if (mode != O_RDONLY) + { + /* issue a pseudo-error message */ +#ifdef ENOSYS + errno = ENOSYS; +#else +# ifdef EFTYPE + errno = EFTYPE; +# else + errno = ENXIO; +# endif +#endif + return FALSE; + } + +#ifdef HESIOD_INIT + if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0) + return TRUE; + + if (!bitset(MF_OPTIONAL, map->map_mflags)) + syserr("421 cannot initialize Hesiod map (%s)", + errstring(errno)); + return FALSE; +#else + 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("421 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)) + printf("hes_map_lookup(%s, %s)\n", map->map_file, name); + + if (name[0] == '\\') + { + char *np; + int nl; + char nbuf[MAXNAME]; + + nl = strlen(name); + if (nl < sizeof nbuf - 1) + np = nbuf; + else + np = xalloc(strlen(name) + 2); + np[0] = '\\'; + strcpy(&np[1], name); +#ifdef HESIOD_INIT + hp = hesiod_resolve(HesiodContext, np, map->map_file); +#else + hp = hes_resolve(np, map->map_file); +#endif /* HESIOD_INIT */ + if (np != nbuf) + free(np); + } + else + { +#ifdef HESIOD_INIT + hp = hesiod_resolve(HesiodContext, name, map->map_file); +#else + hp = hes_resolve(name, map->map_file); +#endif /* HESIOD_INIT */ + } +#ifdef HESIOD_INIT + if (hp == NULL) + return NULL; + if (*hp == NULL) + { + hesiod_free_list(HesiodContext, hp); + switch (errno) + { + case ENOENT: + *statp = EX_NOTFOUND; + break; + case ECONNREFUSED: + case EMSGSIZE: + *statp = EX_TEMPFAIL; + break; + case ENOMEM: + default: + *statp = EX_UNAVAILABLE; + break; + } + return NULL; + } +#else + 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); +} + +#endif + /* +** NeXT NETINFO Modules +*/ + +#if NETINFO + +# define NETINFO_DEFAULT_DIR "/aliases" +# define NETINFO_DEFAULT_PROPERTY "members" + +extern char *ni_propval __P((char *, char *, char *, char *, int)); + + +/* +** NI_MAP_OPEN -- open NetInfo Aliases +*/ + +bool +ni_map_open(map, mode) + MAP *map; + int mode; +{ + char *p; + + if (tTd(38, 2)) + printf("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' && bitset(MF_ALIAS, 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)) + printf("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; + + if (bitset(MF_MATCHONLY, map->map_mflags)) + res = map_rewrite(map, name, strlen(name), NULL); + else + res = map_rewrite(map, propval, strlen(propval), av); + free(propval); + return res; +} + + +bool +ni_getcanonname(name, hbsize, statp) + char *name; + int hbsize; + int *statp; +{ + char *vptr; + char *ptr; + char nbuf[MAXNAME + 1]; + + if (tTd(38, 20)) + printf("ni_getcanonname(%s)\n", name); + + if (strlen(name) >= sizeof nbuf) + { + *statp = EX_UNAVAILABLE; + return FALSE; + } + (void) strcpy(nbuf, name); + 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 (hbsize >= strlen(vptr)) + { + strcpy(name, vptr); + *statp = EX_OK; + return TRUE; + } + *statp = EX_UNAVAILABLE; + free(vptr); + return FALSE; +} + + +/* +** NI_PROPVAL -- NetInfo property value lookup routine +** +** Parameters: +** keydir -- the NetInfo directory name in which to search +** for the key. +** keyprop -- the name of the property in which to find the +** property we are interested. Defaults to "name". +** keyval -- the value for which we are really searching. +** valprop -- the property name for the value in which we +** are interested. +** sepchar -- if non-nil, this can be multiple-valued, and +** we should return a string separated by this +** character. +** +** Returns: +** NULL -- if: +** 1. the directory is not found +** 2. the property name is not found +** 3. the property contains multiple values +** 4. some error occured +** else -- the value of the lookup. +** +** Example: +** To search for an alias value, use: +** ni_propval("/aliases", "name", aliasname, "members", ',') +** +** Notes: +** Caller should free the return value of ni_proval +*/ + +# include + +# define LOCAL_NETINFO_DOMAIN "." +# define PARENT_NETINFO_DOMAIN ".." +# define MAX_NI_LEVELS 256 + +char * +ni_propval(keydir, keyprop, keyval, valprop, sepchar) + char *keydir; + char *keyprop; + char *keyval; + char *valprop; + int sepchar; +{ + char *propval = NULL; + int i; + int j, alen; + void *ni = NULL; + void *lastni = NULL; + ni_status nis; + ni_id nid; + ni_namelist ninl; + register char *p; + char keybuf[1024]; + + /* + ** Create the full key from the two parts. + ** + ** Note that directory can end with, e.g., "name=" to specify + ** an alternate search property. + */ + + i = strlen(keydir) + strlen(keyval) + 2; + if (keyprop != NULL) + i += strlen(keyprop) + 1; + if (i > sizeof keybuf) + return NULL; + strcpy(keybuf, keydir); + strcat(keybuf, "/"); + if (keyprop != NULL) + { + strcat(keybuf, keyprop); + strcat(keybuf, "="); + } + strcat(keybuf, keyval); + + if (tTd(38, 21)) + printf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n", + keydir, keyprop, keyval, valprop, sepchar, keybuf); + /* + ** If the passed directory and property name are found + ** in one of netinfo domains we need to search (starting + ** from the local domain moving all the way back to the + ** root domain) set propval to the property's value + ** and return it. + */ + + for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++) + { + if (i == 0) + { + nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni); + if (tTd(38, 20)) + printf("ni_open(LOCAL) = %d\n", nis); + } + else + { + if (lastni != NULL) + ni_free(lastni); + lastni = ni; + nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni); + if (tTd(38, 20)) + printf("ni_open(PARENT) = %d\n", nis); + } + + /* + ** Don't bother if we didn't get a handle on a + ** proper domain. This is not necessarily an error. + ** We would get a positive ni_status if, for instance + ** we never found the directory or property and tried + ** to open the parent of the root domain! + */ + + if (nis != 0) + break; + + /* + ** Find the path to the server information. + */ + + if (ni_pathsearch(ni, &nid, keybuf) != 0) + continue; + + /* + ** Find associated value information. + */ + + if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0) + continue; + + if (tTd(38, 20)) + printf("ni_lookupprop: len=%d\n", ninl.ni_namelist_len); + /* + ** See if we have an acceptable number of values. + */ + + if (ninl.ni_namelist_len <= 0) + continue; + + if (sepchar == '\0' && ninl.ni_namelist_len > 1) + { + ni_namelist_free(&ninl); + continue; + } + + /* + ** Calculate number of bytes needed and build result + */ + + alen = 1; + for (j = 0; j < ninl.ni_namelist_len; j++) + alen += strlen(ninl.ni_namelist_val[j]) + 1; + propval = p = xalloc(alen); + for (j = 0; j < ninl.ni_namelist_len; j++) + { + strcpy(p, ninl.ni_namelist_val[j]); + p += strlen(p); + *p++ = sepchar; + } + *--p = '\0'; + + ni_namelist_free(&ninl); + } + + /* + ** Clean up. + */ + + if (ni != NULL) + ni_free(ni); + if (lastni != NULL && ni != lastni) + ni_free(lastni); + if (tTd(38, 20)) + printf("ni_propval returns: '%s'\n", propval); + + return propval; +} + +#endif + /* +** 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; +{ + int sff; + int i; + + if (tTd(38, 2)) + printf("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 (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) + sff |= SFF_NOWLINK; + if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) + sff |= SFF_SAFEDIRPATH; + if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName, + sff, S_IRUSR, NULL)) != 0) + { + /* cannot open this map */ + if (tTd(38, 2)) + printf("\tunsafe map file: %d\n", i); + 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)) + { + printf("text_map_open(%s, %s): delimiter = ", + map->map_mname, map->map_file); + if (map->map_coldelim == '\0') + printf("(white space)\n"); + else + printf("%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; + FILE *f; + char delim; + int key_idx; + bool found_it; + int sff = map->map_sff; + char search_key[MAXNAME + 1]; + char linebuf[MAXLINE]; + char buf[MAXNAME + 1]; + extern char *get_column __P((char *, int, char, char *, int)); + + found_it = FALSE; + if (tTd(38, 20)) + printf("text_map_lookup(%s, %s)\n", map->map_mname, name); + + buflen = strlen(name); + if (buflen > sizeof search_key - 1) + buflen = sizeof search_key - 1; + bcopy(name, search_key, 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 (fgets(linebuf, MAXLINE, f) != 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 && strcasecmp(search_key, p) == 0) + { + found_it = TRUE; + break; + } + } + fclose(f); + if (!found_it) + { + *statp = EX_NOTFOUND; + return NULL; + } + vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf); + 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 +*/ + +bool +text_getcanonname(name, hbsize, statp) + char *name; + int hbsize; + int *statp; +{ + bool found; + FILE *f; + char linebuf[MAXLINE]; + char cbuf[MAXNAME + 1]; + char nbuf[MAXNAME + 1]; + + if (tTd(38, 20)) + printf("text_getcanonname(%s)\n", name); + + if (strlen(name) >= (SIZE_T) sizeof nbuf) + { + *statp = EX_UNAVAILABLE; + return FALSE; + } + (void) strcpy(nbuf, name); + shorten_hostname(nbuf); + + f = fopen(HostsFile, "r"); + if (f == NULL) + { + *statp = EX_UNAVAILABLE; + return FALSE; + } + found = FALSE; + while (!found && fgets(linebuf, MAXLINE, f) != NULL) + { + char *p = strpbrk(linebuf, "#\n"); + + if (p != NULL) + *p = '\0'; + if (linebuf[0] != '\0') + found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf); + } + fclose(f); + if (!found) + { + *statp = EX_NOHOST; + return FALSE; + } + + if ((SIZE_T) hbsize >= strlen(cbuf)) + { + strcpy(name, cbuf); + *statp = EX_OK; + return TRUE; + } + *statp = EX_UNAVAILABLE; + return FALSE; +} + /* +** 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)) + printf("stab_lookup(%s, %s)\n", + map->map_mname, name); + + s = stab(name, ST_ALIAS, ST_FIND); + if (s != NULL) + return (s->s_alias); + return (NULL); +} + + +/* +** 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; +{ + FILE *af; + int sff; + struct stat st; + + if (tTd(38, 2)) + printf("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 (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) + sff |= SFF_NOWLINK; + if (!bitset(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(fileno(af), &st) >= 0) + map->map_mtime = st.st_mtime; + fclose(af); + + 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)) + printf("impl_map_lookup(%s, %s)\n", + map->map_mname, name); + +#ifdef NEWDB + if (bitset(MF_IMPL_HASH, map->map_mflags)) + return db_map_lookup(map, name, av, pstat); +#endif +#ifdef NDBM + if (bitset(MF_IMPL_NDBM, map->map_mflags)) + return ndbm_map_lookup(map, name, av, pstat); +#endif + 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)) + printf("impl_map_store(%s, %s, %s)\n", + map->map_mname, lhs, rhs); +#ifdef NEWDB + if (bitset(MF_IMPL_HASH, map->map_mflags)) + db_map_store(map, lhs, rhs); +#endif +#ifdef NDBM + if (bitset(MF_IMPL_NDBM, map->map_mflags)) + ndbm_map_store(map, lhs, rhs); +#endif + 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)) + printf("impl_map_open(%s, %s, %d)\n", + map->map_mname, map->map_file, mode); + + mode &= O_ACCMODE; +#ifdef 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 + return TRUE; + } + else + map->map_mflags &= ~MF_IMPL_HASH; +#endif +#ifdef NDBM + map->map_mflags |= MF_IMPL_NDBM; + if (ndbm_map_open(map, mode)) + { + return TRUE; + } + else + map->map_mflags &= ~MF_IMPL_NDBM; +#endif + +#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 + if (mode != O_RDONLY) + usrerr("Cannot rebuild aliases: no database format defined"); +#endif + + 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)) + printf("impl_map_close(%s, %s, %lx)\n", + map->map_mname, map->map_file, map->map_mflags); +#ifdef NEWDB + if (bitset(MF_IMPL_HASH, map->map_mflags)) + { + db_map_close(map); + map->map_mflags &= ~MF_IMPL_HASH; + } +#endif + +#ifdef NDBM + if (bitset(MF_IMPL_NDBM, map->map_mflags)) + { + ndbm_map_close(map); + map->map_mflags &= ~MF_IMPL_NDBM; + } +#endif +} + /* +** 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)) + printf("user_map_open(%s, %d)\n", + map->map_mname, mode); + + mode &= O_ACCMODE; + if (mode != O_RDONLY) + { + /* issue a pseudo-error message */ +#ifdef ENOSYS + errno = ENOSYS; +#else +# ifdef EFTYPE + errno = EFTYPE; +# else + errno = ENXIO; +# endif +#endif + return FALSE; + } + if (map->map_valcolnm == NULL) + /* nothing */ ; + else if (strcasecmp(map->map_valcolnm, "name") == 0) + map->map_valcolno = 1; + else if (strcasecmp(map->map_valcolnm, "passwd") == 0) + map->map_valcolno = 2; + else if (strcasecmp(map->map_valcolnm, "uid") == 0) + map->map_valcolno = 3; + else if (strcasecmp(map->map_valcolnm, "gid") == 0) + map->map_valcolno = 4; + else if (strcasecmp(map->map_valcolnm, "gecos") == 0) + map->map_valcolno = 5; + else if (strcasecmp(map->map_valcolnm, "dir") == 0) + map->map_valcolno = 6; + else if (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; +{ + struct passwd *pw; + auto bool fuzzy; + + if (tTd(38, 20)) + printf("user_map_lookup(%s, %s)\n", + map->map_mname, key); + + pw = finduser(key, &fuzzy); + if (pw == NULL) + 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 = pw->pw_name; + break; + + case 2: + rwval = pw->pw_passwd; + break; + + case 3: + snprintf(buf, sizeof buf, "%d", pw->pw_uid); + rwval = buf; + break; + + case 4: + snprintf(buf, sizeof buf, "%d", pw->pw_gid); + rwval = buf; + break; + + case 5: + rwval = pw->pw_gecos; + break; + + case 6: + rwval = pw->pw_dir; + break; + + case 7: + rwval = pw->pw_shell; + break; + } + 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; + register char *p; + int fd; + auto pid_t pid; + char *rval; + int stat; + char *argv[MAXPV + 1]; + char buf[MAXLINE]; + + if (tTd(38, 20)) + printf("prog_map_lookup(%s, %s) %s\n", + map->map_mname, name, map->map_file); + + i = 0; + argv[i++] = map->map_file; + if (map->map_rebuild != NULL) + { + snprintf(buf, sizeof buf, "%s", map->map_rebuild); + 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)) + { + printf("prog_open:"); + for (i = 0; argv[i] != NULL; i++) + printf(" %s", argv[i]); + printf("\n"); + } + (void) blocksignal(SIGCHLD); + pid = prog_open(argv, &fd, CurEnv); + if (pid < 0) + { + if (!bitset(MF_OPTIONAL, map->map_mflags)) + syserr("prog_map_lookup(%s) failed (%s) -- closing", + map->map_mname, errstring(errno)); + else if (tTd(38, 9)) + printf("prog_map_lookup(%s) failed (%s) -- closing", + map->map_mname, 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\n", + map->map_mname, errstring(errno)); + rval = NULL; + } + else if (i == 0) + { + if (tTd(38, 20)) + printf("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), NULL); + + /* now flush any additional output */ + while ((i = read(fd, buf, sizeof buf)) > 0) + continue; + } + + /* wait for the process to terminate */ + close(fd); + stat = waitfor(pid); + (void) releasesignal(SIGCHLD); + + if (stat == -1) + { + syserr("prog_map_lookup(%s): wait error %s\n", + map->map_mname, errstring(errno)); + *statp = EX_SOFTWARE; + rval = NULL; + } + else if (WIFEXITED(stat)) + { + if ((*statp = WEXITSTATUS(stat)) != EX_OK) + rval = NULL; + } + else + { + syserr("prog_map_lookup(%s): child died on signal %d", + map->map_mname, stat); + *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 during startup, including underlying maps. +*/ + +/* +** SEQ_MAP_PARSE -- Sequenced map parsing +*/ + +bool +seq_map_parse(map, ap) + MAP *map; + char *ap; +{ + int maxmap; + + if (tTd(38, 2)) + printf("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++) + 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)) + printf("switch_map_open(%s, %s, %d)\n", + map->map_mname, map->map_file, mode); + + mode &= O_ACCMODE; + nmaps = switch_map_find(map->map_file, maptype, map->map_return); + if (tTd(38, 19)) + { + printf("\tswitch_map_find => %d\n", nmaps); + for (mapno = 0; mapno < nmaps; mapno++) + printf("\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) snprintf(nbuf, sizeof nbuf, "%s.%s", + 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)) + printf("\tmap_stack[%d] = %s:%s\n", + mapno, s->s_map.map_class->map_cname, + nbuf); + } + } + return TRUE; +} + + +/* +** SEQ_MAP_CLOSE -- close all underlying maps +*/ + +void +seq_map_close(map) + MAP *map; +{ + int mapno; + + if (tTd(38, 9)) + printf("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_class->map_close(mm); + mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE); + } +} + + +/* +** 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)) + printf("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)) + { + 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)) + printf("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, +}; + /* +** REGEX modules +*/ + +#ifdef 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 pattern_buf; /* xalloc it */ + int *regex_subfields; /* move to type MAP */ + char *delim; /* move to type MAP */ +}; + +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\n", 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)) + printf("regex_map_init: mapname '%s', args '%s'\n", + map->map_mname, ap); + + pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; + + p = ap; + + map_p = (struct regex_map *) xnalloc(sizeof(struct regex_map)); + + 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->delim = ++p; + break; + + case 'a': /* map append */ + map->map_app = ++p; + break; + + case 'm': /* matchonly */ + map->map_mflags |= MF_MATCHONLY; + break; + + } + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p != '\0') + *p++ = '\0'; + } + if (tTd(38, 3)) + printf("regex_map_init: compile '%s' 0x%x\n", p, pflags); + + if ((regerr = regcomp(&(map_p->pattern_buf), p, pflags)) != 0) + { + /* Errorhandling */ + char errbuf[ERRBUF_SIZE]; + + regerror(regerr, &(map_p->pattern_buf), errbuf, ERRBUF_SIZE); + syserr("pattern-compile-error: %s\n", errbuf); + free(map_p); + return FALSE; + } + + if (map->map_app != NULL) + map->map_app = newstr(map->map_app); + if (map_p->delim != NULL) + map_p->delim = newstr(map_p->delim); + else + map_p->delim = defdstr; + + if (!bitset(REG_NOSUB, pflags)) + { + /* substring matching */ + int substrings; + int *fields = (int *)xalloc(sizeof(int) * (MAX_MATCH + 1)); + + substrings = map_p->pattern_buf.re_nsub + 1; + + if (tTd(38, 3)) + printf("regex_map_init: nr of substrings %d\n", substrings); + + if (substrings >= MAX_MATCH) + { + syserr("too many substrings, %d max\n", MAX_MATCH); + free(map_p); + 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 + { + /* set default fields */ + int i; + + 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; + + printf("regex_map_init: subfields"); + for (ip = fields; *ip != END_OF_FIELDS; ip++) + printf(" %d", *ip); + printf("\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, NULL); +} + +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; + + printf("regex_map_lookup: key '%s'\n", name); + for (cpp = av; cpp && *cpp; cpp++) + printf("regex_map_lookup: arg '%s'\n", *cpp); + } + + map_p = (struct regex_map *)(map->map_db1); + reg_res = regexec(&(map_p->pattern_buf), name, MAX_MATCH, pmatch, 0); + + 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->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->delim; *sp; sp++) + { + if (dp < ldp) + *dp++ = *sp; + } + } + else + first = FALSE; + + + if (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 */ diff --git a/contrib/sendmail/src/mci.c b/contrib/sendmail/src/mci.c new file mode 100644 index 0000000..efb8cba --- /dev/null +++ b/contrib/sendmail/src/mci.c @@ -0,0 +1,1293 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)mci.c 8.82 (Berkeley) 6/15/98"; +#endif /* not lint */ + +#include "sendmail.h" +#include +#include + +/* +** 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 NOOP +** before we use them; if the NOOP fails we close down the +** connection and reopen it. Note that this means that a +** server SMTP that doesn't support NOOP will hose the +** algorithm -- but that doesn't seem too likely. +** +** 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. +*/ + +MCI **MciCache; /* the open connection cache */ + +extern int mci_generate_persistent_path __P((const char *, char *, int, bool)); +extern bool mci_load_persistent __P((MCI *)); +extern void mci_uncache __P((MCI **, bool)); + /* +** 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)) + printf("mci_cache: caching %lx (%s) in slot %d\n", + (u_long) mci, mci->mci_host, (int)(mcislot - MciCache)); + if (tTd(91, 100)) + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "mci_cache: caching %x (%.100s) in slot %d", + mci, mci->mci_host, 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 **) xalloc(MaxMciCache * sizeof *MciCache); + bzero((char *) MciCache, 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 != savemci) + { + /* connection idle too long -- close it */ + bestmci = &MciCache[i]; + 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. +*/ + +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)) + printf("mci_uncache: uncaching %lx (%s) from slot %d (%d)\n", + (u_long) mci, mci->mci_host, + (int)(mcislot - MciCache), doquit); + if (tTd(91, 100)) + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "mci_uncache: uncaching %x (%.100s) from slot %d (%d)", + mci, mci->mci_host, mcislot - MciCache, doquit); + +#if SMTP + 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); +#ifdef XLA + xla_host_end(mci->mci_host); +#endif + } + else +#endif + { + if (mci->mci_in != NULL) + xfclose(mci->mci_in, "mci_uncache", "mci_in"); + if (mci->mci_out != NULL) + xfclose(mci->mci_out, "mci_uncache", "mci_out"); + 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_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 +*/ + +MCI * +mci_get(host, m) + char *host; + MAILER *m; +{ + register MCI *mci; + register STAB *s; + +#if DAEMON + extern SOCKADDR CurHostAddr; + + /* clear CurHostAddr so we don't get a bogus address with this name */ + bzero(&CurHostAddr, sizeof CurHostAddr); +#endif + + /* 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; + mci->mci_host = s->s_name; + + if (!mci_load_persistent(mci)) + { + if (tTd(42, 2)) + printf("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)) + { + printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n", + host, m->m_name, mci->mci_state, mci->mci_flags, + mci->mci_exitstat, mci->mci_errno); + } + +#if SMTP + if (mci->mci_state == MCIS_OPEN) + { + extern int smtpprobe __P((MCI *)); + + /* 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; + } +# if DAEMON + else + { + /* get peer host address for logging reasons only */ + /* (this should really be in the mci struct) */ + SOCKADDR_LEN_T socklen = sizeof CurHostAddr; + + (void) getpeername(fileno(mci->mci_in), + (struct sockaddr *) &CurHostAddr, &socklen); + } +# endif + } +#endif + if (mci->mci_state == MCIS_CLOSED) + { + time_t now = curtime(); + + /* if this info is stale, ignore it */ + if (now > mci->mci_lastuse + MciInfoTimeout) + { + mci->mci_lastuse = now; + mci->mci_errno = 0; + mci->mci_exitstat = EX_OK; + } + } + + return mci; +} + /* +** 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; + + mci->mci_status = dstat; + if (mci->mci_rstatus != NULL) + free(mci->mci_rstatus); + if (rstat != NULL) + rstat = newstr(rstat); + mci->mci_rstatus = rstat; +} + /* +** MCI_DUMP -- dump the contents of an MCI structure. +** +** Parameters: +** mci -- the MCI structure to dump. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +struct mcifbits +{ + int mcif_bit; /* flag bit */ + char *mcif_name; /* flag name */ +}; +struct mcifbits MciFlags[] = +{ + { MCIF_VALID, "VALID" }, + { MCIF_TEMP, "TEMP" }, + { MCIF_CACHED, "CACHED" }, + { MCIF_ESMTP, "ESMTP" }, + { MCIF_EXPN, "EXPN" }, + { MCIF_SIZE, "SIZE" }, + { MCIF_8BITMIME, "8BITMIME" }, + { MCIF_7BIT, "7BIT" }, + { MCIF_MULTSTAT, "MULTSTAT" }, + { MCIF_INHEADER, "INHEADER" }, + { MCIF_CVT8TO7, "CVT8TO7" }, + { MCIF_DSN, "DSN" }, + { MCIF_8BITOK, "8BITOK" }, + { MCIF_CVT7TO8, "CVT7TO8" }, + { MCIF_INMIME, "INMIME" }, + { 0, NULL } +}; + + +void +mci_dump(mci, logit) + register MCI *mci; + bool logit; +{ + register char *p; + char *sep; + char buf[4000]; + extern char *ctime(); + + sep = logit ? " " : "\n\t"; + p = buf; + snprintf(p, SPACELEFT(buf, p), "MCI@%x: ", mci); + p += strlen(p); + if (mci == NULL) + { + snprintf(p, SPACELEFT(buf, p), "NULL"); + goto printit; + } + snprintf(p, SPACELEFT(buf, p), "flags=%x", mci->mci_flags); + p += strlen(p); + if (mci->mci_flags != 0) + { + struct mcifbits *f; + + *p++ = '<'; + for (f = MciFlags; f->mcif_bit != 0; f++) + { + if (!bitset(f->mcif_bit, mci->mci_flags)) + continue; + snprintf(p, SPACELEFT(buf, p), "%s,", f->mcif_name); + p += strlen(p); + } + p[-1] = '>'; + } + 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, mci->mci_pid, sep); + p += strlen(p); + snprintf(p, SPACELEFT(buf, p), + "maxsize=%ld, phase=%s, mailer=%s,%s", + mci->mci_maxsize, + mci->mci_phase == NULL ? "NULL" : mci->mci_phase, + mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, + sep); + p += strlen(p); + snprintf(p, SPACELEFT(buf, p), + "status=%s, rstatus=%s,%s", + mci->mci_status == NULL ? "NULL" : mci->mci_status, + mci->mci_rstatus == NULL ? "NULL" : mci->mci_rstatus, + sep); + p += strlen(p); + snprintf(p, SPACELEFT(buf, p), + "host=%s, lastuse=%s", + mci->mci_host == NULL ? "NULL" : mci->mci_host, + ctime(&mci->mci_lastuse)); +printit: + if (logit) + sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf); + else + printf("%s\n", buf); +} + /* +** MCI_DUMP_ALL -- print the entire MCI cache +** +** Parameters: +** logit -- if set, log the result instead of printing +** to stdout. +** +** Returns: +** none. +*/ + +void +mci_dump_all(logit) + bool logit; +{ + register int i; + + if (MciCache == NULL) + return; + + for (i = 0; i < MaxMciCache; i++) + mci_dump(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 give +** 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)) + printf("mci_lock_host: NULL mci\n"); + return EX_OK; + } + + if (!SingleThreadDelivery) + return EX_OK; + + return mci_lock_host_statfile(mci); +} + +int +mci_lock_host_statfile(mci) + MCI *mci; +{ + int savedErrno = errno; + int retVal = EX_OK; + char fname[MAXPATHLEN+1]; + + if (HostStatDir == NULL || mci->mci_host == NULL) + return EX_OK; + + if (tTd(56, 2)) + printf("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)) + printf("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(fileno(mci->mci_statfile), fname, "", LOCK_EX|LOCK_NB)) + { + if (tTd(56, 2)) + printf("mci_lock_host: couldn't get lock on %s\n", + fname); + fclose(mci->mci_statfile); + mci->mci_statfile = NULL; + retVal = EX_TEMPFAIL; + goto cleanup; + } + + if (tTd(56, 12) && mci->mci_statfile != NULL) + printf("mci_lock_host: Sanity check -- lock is good\n"); + +cleanup: + errno = savedErrno; + 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 saveErrno = errno; + + if (mci == NULL) + { + if (tTd(56, 1)) + printf("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)) + printf("mci_unlock_host: stat file already locked\n"); + } + else + { + if (tTd(56, 2)) + printf("mci_unlock_host: store prior to unlock\n"); + + mci_store_persistent(mci); + } + + if (mci->mci_statfile != NULL) + { + fclose(mci->mci_statfile); + mci->mci_statfile = NULL; + } + + errno = saveErrno; +} + /* +** 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 +*/ + +bool +mci_load_persistent(mci) + MCI *mci; +{ + int saveErrno = errno; + bool locked = TRUE; + FILE *fp; + char fname[MAXPATHLEN+1]; + + if (mci == NULL) + { + if (tTd(56, 1)) + printf("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)) + printf("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)) + printf("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)) + printf("mci_load_persistent: open(%s): %s\n", + fname, errstring(errno)); + goto cleanup; + } + + FileName = fname; + locked = lockfile(fileno(fp), fname, "", LOCK_SH|LOCK_NB); + (void) mci_read_persistent(fp, mci); + FileName = NULL; + if (locked) + lockfile(fileno(fp), fname, "", LOCK_UN); + fclose(fp); + +cleanup: + errno = saveErrno; + 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. +*/ + +int +mci_read_persistent(fp, mci) + FILE *fp; + register MCI *mci; +{ + int ver; + register char *p; + int saveLineNumber = LineNumber; + char buf[MAXLINE]; + + if (fp == NULL) + syserr("mci_read_persistent: NULL fp"); + if (mci == NULL) + syserr("mci_read_persistent: NULL mci"); + if (tTd(56, 93)) + { + printf("mci_read_persistent: fp=%lx, mci=", (u_long) fp); + mci_dump(mci, FALSE); + } + + mci->mci_status = NULL; + if (mci->mci_rstatus != NULL) + free(mci->mci_rstatus); + mci->mci_rstatus = NULL; + + rewind(fp); + ver = -1; + LineNumber = 0; + while (fgets(buf, sizeof buf, fp) != 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 */ + 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 (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 saveErrno = errno; + + if (mci == NULL) + { + if (tTd(56, 1)) + printf("mci_store_persistent: NULL mci\n"); + return; + } + + if (HostStatDir == NULL || mci->mci_host == NULL) + return; + + if (tTd(56, 1)) + printf("mci_store_persistent: Storing information for %s\n", + mci->mci_host); + + if (mci->mci_statfile == NULL) + { + if (tTd(56, 1)) + printf("mci_store_persistent: no statfile\n"); + return; + } + + rewind(mci->mci_statfile); +#if !NOFTRUNCATE + (void) ftruncate(fileno(mci->mci_statfile), (off_t) 0); +#endif + + fprintf(mci->mci_statfile, "V0\n"); + fprintf(mci->mci_statfile, "E%d\n", mci->mci_errno); + fprintf(mci->mci_statfile, "H%d\n", mci->mci_herrno); + fprintf(mci->mci_statfile, "S%d\n", mci->mci_exitstat); + if (mci->mci_status != NULL) + fprintf(mci->mci_statfile, "D%.80s\n", + denlstring(mci->mci_status, TRUE, FALSE)); + if (mci->mci_rstatus != NULL) + fprintf(mci->mci_statfile, "R%.80s\n", + denlstring(mci->mci_rstatus, TRUE, FALSE)); + fprintf(mci->mci_statfile, "U%ld\n", (long)(mci->mci_lastuse)); + fprintf(mci->mci_statfile, ".\n"); + + fflush(mci->mci_statfile); + + errno = saveErrno; + 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. +*/ + +int +mci_traverse_persistent(action, pathname) + int (*action)(); + char *pathname; +{ + struct stat statbuf; + DIR *d; + int ret; + + if (pathname == NULL) + pathname = HostStatDir; + if (pathname == NULL) + return -1; + + if (tTd(56, 1)) + printf("mci_traverse: pathname is %s\n", pathname); + + ret = stat(pathname, &statbuf); + if (ret < 0) + { + if (tTd(56, 2)) + printf("mci_traverse: Failed to stat %s: %s\n", + pathname, errstring(errno)); + return ret; + } + if (S_ISDIR(statbuf.st_mode)) + { + struct dirent *e; + char *newptr; + char newpath[MAXPATHLEN+1]; + + if ((d = opendir(pathname)) == NULL) + { + if (tTd(56, 2)) + printf("mci_traverse: opendir %s: %s\n", + pathname, errstring(errno)); + return -1; + } + + if (strlen(pathname) >= sizeof newpath - MAXNAMLEN - 3) + { + if (tTd(56, 2)) + printf("mci_traverse: path \"%s\" too long", + pathname); + return -1; + } + strcpy(newpath, pathname); + newptr = newpath + strlen(newpath); + *newptr++ = '/'; + + while ((e = readdir(d)) != NULL) + { + if (e->d_name[0] == '.') + continue; + + strncpy(newptr, e->d_name, + sizeof newpath - (newptr - newpath) - 1); + newpath[sizeof newpath - 1] = '\0'; + + ret = mci_traverse_persistent(action, newpath); + if (ret < 0) + break; + + /* + ** The following appears to be + ** necessary during purges, since + ** we modify the directory structure + */ + + if (action == mci_purge_persistent) + rewinddir(d); + } + + /* purge (or whatever) the directory proper */ + *--newptr = '\0'; + ret = (*action)(newpath, NULL); + 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 - 1) != '/') + start--; + + if (*end == '.') + end--; + + for (scan = start; scan <= end; scan++) + *(hostptr++) = *scan; + + end = start - 2; + } while (*end == '.'); + + *hostptr = '\0'; + + /* + ** Do something with the file containing the persistent + ** information. + */ + ret = (*action)(pathname, host); + } + + return ret; +} + /* +** MCI_PRINT_PERSISTENT -- print persisten 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 int initflag = FALSE; + FILE *fp; + int width = Verbose ? 78 : 25; + bool locked; + MCI mcib; + + /* skip directories */ + if (hostname == NULL) + return 0; + + if (!initflag) + { + initflag = TRUE; + printf(" -------------- Hostname --------------- How long ago ---------Results---------\n"); + } + + fp = safefopen(pathname, O_RDWR, FileMode, + SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); + + if (fp == NULL) + { + if (tTd(56, 1)) + printf("mci_print_persistent: cannot open %s: %s\n", + pathname, errstring(errno)); + return 0; + } + + FileName = pathname; + bzero(&mcib, sizeof mcib); + if (mci_read_persistent(fp, &mcib) < 0) + { + syserr("%s: could not read status file", pathname); + fclose(fp); + FileName = NULL; + return 0; + } + + locked = !lockfile(fileno(fp), pathname, "", LOCK_EX|LOCK_NB); + fclose(fp); + FileName = NULL; + + printf("%c%-39s %12s ", + locked ? '*' : ' ', hostname, + pintvl(curtime() - mcib.mci_lastuse, TRUE)); + if (mcib.mci_rstatus != NULL) + printf("%.*s\n", width, mcib.mci_rstatus); + else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0) + printf("Deferred: %.*s\n", width - 10, errstring(mcib.mci_errno)); + else if (mcib.mci_exitstat != 0) + { + int i = mcib.mci_exitstat - EX__BASE; + extern int N_SysEx; + extern char *SysExMsg[]; + + if (i < 0 || i >= N_SysEx) + { + char buf[80]; + + snprintf(buf, sizeof buf, "Unknown mailer error %d", + mcib.mci_exitstat); + printf("%.*s\n", width, buf); + } + else + printf("%.*s\n", width, &(SysExMsg[i])[5]); + } + else if (mcib.mci_errno == 0) + printf("OK\n"); + else + printf("OK: %.*s\n", width - 4, 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 +*/ + +int +mci_purge_persistent(pathname, hostname) + char *pathname; + char *hostname; +{ + char *end = pathname + strlen(pathname) - 1; + + if (tTd(56, 1)) + printf("mci_purge_persistent: purging %s\n", pathname); + + if (hostname != NULL) + { + /* remove the file */ + if (unlink(pathname) < 0) + { + if (tTd(56, 2)) + printf("mci_purge_persistent: failed to unlink %s: %s\n", + pathname, errstring(errno)); + } + } + else + { + /* remove the directory */ + if (*end != '.') + return 0; + + if (tTd(56, 1)) + printf("mci_purge_persistent: dpurge %s\n", pathname); + + if (rmdir(pathname) < 0) + { + if (tTd(56, 2)) + printf("mci_purge_persistent: rmdir %s: %s\n", + pathname, errstring(errno)); + } + + } + + return 0; +} + /* +** MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname +** +** Given `host', convert from a.b.c to $QueueDir/.hoststat/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 +*/ + +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]; + + /* + ** 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)) + printf("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] == '[') + strcpy(t_host, host + 1); + else + strcpy(t_host, 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] == '[' && inet_addr(t_host) == INADDR_NONE) + 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; + + strcpy(path, HostStatDir); + 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) + printf("FAILURE %d\n", ret); + else + printf("SUCCESS %s\n", path); + } + + return (ret); +} diff --git a/contrib/sendmail/src/mime.c b/contrib/sendmail/src/mime.c new file mode 100644 index 0000000..11a141e --- /dev/null +++ b/contrib/sendmail/src/mime.c @@ -0,0 +1,1171 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 1994, 1996-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +# include "sendmail.h" +# include + +#ifndef lint +static char sccsid[] = "@(#)mime.c 8.66 (Berkeley) 5/19/98"; +#endif /* not lint */ + +/* +** 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. +*/ + +#if MIME8TO7 + +/* character set for hex and base64 encoding */ +char Base16Code[] = "0123456789ABCDEF"; +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" +}; + +bool MapNLtoCRLF; + +extern int mimeboundary __P((char *, char **)); + /* +** 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. +** +** 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 +*/ + +struct args +{ + char *field; /* name of field */ + char *value; /* value of that field */ +}; + +int +mime8to7(mci, header, e, boundaries, flags) + register MCI *mci; + HDR *header; + register ENVELOPE *e; + char **boundaries; + int flags; +{ + 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 u_char MimeTokenTab[256]; + extern int mime_getchar __P((FILE *, char **, int *)); + extern int mime_getchar_crlf __P((FILE *, char **, int *)); + + if (tTd(43, 1)) + { + printf("mime8to7: flags = %x, boundaries =", flags); + if (boundaries[0] == NULL) + printf(" "); + else + { + for (i = 0; boundaries[i] != NULL; i++) + printf(" %s", boundaries[i]); + } + printf("\n"); + } + MapNLtoCRLF = TRUE; + p = hvalue("Content-Transfer-Encoding", header); + if (p == NULL || + (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, + MimeTokenTab)) == NULL || + pvp[0] == NULL) + { + cte = NULL; + } + else + { + cataddr(pvp, NULL, buf, sizeof buf, '\0'); + cte = newstr(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)) != NULL && + pvp[0] != NULL) + { + if (tTd(43, 40)) + { + for (i = 0; pvp[i] != NULL; i++) + printf("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; + + /* extract field name */ + argv[argc].field = *pvp++; + + /* see if there is a value */ + if (*pvp != NULL && strcmp(*pvp, "=") == 0 && + (*++pvp == NULL || strcmp(*pvp, ";") != 0)) + { + argv[argc].value = *pvp; + argc++; + } + } + } + + /* check for disaster cases */ + if (type == NULL) + type = "-none-"; + if (subtype == NULL) + subtype = "-none-"; + + /* don't propogate 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. + */ + + 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 + if (wordinclass(buf, 'q') || wordinclass(type, 'q')) + use_qp = TRUE; + + /* + ** Multipart requires special processing. + ** + ** Do a recursive descent into the message. + */ + + if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags)) + { + int blen; + + if (strcasecmp(subtype, "digest") == 0) + flags |= M87F_DIGEST; + + for (i = 0; i < argc; i++) + { + if (strcasecmp(argv[i].field, "boundary") == 0) + break; + } + if (i >= argc || argv[i].value == NULL) + { + syserr("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].value; + stripquotes(p); + } + blen = strlen(p); + if (blen > sizeof bbuf - 1) + { + syserr("mime8to7: multipart boundary \"%s\" too long", + p); + blen = sizeof bbuf - 1; + + /* avoid bounce loops */ + e->e_flags |= EF_DONT_MIME; + } + strncpy(bbuf, p, blen); + bbuf[blen] = '\0'; + if (tTd(43, 1)) + printf("mime8to7: multipart boundary \"%s\"\n", bbuf); + for (i = 0; i < MAXMIMENESTING; i++) + if (boundaries[i] == NULL) + break; + if (i >= MAXMIMENESTING) + { + syserr("mime8to7: multipart nesting boundary too deep"); + + /* avoid bounce loops */ + e->e_flags |= EF_DONT_MIME; + } + else + { + boundaries[i] = bbuf; + boundaries[i + 1] = NULL; + } + mci->mci_flags |= MCIF_INMIME; + + /* skip the early "comment" prologue */ + putline("", mci); + mci->mci_flags &= ~MCIF_INHEADER; + while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + { + bt = mimeboundary(buf, boundaries); + if (bt != MBT_NOTSEP) + break; + putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT); + if (tTd(43, 99)) + printf(" ...%s", buf); + } + if (feof(e->e_dfp)) + bt = MBT_FINAL; + while (bt != MBT_FINAL) + { + auto HDR *hdr = NULL; + + snprintf(buf, sizeof buf, "--%s", bbuf); + putline(buf, mci); + if (tTd(43, 35)) + printf(" ...%s\n", buf); + collect(e->e_dfp, FALSE, &hdr, e); + if (tTd(43, 101)) + putline("+++after collect", mci); + putheader(mci, hdr, e); + if (tTd(43, 101)) + putline("+++after putheader", mci); + bt = mime8to7(mci, hdr, e, boundaries, flags); + } + snprintf(buf, sizeof buf, "--%s--", bbuf); + putline(buf, mci); + if (tTd(43, 35)) + printf(" ...%s\n", buf); + boundaries[i] = NULL; + mci->mci_flags &= ~MCIF_INMIME; + + /* skip the late "comment" epilogue */ + while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + { + bt = mimeboundary(buf, boundaries); + if (bt != MBT_NOTSEP) + break; + putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT); + if (tTd(43, 99)) + printf(" ...%s", buf); + } + if (feof(e->e_dfp)) + bt = MBT_FINAL; + if (tTd(43, 3)) + printf("\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 (strcasecmp(type, "message") == 0) + { + if (!wordinclass(subtype, 's')) + { + flags |= M87F_NO8BIT; + } + else + { + auto HDR *hdr = NULL; + + putline("", mci); + + mci->mci_flags |= MCIF_INMIME; + collect(e->e_dfp, FALSE, &hdr, e); + if (tTd(43, 101)) + putline("+++after collect", mci); + putheader(mci, hdr, e); + if (tTd(43, 101)) + putline("+++after putheader", mci); + if (hvalue("MIME-Version", hdr) == NULL) + putline("MIME-Version: 1.0", mci); + bt = mime8to7(mci, hdr, e, boundaries, flags); + 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, flags)) + { + /* remember where we were */ + offset = ftell(e->e_dfp); + if (offset == -1) + syserr("mime8to7: cannot ftell on df%s", e->e_id); + + /* do a scan of this body type to count character types */ + while (fgets(buf, sizeof buf, e->e_dfp) != 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 (fseek(e->e_dfp, offset, SEEK_SET) < 0) + syserr("mime8to7: cannot fseek on df%s", e->e_id); + else + 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)) + { + printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n", + (long) sectionhighbits, (long) sectionsize, + cte == NULL ? "[none]" : cte, + type == NULL ? "[none]" : type, + subtype == NULL ? "[none]" : subtype); + } + if (cte != NULL && strcasecmp(cte, "binary") == 0) + sectionsize = sectionhighbits; + linelen = 0; + bp = buf; + if (sectionhighbits == 0) + { + /* no encoding necessary */ + if (cte != NULL) + { + snprintf(buf, sizeof buf, + "Content-Transfer-Encoding: %.200s", cte); + putline(buf, mci); + if (tTd(43, 36)) + printf(" ...%s\n", buf); + } + putline("", mci); + mci->mci_flags &= ~MCIF_INHEADER; + while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + { + bt = mimeboundary(buf, boundaries); + if (bt != MBT_NOTSEP) + break; + putline(buf, mci); + } + if (feof(e->e_dfp)) + bt = MBT_FINAL; + } + else if (!MapNLtoCRLF || + (sectionsize / 8 < sectionhighbits && !use_qp)) + { + /* use base64 encoding */ + int c1, c2; + + if (tTd(43, 36)) + printf(" ...Content-Transfer-Encoding: base64\n"); + putline("Content-Transfer-Encoding: base64", mci); + snprintf(buf, sizeof buf, + "X-MIME-Autoconverted: from 8bit to base64 by %s id %s", + MyHostName, e->e_id); + putline(buf, mci); + putline("", mci); + mci->mci_flags &= ~MCIF_INHEADER; + while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF) + { + if (linelen > 71) + { + *bp = '\0'; + putline(buf, mci); + 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 == 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 == EOF) + { + *bp++ = Base64Code[c1]; + *bp++ = '='; + break; + } + c1 |= (c2 >> 6) & 0x03; + *bp++ = Base64Code[c1]; + *bp++ = Base64Code[c2 & 0x3f]; + } + *bp = '\0'; + putline(buf, mci); + } + else + { + /* use quoted-printable encoding */ + int c1, c2; + int fromstate; + BITMAP 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)) + printf(" ...Content-Transfer-Encoding: quoted-printable\n"); + putline("Content-Transfer-Encoding: quoted-printable", mci); + snprintf(buf, sizeof buf, + "X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s", + MyHostName, e->e_id); + putline(buf, mci); + putline("", mci); + mci->mci_flags &= ~MCIF_INHEADER; + fromstate = 0; + c2 = '\n'; + while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != 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'; + putline(buf, mci); + 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'; + putline(buf, mci); + linelen = fromstate = 0; + bp = buf; + if (c2 == '.') + { + *bp++ = '.'; + linelen++; + } + } + if (bitnset(c1 & 0xff, 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'; + putline(buf, mci); + } + + } + if (tTd(43, 3)) + printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); + return bt; +} + /* +** MIME_GETCHAR -- get a character for MIME processing +** +** Treats boundaries as EOF. +** +** Parameters: +** fp -- the input file. +** boundaries -- the current MIME boundaries. +** btp -- if the return value is EOF, *btp is set to +** the type of the boundary. +** +** Returns: +** The next character in the input stream. +*/ + +int +mime_getchar(fp, boundaries, btp) + register FILE *fp; + char **boundaries; + int *btp; +{ + int c; + static u_char *bp = NULL; + static int buflen = 0; + static bool atbol = TRUE; /* at beginning of line */ + static int bt = MBT_SYNTAX; /* boundary type of next EOF */ + static u_char buf[128]; /* need not be a full line */ + + if (buflen > 0) + { + buflen--; + return *bp++; + } + bp = buf; + buflen = 0; + c = getc(fp); + if (c == '\n') + { + /* might be part of a MIME boundary */ + *bp++ = c; + atbol = TRUE; + c = getc(fp); + if (c == '\n') + { + ungetc(c, fp); + return c; + } + } + if (c != EOF) + *bp++ = c; + else + bt = MBT_FINAL; + if (atbol && c == '-') + { + /* check for a message boundary */ + c = getc(fp); + if (c != '-') + { + if (c != 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 = getc(fp)) != EOF && c != '\n') + { + *bp++ = c; + } + *bp = '\0'; + bt = mimeboundary((char *) &buf[1], boundaries); + switch (bt) + { + case MBT_FINAL: + case MBT_INTERMED: + /* we have a message boundary */ + buflen = 0; + *btp = bt; + return EOF; + } + + atbol = c == '\n'; + if (c != EOF) + *bp++ = c; + } + + buflen = bp - buf - 1; + if (buflen < 0) + { + *btp = bt; + return EOF; + } + bp = buf; + return *bp++; +} + /* +** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF +** +** Parameters: +** fp -- the input file. +** boundaries -- the current MIME boundaries. +** btp -- if the return value is EOF, *btp is set to +** the type of the boundary. +** +** Returns: +** The next character in the input stream. +*/ + +int +mime_getchar_crlf(fp, boundaries, btp) + register FILE *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. +*/ + +int +mimeboundary(line, boundaries) + register char *line; + char **boundaries; +{ + int type = MBT_NOTSEP; + int i; + int savec; + extern int isboundary __P((char *, char **)); + + if (line[0] != '-' || line[1] != '-' || boundaries == NULL) + return MBT_NOTSEP; + i = strlen(line); + if (line[i - 1] == '\n') + i--; + + /* strip off trailing whitespace */ + while (line[i - 1] == ' ' || line[i - 1] == '\t') + i--; + savec = line[i]; + line[i] = '\0'; + + if (tTd(43, 5)) + printf("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)) + printf("%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. +** +*/ + +int +isboundary(line, boundaries) + char *line; + char **boundaries; +{ + register int i; + + for (i = 0; boundaries[i] != NULL; i++) + { + if (strcmp(line, boundaries[i]) == 0) + return i; + } + return -1; +} + +#endif /* MIME8TO7 */ + +#if MIME7TO8 + +/* +** 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 wont 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: +** none. +*/ + +extern int mime_fromqp __P((u_char *, u_char **, int, int)); + +static char index_64[128] = +{ + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -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)]) + + +void +mime7to8(mci, header, e) + register MCI *mci; + HDR *header; + register ENVELOPE *e; +{ + register char *p; + char *cte; + char **pvp; + u_char *fbufp; + char buf[MAXLINE]; + u_char fbuf[MAXLINE + 1]; + char pvpbuf[MAXLINE]; + extern u_char MimeTokenTab[256]; + + p = hvalue("Content-Transfer-Encoding", header); + if (p == NULL || + (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, + MimeTokenTab)) == 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) + { + snprintf(buf, sizeof buf, + "Content-Transfer-Encoding: %s", p); + putline(buf, mci); + } + putline("", mci); + mci->mci_flags &= ~MCIF_INHEADER; + while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + putline(buf, mci); + return; + } + cataddr(pvp, NULL, buf, sizeof buf, '\0'); + cte = newstr(buf); + + mci->mci_flags |= MCIF_INHEADER; + putline("Content-Transfer-Encoding: 8bit", mci); + snprintf(buf, sizeof buf, + "X-MIME-Autoconverted: from %.200s to 8bit by %s id %s", + cte, MyHostName, e->e_id); + putline(buf, mci); + putline("", mci); + 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. + */ + + if (strcasecmp(cte, "base64") == 0) + { + int c1, c2, c3, c4; + + fbufp = fbuf; + while ((c1 = fgetc(e->e_dfp)) != EOF) + { + if (isascii(c1) && isspace(c1)) + continue; + + do + { + c2 = fgetc(e->e_dfp); + } while (isascii(c2) && isspace(c2)); + if (c2 == EOF) + break; + + do + { + c3 = fgetc(e->e_dfp); + } while (isascii(c3) && isspace(c3)); + if (c3 == EOF) + break; + + do + { + c4 = fgetc(e->e_dfp); + } while (isascii(c4) && isspace(c4)); + if (c4 == EOF) + break; + + if (c1 == '=' || c2 == '=') + continue; + c1 = CHAR64(c1); + c2 = CHAR64(c2); + + *fbufp = (c1 << 2) | ((c2 & 0x30) >> 4); + if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE]) + { + if (*--fbufp != '\n' || + (fbufp > fbuf && *--fbufp != '\r')) + fbufp++; + putxline((char *) fbuf, fbufp - fbuf, + mci, PXLF_MAPFROM); + fbufp = fbuf; + } + if (c3 == '=') + continue; + c3 = CHAR64(c3); + *fbufp = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2); + if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE]) + { + if (*--fbufp != '\n' || + (fbufp > fbuf && *--fbufp != '\r')) + fbufp++; + putxline((char *) fbuf, fbufp - fbuf, + mci, PXLF_MAPFROM); + fbufp = fbuf; + } + if (c4 == '=') + continue; + c4 = CHAR64(c4); + *fbufp = ((c3 & 0x03) << 6) | c4; + if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE]) + { + if (*--fbufp != '\n' || + (fbufp > fbuf && *--fbufp != '\r')) + fbufp++; + putxline((char *) fbuf, fbufp - fbuf, + mci, PXLF_MAPFROM); + fbufp = fbuf; + } + } + } + else + { + /* quoted-printable */ + fbufp = fbuf; + while (fgets(buf, sizeof buf, e->e_dfp) != NULL) + { + if (mime_fromqp((u_char *) buf, &fbufp, 0, + &fbuf[MAXLINE] - fbufp) == 0) + continue; + + if (fbufp - fbuf > 0) + putxline((char *) fbuf, fbufp - fbuf - 1, mci, + PXLF_MAPFROM); + fbufp = fbuf; + } + } + + /* force out partial last line */ + if (fbufp > fbuf) + { + *fbufp = '\0'; + putxline((char *) fbuf, fbufp - fbuf, mci, PXLF_MAPFROM); + } + if (tTd(43, 3)) + printf("\t\t\tmime7to8 => %s to 8bit done\n", cte); +} + /* +** The following is based on Borenstein's "codes.c" module, with simplifying +** changes as we do not deal with multipart, and to do the translation in-core, +** with an attempt to prevent overrun of output buffers. +** +** What is needed here are changes to defned this code better against +** 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)]) + +int +mime_fromqp(infile, outfile, state, maxlen) + u_char *infile; + u_char **outfile; + int state; /* Decoding body (0) or header (1) */ + int maxlen; /* Max # of chars allowed in outfile */ +{ + int c1, c2; + int nchar = 0; + + while ((c1 = *infile++) != '\0') + { + if (c1 == '=') + { + if ((c1 = *infile++) == 0) + break; + + if (c1 == '\n' || (c1 = HEXCHAR(c1)) == -1) + { + /* ignore it */ + if (state == 0) + return 0; + } + else + { + do + { + if ((c2 = *infile++) == '\0') + { + c2 = -1; + break; + } + } while ((c2 = HEXCHAR(c2)) == -1); + + if (c2 == -1 || ++nchar > maxlen) + break; + + *(*outfile)++ = c1 << 4 | c2; + } + } + else + { + if (state == 1 && c1 == '_') + c1 = ' '; + + if (++nchar > maxlen) + break; + + *(*outfile)++ = c1; + + if (c1 == '\n') + break; + } + } + *(*outfile)++ = '\0'; + return 1; +} + + +#endif /* MIME7TO8 */ diff --git a/contrib/sendmail/src/newaliases.1 b/contrib/sendmail/src/newaliases.1 new file mode 100644 index 0000000..acf3245 --- /dev/null +++ b/contrib/sendmail/src/newaliases.1 @@ -0,0 +1,47 @@ +.\" Copyright (c) 1998 Sendmail, Inc. 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. +.\" +.\" +.\" @(#)newaliases.1 8.10 (Berkeley) 5/19/98 +.\" +.Dd May 19, 1998 +.Dt NEWALIASES 1 +.Os BSD 4 +.Sh NAME +.Nm newaliases +.Nd rebuild the data base for the mail aliases file +.Sh SYNOPSIS +.Nm newaliases +.Sh DESCRIPTION +.Nm Newaliases +rebuilds the random access data base for the mail aliases file +.Pa /etc/aliases . +It must be run each time this file is changed in order +for the change to take effect. +.Pp +.Nm Newaliases +is identical to +.Dq Li "sendmail -bi" . +.Pp +The +.Nm newaliases +utility exits 0 on success, and >0 if an error occurs. +.Sh FILES +.Bl -tag -width /etc/aliases -compact +.It Pa /etc/aliases +The mail aliases file +.El +.Sh SEE ALSO +.Xr aliases 5 , +.Xr sendmail 8 +.Sh HISTORY +The +.Nm newaliases +command appeared in +.Bx 4.0 . diff --git a/contrib/sendmail/src/parseaddr.c b/contrib/sendmail/src/parseaddr.c new file mode 100644 index 0000000..c3c89b4 --- /dev/null +++ b/contrib/sendmail/src/parseaddr.c @@ -0,0 +1,2547 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)parseaddr.c 8.153 (Berkeley) 6/24/98"; +#endif /* not lint */ + +# include "sendmail.h" + +/* +** 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, a header 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. +** +** Returns: +** A pointer to the address descriptor header (`a' if +** `a' is non-NULL). +** NULL on error. +** +** Side Effects: +** none +*/ + +/* following delimiters are inherent to the internal algorithms */ +# define DELIMCHARS "()<>,;\r\n" /* default word delimiters */ + +ADDRESS * +parseaddr(addr, a, flags, delim, delimptr, e) + char *addr; + register ADDRESS *a; + int flags; + int delim; + char **delimptr; + register ENVELOPE *e; +{ + register char **pvp; + auto char *delimptrbuf; + bool queueup; + char pvpbuf[PSBUFSIZE]; + extern ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *)); + extern bool invalidaddr __P((char *, char *)); + extern void allocaddr __P((ADDRESS *, int, char *)); + + /* + ** Initialize and prescan address. + */ + + e->e_to = addr; + if (tTd(20, 1)) + printf("\n--parseaddr(%s)\n", addr); + + if (delimptr == NULL) + delimptr = &delimptrbuf; + + pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL); + if (pvp == NULL) + { + if (tTd(20, 1)) + printf("parseaddr-->NULL\n"); + return (NULL); + } + + if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr)) + { + if (tTd(20, 1)) + printf("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 = newstr(addr); + if (savec != '\0') + **delimptr = savec; + } + + /* + ** Apply rewriting rules. + ** Ruleset 0 does basic parsing. It must resolve. + */ + + queueup = FALSE; + if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) + queueup = TRUE; + if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL) + queueup = TRUE; + + + /* + ** Build canonical address from pvp. + */ + + a = buildaddr(pvp, a, flags, e); + + /* + ** Make local copies of the host & user and then + ** transport them out. + */ + + allocaddr(a, flags, addr); + if (bitset(QBADADDR, a->q_flags)) + return a; + + /* + ** If there was a parsing failure, mark it for queueing. + */ + + if (queueup && OpMode != MD_INITALIAS) + { + char *msg = "Transient parse error -- message queued for future delivery"; + + if (e->e_sendmode == SM_DEFER) + msg = "Deferring message until queue run"; + if (tTd(20, 1)) + printf("parseaddr: queuing message\n"); + message(msg); + if (e->e_message == NULL && e->e_sendmode != SM_DEFER) + e->e_message = newstr(msg); + a->q_flags |= QQUEUEUP; + a->q_status = "4.4.3"; + } + + /* + ** Compute return value. + */ + + if (tTd(20, 1)) + { + printf("parseaddr-->"); + printaddr(a, FALSE); + } + + return (a); +} + /* +** INVALIDADDR -- check for address containing meta-characters +** +** Parameters: +** addr -- the address to check. +** +** Returns: +** TRUE -- if the address has any "wierd" characters +** FALSE -- otherwise. +*/ + +bool +invalidaddr(addr, delimptr) + register char *addr; + char *delimptr; +{ + char savedelim = '\0'; + + if (delimptr != NULL) + { + savedelim = *delimptr; + if (savedelim != '\0') + *delimptr = '\0'; + } + if (strlen(addr) > TOBUFSIZE - 2) + { + usrerr("553 Address too long (%d bytes max)", TOBUFSIZE - 2); + goto failure; + } + for (; *addr != '\0'; addr++) + { + if ((*addr & 0340) == 0200) + break; + } + if (*addr == '\0') + { + if (delimptr != NULL && savedelim != '\0') + *delimptr = savedelim; + return FALSE; + } + setstat(EX_USAGE); + usrerr("553 Address contained invalid control characters"); +failure: + if (delimptr != NULL && savedelim != '\0') + *delimptr = savedelim; + return TRUE; +} + /* +** 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. +** +** Returns: +** none. +** +** Side Effects: +** Copies portions of a into local buffers as requested. +*/ + +void +allocaddr(a, flags, paddr) + register ADDRESS *a; + int flags; + char *paddr; +{ + if (tTd(24, 4)) + printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr); + + 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 = newstr(a->q_host); + if (a->q_user != a->q_paddr) + a->q_user = newstr(a->q_user); + } + + if (a->q_paddr == NULL) + a->q_paddr = a->q_user; +} + /* +** PRESCAN -- Prescan name and make it canonical +** +** Scans a name and turns it into a set of tokens. This process +** deletes blanks and comments (in parentheses). +** +** 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. +** +** 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 }, +}; + +/* token type table -- it gets modified with $o characters */ +static u_char TokTypeTab[256] = +{ + /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, + /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* sp ! " # $ % & ' ( ) * + , - . / */ + SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM, + /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* @ A B C D E F G H I J K L M N O */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* P Q R S T U V W X Y Z [ \ ] ^ _ */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* ` a b c d e f g h i j k l m n o */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* p q r s t u v w x y z { | } ~ del */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + + /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ + OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, + /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ + OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, + /* sp ! " # $ % & ' ( ) * + , - . / */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* @ A B C D E F G H I J K L M N O */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* P Q R S T U V W X Y Z [ \ ] ^ _ */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* ` a b c d e f g h i j k l m n o */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* p q r s t u v w x y z { | } ~ del */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, +}; + +/* token type table for MIME parsing */ +u_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, ATM,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,ILL, +}; + + +# define NOCHAR -1 /* signal nothing in lookahead token */ + +char ** +prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) + char *addr; + int delim; + char pvpbuf[]; + int pvpbsize; + char **delimptr; + u_char *toktab; +{ + 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 char firsttime = TRUE; + extern int errno; + + 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); + strcat(obuf, DELIMCHARS); + for (p = obuf; *p != '\0'; p++) + { + if (TokTypeTab[*p & 0xff] == ATM) + TokTypeTab[*p & 0xff] = OPR; + } + } + if (toktab == NULL) + toktab = TokTypeTab; + + /* 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)) + { + printf("prescan: "); + xputs(p); + (void) putchar('\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]) + { + usrerr("553 Address too long"); + if (strlen(addr) > (SIZE_T) MAXNAME) + addr[MAXNAME] = '\0'; + returnnull: + if (delimptr != NULL) + *delimptr = p; + CurEnv->e_to = saveto; + return (NULL); + } + + /* squirrel it away */ + *q++ = c; + } + + /* read a new input character */ + c = *p++; + if (c == '\0') + { + /* diagnose and patch up bad syntax */ + if (state == QST) + { + usrerr("653 Unbalanced '\"'"); + c = '"'; + } + else if (cmntcnt > 0) + { + usrerr("653 Unbalanced '('"); + c = ')'; + } + else if (anglecnt > 0) + { + c = '>'; + usrerr("653 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) + { + usrerr("653 Unbalanced '<'"); + c = '>'; + p--; + } + } + + if (tTd(22, 101)) + printf("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) + { + *q++ = '\\'; + continue; + } + } + + if (c == '\\') + { + bslashmode = TRUE; + } + else if (state == QST) + { + /* do nothing, just avoid next clauses */ + } + else if (c == '(') + { + cmntcnt++; + c = NOCHAR; + } + else if (c == ')') + { + if (cmntcnt <= 0) + { + usrerr("653 Unbalanced ')'"); + c = NOCHAR; + } + else + cmntcnt--; + } + else if (cmntcnt > 0) + c = NOCHAR; + else if (c == '<') + { + char *q = p; + + anglecnt++; + while (isascii(*q) && isspace(*q)) + q++; + if (*q == '@') + route_syntax = TRUE; + } + else if (c == '>') + { + if (anglecnt <= 0) + { + usrerr("653 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)) + printf("ns=%02o\n", newstate); + state = newstate & TYPE; + if (state == ILL) + { + if (isascii(c) && isprint(c)) + usrerr("653 Illegal character %c", c); + else + usrerr("653 Illegal character 0x%02x", c); + } + if (bitset(M, newstate)) + c = NOCHAR; + if (bitset(B, newstate)) + break; + } + + /* new token */ + if (tok != q) + { + *q++ = '\0'; + if (tTd(22, 36)) + { + printf("tok="); + xputs(tok); + (void) putchar('\n'); + } + if (avp >= &av[MAXATOM]) + { + usrerr("553 prescan: too many tokens"); + goto returnnull; + } + if (q - tok > MAXNAME) + { + usrerr("553 prescan: token too long"); + goto returnnull; + } + *avp++ = tok; + } + } while (c != '\0' && (c != delim || anglecnt > 0)); + *avp = NULL; + p--; + if (delimptr != NULL) + *delimptr = p; + if (tTd(22, 12)) + { + printf("prescan==>"); + printav(av); + } + CurEnv->e_to = saveto; + if (av[0] == NULL) + { + if (tTd(22, 1)) + printf("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. +** +** Returns: +** A status code. If EX_TEMPFAIL, higher level code should +** attempt recovery. +** +** Side Effects: +** pvp is modified. +*/ + +struct match +{ + char **first; /* first token matched */ + char **last; /* last token matched */ + char **pattern; /* pointer to pattern */ +}; + +# define MAXMATCH 9 /* max params per rewrite */ + + +int +rewrite(pvp, ruleset, reclevel, e) + char **pvp; + int ruleset; + int reclevel; + register ENVELOPE *e; +{ + register char *ap; /* address pointer */ + register char *rp; /* rewrite pointer */ + 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]; + extern int callsubr __P((char**, int, ENVELOPE *)); + extern int sm_strcasecmp __P((char *, char *)); + + if (OpMode == MD_TEST || tTd(21, 1)) + { + printf("rewrite: ruleset %3d input:", ruleset); + printav(pvp); + } + if (ruleset < 0 || ruleset >= MAXRWSETS) + { + syserr("554 rewrite: illegal ruleset number %d", ruleset); + return EX_CONFIG; + } + if (reclevel++ > MaxRuleRecursion) + { + syserr("rewrite: excessive recursion (max %d), ruleset %d", + MaxRuleRecursion, ruleset); + return EX_CONFIG; + } + if (pvp == NULL) + 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 stat; + + /* if already canonical, quit now */ + if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET) + break; + + if (tTd(21, 12)) + { + printf("-----trying rule:"); + printav(rwr->r_lhs); + } + + /* try to match on this rule */ + mlp = mlist; + rvp = rwr->r_lhs; + avp = pvp; + if (++loopcount > 100) + { + syserr("554 Infinite loop in ruleset %d, rule %d", + ruleset, ruleno); + if (tTd(21, 1)) + { + printf("workspace: "); + printav(pvp); + } + break; + } + + while ((ap = *avp) != NULL || *rvp != NULL) + { + rp = *rvp; + if (tTd(21, 35)) + { + printf("ADVANCE rp="); + xputs(rp); + printf(", ap="); + xputs(ap); + printf("\n"); + } + if (rp == NULL) + { + /* end-of-pattern before end-of-address */ + goto backup; + } + if (ap == NULL && (*rp & 0377) != MATCHZANY && + (*rp & 0377) != MATCHZERO) + { + /* end-of-input with patterns left */ + goto backup; + } + + switch (*rp & 0377) + { + case MATCHCLASS: + /* match any phrase in a class */ + mlp->pattern = rvp; + mlp->first = avp; + extendclass: + ap = *avp; + if (ap == NULL) + goto backup; + mlp->last = avp++; + cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0'); + if (!wordinclass(buf, rp[1])) + { + if (tTd(21, 36)) + { + printf("EXTEND rp="); + xputs(rp); + printf(", ap="); + xputs(ap); + printf("\n"); + } + goto extendclass; + } + if (tTd(21, 36)) + printf("CLMATCH\n"); + mlp++; + break; + + case MATCHNCLASS: + /* match any token not in a class */ + if (wordinclass(ap, rp[1])) + goto backup; + + /* fall through */ + + case MATCHONE: + case MATCHANY: + /* match exactly one token */ + mlp->pattern = rvp; + mlp->first = avp; + mlp->last = avp++; + mlp++; + break; + + case MATCHZANY: + /* match zero or more tokens */ + mlp->pattern = rvp; + mlp->first = avp; + mlp->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->first = avp; + if (tTd(21, 2)) + printf("rewrite: LHS $&%s => \"%s\"\n", + macname(rp[1]), + ap == NULL ? "(NULL)" : ap); + + if (ap == NULL) + break; + while (*ap != '\0') + { + if (*avp == NULL || + strncasecmp(ap, *avp, strlen(*avp)) != 0) + { + /* no match */ + avp = mlp->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->pattern; + rp = *rvp; + avp = mlp->last + 1; + ap = *avp; + + if (tTd(21, 36)) + { + printf("BACKUP rp="); + xputs(rp); + printf(", ap="); + xputs(ap); + printf("\n"); + } + + if (ap == NULL) + { + /* run off the end -- back up again */ + continue; + } + if ((*rp & 0377) == MATCHANY || + (*rp & 0377) == MATCHZANY) + { + /* extend binding and continue */ + mlp->last = avp++; + rvp++; + mlp++; + break; + } + if ((*rp & 0377) == MATCHCLASS) + { + /* extend binding and try again */ + mlp->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)) + printf("----- rule fails\n"); + rwr = rwr->r_next; + ruleno++; + loopcount = 0; + continue; + } + + rvp = rwr->r_rhs; + if (tTd(21, 12)) + { + printf("-----rule matches:"); + printav(rvp); + } + + rp = *rvp; + if ((*rp & 0377) == CANONUSER) + { + rvp++; + rwr = rwr->r_next; + ruleno++; + loopcount = 0; + } + else if ((*rp & 0377) == CANONHOST) + { + rvp++; + rwr = NULL; + } + + /* substitute */ + for (avp = npvp; *rvp != NULL; rvp++) + { + register struct match *m; + register char **pp; + + rp = *rvp; + if ((*rp & 0377) == MATCHREPL) + { + /* substitute from LHS */ + m = &mlist[rp[1] - '1']; + if (m < mlist || m >= mlp) + { + syserr("554 rewrite: ruleset %d: replacement $%c out of bounds", + ruleset, rp[1]); + return EX_CONFIG; + } + if (tTd(21, 15)) + { + printf("$%c:", rp[1]); + pp = m->first; + while (pp <= m->last) + { + printf(" %lx=\"", (u_long) *pp); + (void) fflush(stdout); + printf("%s\"", *pp++); + } + printf("\n"); + } + pp = m->first; + while (pp <= m->last) + { + if (avp >= &npvp[MAXATOM]) + { + syserr("554 rewrite: expansion too long"); + return EX_DATAERR; + } + *avp++ = *pp++; + } + } + else + { + /* some sort of replacement */ + if (avp >= &npvp[MAXATOM]) + { + toolong: + syserr("554 rewrite: expansion too long"); + return EX_DATAERR; + } + if ((*rp & 0377) != MACRODEXPAND) + { + /* vanilla replacement */ + *avp++ = rp; + } + else + { + /* $&x replacement */ + char *mval = macvalue(rp[1], e); + char **xpvp; + int trsize = 0; + static size_t pvpb1_size = 0; + static char **pvpb1 = NULL; + char pvpbuf[PSBUFSIZE]; + + if (tTd(21, 2)) + printf("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) + free(pvpb1); + pvpb1 = (char **)xalloc(trsize); + pvpb1_size = trsize; + } + + bcopy((char *) pvp, (char *) pvpb1, trsize); + + /* scan the new replacement */ + xpvp = prescan(mval, '\0', pvpbuf, + sizeof pvpbuf, NULL, NULL); + if (xpvp == NULL) + { + /* prescan pre-printed error */ + return EX_DATAERR; + } + + /* insert it into the output stream */ + while (*xpvp != NULL) + { + if (tTd(21, 19)) + printf(" ... %s\n", *xpvp); + *avp++ = newstr(*xpvp); + if (avp >= &npvp[MAXATOM]) + goto toolong; + xpvp++; + } + if (tTd(21, 19)) + printf(" ... DONE\n"); + + /* restore the old trailing input */ + bcopy((char *) pvpb1, (char *) pvp, trsize); + } + } + } + *avp++ = NULL; + + /* + ** Check for any hostname/keyword lookups. + */ + + for (rvp = npvp; *rvp != NULL; rvp++) + { + char **hbrvp; + char **xpvp; + int trsize; + char *replac; + int endtoken; + STAB *map; + char *mapname; + char **key_rvp; + char **arg_rvp; + char **default_rvp; + char buf[MAXNAME + 1]; + char *pvpb1[MAXATOM + 1]; + char *argvect[10]; + char pvpbuf[PSBUFSIZE]; + char *nullpvp[1]; + extern char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *)); + + if ((**rvp & 0377) != HOSTBEGIN && + (**rvp & 0377) != LOOKUPBEGIN) + continue; + + /* + ** Got a hostname/keyword lookup. + ** + ** This could be optimized fairly easily. + */ + + hbrvp = rvp; + if ((**rvp & 0377) == HOSTBEGIN) + { + endtoken = HOSTEND; + mapname = "host"; + } + else + { + endtoken = LOOKUPEND; + mapname = *++rvp; + } + map = stab(mapname, ST_MAP, ST_FIND); + if (map == NULL) + syserr("554 rewrite: map %s not found", mapname); + + /* extract the match part */ + key_rvp = ++rvp; + default_rvp = NULL; + arg_rvp = argvect; + xpvp = NULL; + replac = pvpbuf; + while (*rvp != NULL && (**rvp & 0377) != endtoken) + { + int nodetype = **rvp & 0377; + + if (nodetype != CANONHOST && nodetype != CANONUSER) + { + rvp++; + continue; + } + + *rvp++ = NULL; + + if (xpvp != NULL) + { + cataddr(xpvp, NULL, replac, + &pvpbuf[sizeof pvpbuf] - replac, + '\0'); + *++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'); + *++arg_rvp = replac; + } + *++arg_rvp = NULL; + + /* save the remainder of the input string */ + trsize = (int) (avp - rvp + 1) * sizeof *rvp; + bcopy((char *) rvp, (char *) pvpb1, trsize); + + /* look it up */ + cataddr(key_rvp, NULL, buf, sizeof buf, '\0'); + argvect[0] = buf; + replac = map_lookup(map, buf, argvect, &rstat, e); + + /* if no replacement, use default */ + if (replac == NULL && default_rvp != NULL) + { + /* create the default */ + cataddr(default_rvp, NULL, buf, sizeof buf, '\0'); + replac = buf; + } + + 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); + if (xpvp == NULL) + { + /* prescan already printed error */ + return EX_DATAERR; + } + } + + /* append it to the token list */ + for (avp = hbrvp; *xpvp != NULL; xpvp++) + { + *avp++ = newstr(*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. + */ + + stat = callsubr(npvp, reclevel, e); + if (rstat == EX_OK || stat == EX_TEMPFAIL) + rstat = stat; + + /* copy vector back into original space. */ + for (avp = npvp; *avp++ != NULL;) + continue; + bcopy((char *) npvp, (char *) pvp, + (int) (avp - npvp) * sizeof *avp); + + if (tTd(21, 4)) + { + printf("rewritten as:"); + printav(pvp); + } + } + + if (OpMode == MD_TEST || tTd(21, 1)) + { + printf("rewrite: ruleset %3d returns:", ruleset); + printav(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. +*/ + +int +callsubr(pvp, reclevel, e) + char **pvp; + int reclevel; + ENVELOPE *e; +{ + char **avp; + char **rvp; + register int i; + int subr; + int stat; + int rstat = EX_OK; + char *tpvp[MAXATOM + 1]; + + for (avp = pvp; *avp != NULL; avp++) + { + if ((**avp & 0377) == CALLSUBR && avp[1] != NULL) + { + stripquotes(avp[1]); + subr = strtorwset(avp[1], NULL, ST_FIND); + if (subr < 0) + { + syserr("Unknown ruleset %s", avp[1]); + return EX_CONFIG; + } + + if (tTd(21, 3)) + printf("-----callsubr %s (%d)\n", avp[1], subr); + + /* + ** Take care of possible inner calls first. + ** use a full size temporary buffer to avoid + ** overflows in rewrite, but strip off the + ** subroutine call. + */ + + for (i = 2; avp[i] != NULL; i++) + tpvp[i - 2] = avp[i]; + tpvp[i - 2] = NULL; + + stat = callsubr(tpvp, reclevel, e); + if (rstat == EX_OK || stat == EX_TEMPFAIL) + rstat = stat; + + /* + ** Now we need to call the ruleset specified for + ** the subroutine. we can do this with the + ** temporary buffer that we set up earlier, + ** since it has all the data we want to rewrite. + */ + + stat = rewrite(tpvp, subr, reclevel, e); + if (rstat == EX_OK || stat == EX_TEMPFAIL) + rstat = stat; + + /* + ** Find length of tpvp and current offset into + ** pvp, if the total is greater than MAXATOM, + ** then it would overflow the buffer if we copied + ** it back in to pvp, in which case we throw a + ** fit. + */ + + for (rvp = tpvp; *rvp != NULL; rvp++) + continue; + if (((rvp - tpvp) + (avp - pvp)) > MAXATOM) + { + syserr("554 callsubr: expansion too long"); + return EX_DATAERR; + } + + /* + ** Now we can copy the rewritten code over + ** the initial subroutine call in the buffer. + */ + + for (i = 0; tpvp[i] != NULL; i++) + avp[i] = tpvp[i]; + avp[i] = NULL; + + /* + ** If we got this far, we've processed the left + ** most subroutine, and recursively called ourselves + ** to handle any other subroutines. We're done. + */ + + break; + } + } + return rstat; +} + /* +** MAP_LOOKUP -- do lookup in map +** +** Parameters: +** map -- 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. +*/ + +char * +map_lookup(map, key, argvect, pstat, e) + STAB *map; + char key[]; + char **argvect; + int *pstat; + ENVELOPE *e; +{ + auto int stat = EX_OK; + char *replac; + + if (e->e_sendmode == SM_DEFER) + { + /* don't do any map lookups */ + if (tTd(60, 1)) + printf("map_lookup(%s, %s) => DEFERRED\n", + map->s_name, key); + *pstat = EX_TEMPFAIL; + return NULL; + } + if (map == NULL || !bitset(MF_OPEN, map->s_map.map_mflags)) + return NULL; + + if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags)) + stripquotes(key); + + /* XXX should try to auto-open the map here */ + + if (tTd(60, 1)) + printf("map_lookup(%s, %s) => ", + map->s_name, key); + replac = (*map->s_map.map_class->map_lookup)(&map->s_map, + key, argvect, &stat); + if (tTd(60, 1)) + printf("%s (%d)\n", + replac != NULL ? replac : "NOT FOUND", + stat); + + /* should recover if stat == EX_TEMPFAIL */ + if (stat == EX_TEMPFAIL && !bitset(MF_NODEFER, map->s_map.map_mflags)) + { + *pstat = EX_TEMPFAIL; + if (tTd(60, 1)) + printf("map_lookup(%s, %s) tempfail: errno=%d\n", + map->s_name, key, errno); + if (e->e_message == NULL) + { + char mbuf[320]; + + snprintf(mbuf, sizeof mbuf, + "%.80s map: lookup (%s): deferred", + map->s_name, + shortenstring(key, MAXSHORTSTR)); + e->e_message = newstr(mbuf); + } + } + if (stat == EX_TEMPFAIL && map->s_map.map_tapp != NULL) + { + size_t i = strlen(key) + strlen(map->s_map.map_tapp) + 1; + static char *rwbuf = NULL; + static size_t rwbuflen = 0; + + if (i > rwbuflen) + { + if (rwbuf != NULL) + free(rwbuf); + rwbuflen = i; + rwbuf = (char *) xalloc(rwbuflen); + } + snprintf(rwbuf, rwbuflen, "%s%s", key, map->s_map.map_tapp); + if (tTd(60, 4)) + printf("map_lookup tempfail: returning \"%s\"\n", + rwbuf); + return rwbuf; + } + return replac; +} + /* +** 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' +*/ + +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 }, +#ifdef EX_CONFIG + { "config", EX_CONFIG }, +#endif + { NULL, EX_UNAVAILABLE } +}; + +ADDRESS * +buildaddr(tv, a, flags, e) + register char **tv; + register ADDRESS *a; + int flags; + register ENVELOPE *e; +{ + struct mailer **mp; + register struct mailer *m; + register char *p; + char *mname; + char **hostp; + char hbuf[MAXNAME + 1]; + static MAILER discardmailer; + static MAILER errormailer; + static char *discardargv[] = { "DISCARD", NULL }; + static char *errorargv[] = { "ERROR", NULL }; + static char ubuf[MAXNAME + 1]; + + if (tTd(24, 5)) + { + printf("buildaddr, flags=%x, tv=", flags); + printav(tv); + } + + if (a == NULL) + a = (ADDRESS *) xalloc(sizeof *a); + bzero((char *) a, sizeof *a); + + /* set up default error return flags */ + a->q_flags |= DefaultNotify; + + if (discardmailer.m_name == NULL) + { + /* initialize the discard mailer */ + discardmailer.m_name = "*discard*"; + discardmailer.m_mailer = "DISCARD"; + discardmailer.m_argv = discardargv; + } + + /* figure out what net/mailer to use */ + if (*tv == NULL || (**tv & 0377) != CANONNET) + { + syserr("554 buildaddr: no mailer in parsed address"); +badaddr: + a->q_flags |= QBADADDR; + a->q_mailer = &errormailer; + if (errormailer.m_name == NULL) + { + /* initialize the bogus mailer */ + errormailer.m_name = "*error*"; + errormailer.m_mailer = "ERROR"; + errormailer.m_argv = errorargv; + } + return a; + } + mname = *++tv; + + /* extract host and user portions */ + if (*++tv != NULL && (**tv & 0377) == CANONHOST) + hostp = ++tv; + else + hostp = NULL; + while (*tv != NULL && (**tv & 0377) != CANONUSER) + tv++; + if (*tv == NULL) + { + syserr("554 buildaddr: no user"); + goto badaddr; + } + if (tv == hostp) + hostp = NULL; + else if (hostp != NULL) + cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0'); + cataddr(++tv, NULL, ubuf, sizeof ubuf, ' '); + + /* save away the host name */ + if (strcasecmp(mname, "error") == 0) + { + if (hostp != NULL) + { + register struct errcodes *ep; + + if (strchr(hbuf, '.') != NULL) + { + extern int dsntoexitstat __P((char *)); + + a->q_status = newstr(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 (strcasecmp(ep->ec_name, hbuf) == 0) + break; + setstat(ep->ec_code); + } + } + else + setstat(EX_UNAVAILABLE); + stripquotes(ubuf); + if (isascii(ubuf[0]) && isdigit(ubuf[0]) && + isascii(ubuf[1]) && isdigit(ubuf[1]) && + isascii(ubuf[2]) && isdigit(ubuf[2]) && + ubuf[3] == ' ') + { + char fmt[10]; + + strncpy(fmt, ubuf, 3); + strcpy(&fmt[3], " %s"); + usrerr(fmt, ubuf + 4); + + /* + ** If this is a 4xx code and we aren't running + ** SMTP on our input, bounce this message; + ** otherwise it disappears without a trace. + */ + + if (fmt[0] == '4' && OpMode != MD_SMTP && + OpMode != MD_DAEMON) + { + e->e_flags |= EF_FATALERRS; + } + } + else + { + usrerr("553 %s", ubuf); + } + goto badaddr; + } + + for (mp = Mailer; (m = *mp++) != NULL; ) + { + if (strcasecmp(m->m_name, mname) == 0) + break; + } + if (m == NULL) + { + syserr("554 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 buildaddr: no host"); + goto badaddr; + } + a->q_host = NULL; + } + else + a->q_host = newstr(hbuf); + + /* figure out the user */ + p = ubuf; + if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@') + { + p++; + tv++; + 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 (strncasecmp(ubuf, ":include:", 9) == 0) + { + /* if :include:, don't need further rewriting */ + a->q_mailer = m = InclMailer; + a->q_user = newstr(&ubuf[9]); + return a; + } + } + + /* rewrite according recipient mailer rewriting rules */ + define('h', a->q_host, e); + if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags)) + { + /* sender addresses done later */ + (void) rewrite(tv, 2, 0, e); + if (m->m_re_rwset > 0) + (void) rewrite(tv, m->m_re_rwset, 0, e); + } + (void) rewrite(tv, 4, 0, e); + + /* save the result for the command line/RCPT argument */ + cataddr(tv, NULL, ubuf, sizeof ubuf, '\0'); + a->q_user = 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)) + { + printf("buildaddr => "); + printaddr(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 null, +** use SpaceSub. +** +** Returns: +** none. +** +** Side Effects: +** Destroys buf. +*/ + +void +cataddr(pvp, evp, buf, sz, spacesub) + char **pvp; + char **evp; + char *buf; + register int sz; + int spacesub; +{ + bool oatomtok = FALSE; + bool natomtok = FALSE; + register int i; + register char *p; + + if (spacesub == '\0') + spacesub = SpaceSub; + + if (pvp == NULL) + { + (void) strcpy(buf, ""); + return; + } + p = buf; + sz -= 2; + while (*pvp != NULL && (i = strlen(*pvp)) < sz) + { + natomtok = (TokTypeTab[**pvp & 0xff] == ATM); + if (oatomtok && natomtok) + *p++ = spacesub; + (void) strcpy(p, *pvp); + oatomtok = natomtok; + p += i; + sz -= i + 1; + if (pvp++ == evp) + break; + } + *p = '\0'; +} + /* +** 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; + u_long qf_bit; +}; + +struct qflags AddressFlags[] = +{ + { "QDONTSEND", QDONTSEND }, + { "QBADADDR", QBADADDR }, + { "QGOODUID", QGOODUID }, + { "QPRIMARY", QPRIMARY }, + { "QQUEUEUP", QQUEUEUP }, + { "QSENT", QSENT }, + { "QNOTREMOTE", QNOTREMOTE }, + { "QSELFREF", QSELFREF }, + { "QVERIFIED", QVERIFIED }, + { "QBOGUSSHELL", QBOGUSSHELL }, + { "QUNSAFEADDR", QUNSAFEADDR }, + { "QPINGONSUCCESS", QPINGONSUCCESS }, + { "QPINGONFAILURE", QPINGONFAILURE }, + { "QPINGONDELAY", QPINGONDELAY }, + { "QHASNOTIFY", QHASNOTIFY }, + { "QRELAYED", QRELAYED }, + { "QEXPANDED", QEXPANDED }, + { "QDELIVERED", QDELIVERED }, + { "QDELAYED", QDELAYED }, + { "QTHISPASS", QTHISPASS }, + { "QRCPTOK", QRCPTOK }, + { NULL } +}; + +void +printaddr(a, follow) + register ADDRESS *a; + bool follow; +{ + register MAILER *m; + MAILER pseudomailer; + register struct qflags *qfp; + bool firstone; + + if (a == NULL) + { + printf("[NULL]\n"); + return; + } + + while (a != NULL) + { + printf("%lx=", (u_long) a); + (void) fflush(stdout); + + /* find the mailer -- carefully */ + m = a->q_mailer; + if (m == NULL) + { + m = &pseudomailer; + m->m_mno = -1; + m->m_name = "NULL"; + } + + printf("%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); + printf("\tuser `%s', ruser `%s'\n", + a->q_user, + a->q_ruser == NULL ? "" : a->q_ruser); + printf("\tnext=%lx, alias %lx, uid %d, gid %d\n", + (u_long) a->q_next, (u_long) a->q_alias, + (int) a->q_uid, (int) a->q_gid); + printf("\tflags=%lx<", a->q_flags); + firstone = TRUE; + for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++) + { + if (!bitset(qfp->qf_bit, a->q_flags)) + continue; + if (!firstone) + printf(","); + firstone = FALSE; + printf("%s", qfp->qf_name); + } + printf(">\n"); + printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n", + a->q_owner == NULL ? "(none)" : a->q_owner, + a->q_home == NULL ? "(none)" : a->q_home, + a->q_fullname == NULL ? "(none)" : a->q_fullname); + printf("\torcpt=\"%s\", statmta=%s, status=%s\n", + a->q_orcpt == NULL ? "(none)" : a->q_orcpt, + a->q_statmta == NULL ? "(none)" : a->q_statmta, + a->q_status == NULL ? "(none)" : a->q_status); + printf("\trstatus=\"%s\"\n", + a->q_rstatus == NULL ? "(none)" : a->q_rstatus); + printf("\tspecificity=%d, statdate=%s\n", + a->q_specificity, 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 *fancy; + char *oldg = macvalue('g', e); + int rwset; + static char buf[MAXNAME + 1]; + char lbuf[MAXNAME + 1]; + char pvpbuf[PSBUFSIZE]; + extern char *crackaddr __P((char *)); + + if (tTd(12, 1)) + printf("remotename(%s)\n", name); + + /* don't do anything if we are tagging it as special */ + if (bitset(RF_SENDERADDR, flags)) + rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset + : m->m_se_rwset; + else + rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset + : m->m_re_rwset; + if (rwset < 0) + return (name); + + /* + ** 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); + + /* + ** 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); + if (pvp == NULL) + return (name); + if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; + if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL) + { + /* append from domain to this address */ + register char **pxp = pvp; + + /* see if there is an "@domain" in the current name */ + while (*pxp != NULL && strcmp(*pxp, "@") != 0) + pxp++; + if (*pxp == NULL) + { + /* no.... append the "@domain" from the sender */ + register char **qxq = e->e_fromdomain; + + while ((*pxp++ = *qxq++) != NULL) + continue; + if (rewrite(pvp, 3, 0, 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, 0, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; + } + else + { + if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; + } + if (rwset > 0) + { + if (rewrite(pvp, rwset, 0, 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, 0, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; + + /* + ** Now restore the comment information we had at the beginning. + */ + + cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0'); + define('g', lbuf, e); + + /* 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); + + define('g', oldg, e); + + if (tTd(12, 1)) + printf("remotename => `%s'\n", buf); + 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) + +void +maplocaluser(a, sendq, aliaslevel, e) + register ADDRESS *a; + ADDRESS **sendq; + int aliaslevel; + ENVELOPE *e; +{ + register char **pvp; + register ADDRESS *a1 = NULL; + auto char *delimptr; + char pvpbuf[PSBUFSIZE]; + + if (tTd(29, 1)) + { + printf("maplocaluser: "); + printaddr(a, FALSE); + } + pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL); + if (pvp == NULL) + { + if (tTd(29, 9)) + printf("maplocaluser: cannot prescan %s\n", a->q_user); + return; + } + + define('h', a->q_host, e); + define('u', a->q_user, e); + define('z', a->q_home, e); + + if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL) + { + if (tTd(29, 9)) + printf("maplocaluser: rewrite tempfail\n"); + a->q_flags |= QQUEUEUP; + a->q_status = "4.4.3"; + return; + } + if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) + { + if (tTd(29, 9)) + printf("maplocaluser: doesn't resolve\n"); + return; + } + + /* if non-null, mailer destination specified -- has it changed? */ + a1 = buildaddr(pvp, NULL, 0, e); + if (a1 == NULL || sameaddr(a, a1)) + { + if (tTd(29, 9)) + printf("maplocaluser: address unchanged\n"); + if (a1 != NULL) + free(a1); + 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 = a->q_paddr; + + /* mark old address as dead; insert new address */ + a->q_flags |= QDONTSEND; + if (tTd(29, 5)) + { + printf("maplocaluser: QDONTSEND "); + printaddr(a, FALSE); + } + a1->q_alias = a; + allocaddr(a1, RF_COPYALL, a->q_paddr); + (void) recipient(a1, sendq, aliaslevel, e); +} + /* +** DEQUOTE_INIT -- initialize dequote map +** +** This is a no-op. +** +** Parameters: +** map -- the internal map structure. +** args -- arguments. +** +** Returns: +** TRUE. +*/ + +bool +dequote_init(map, args) + MAP *map; + char *args; +{ + register char *p = args; + + 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 's': + map->map_coldelim = *++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_coldelim; + + 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 ' ': + 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. +** +** Returns: +** EX_OK -- if the rwset doesn't resolve to $#error +** else -- the failure status (message printed) +*/ + +int +rscheck(rwset, p1, p2, e) + char *rwset; + char *p1; + char *p2; + ENVELOPE *e; +{ + char *buf; + int bufsize; + int saveexitstat; + int rstat = EX_OK; + char **pvp; + int rsno; + bool discard = FALSE; + auto ADDRESS a1; + bool saveQuickAbort = QuickAbort; + bool saveSuprErrs = SuprErrs; + char buf0[MAXLINE]; + char pvpbuf[PSBUFSIZE]; + extern char MsgBuf[]; + + if (tTd(48, 2)) + printf("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 = xalloc(bufsize); + else + { + buf = buf0; + bufsize = sizeof buf0; + } + (void) snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); + } + else + { + bufsize = strlen(p1) + 1; + if (bufsize > sizeof buf0) + buf = xalloc(bufsize); + else + { + buf = buf0; + bufsize = sizeof buf0; + } + (void) snprintf(buf, bufsize, "%s", p1); + } + SuprErrs = TRUE; + QuickAbort = FALSE; + pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL); + SuprErrs = saveSuprErrs; + if (pvp == NULL) + { + if (tTd(48, 2)) + printf("rscheck: cannot prescan input\n"); +/* + syserr("rscheck: cannot prescan input: \"%s\"", + shortenstring(buf, MAXSHORTSTR)); + rstat = EX_DATAERR; +*/ + goto finis; + } + (void) rewrite(pvp, rsno, 0, e); + if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET || + pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 && + strcmp(pvp[1], "discard") != 0)) + { + goto finis; + } + + if (strcmp(pvp[1], "discard") == 0) + { + if (tTd(48, 2)) + printf("rscheck: discard mailer selected\n"); + e->e_flags |= EF_DISCARD; + discard = TRUE; + } + else + { + int savelogusrerrs = LogUsrErrs; + static bool logged = FALSE; + + /* got an error -- process it */ + saveexitstat = ExitStat; + LogUsrErrs = FALSE; + (void) buildaddr(pvp, &a1, 0, e); + LogUsrErrs = savelogusrerrs; + rstat = ExitStat; + ExitStat = saveexitstat; + if (!logged) + { + markstats(e, &a1, TRUE); + logged = TRUE; + } + } + + if (LogLevel >= 4) + { + char *relay; + char *p; + char lbuf[MAXLINE]; + + p = lbuf; + if (p2 != NULL) + { + snprintf(p, SPACELEFT(lbuf, p), + ", arg2=%s", + p2); + p += strlen(p); + } + if ((relay = macvalue('_', e)) != NULL) + { + snprintf(p, SPACELEFT(lbuf, p), + ", relay=%s", relay); + p += strlen(p); + } + *p = '\0'; + if (discard) + sm_syslog(LOG_NOTICE, e->e_id, + "ruleset=%s, arg1=%s%s, discard", + rwset, p1, lbuf); + else + sm_syslog(LOG_NOTICE, e->e_id, + "ruleset=%s, arg1=%s%s, reject=%s", + rwset, p1, lbuf, MsgBuf); + } + + finis: + /* clean up */ + QuickAbort = saveQuickAbort; + setstat(rstat); + if (buf != buf0) + free(buf); + + if (rstat != EX_OK && QuickAbort) + longjmp(TopFrame, 2); + return rstat; +} diff --git a/contrib/sendmail/src/pathnames.h b/contrib/sendmail/src/pathnames.h new file mode 100644 index 0000000..e10387e --- /dev/null +++ b/contrib/sendmail/src/pathnames.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 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. + * + * + * @(#)pathnames.h 8.8 (Berkeley) 5/19/98 + */ + +#ifndef _PATH_SENDMAILCF +# if defined(USE_VENDOR_CF_PATH) && defined(_PATH_VENDOR_CF) +# define _PATH_SENDMAILCF _PATH_VENDOR_CF +# else +# define _PATH_SENDMAILCF "/etc/sendmail.cf" +# endif +#endif + +#ifndef _PATH_SENDMAILPID +# ifdef BSD4_4 +# define _PATH_SENDMAILPID "/var/run/sendmail.pid" +# else +# define _PATH_SENDMAILPID "/etc/sendmail.pid" +# endif +#endif + +#ifndef _PATH_HOSTS +# define _PATH_HOSTS "/etc/hosts" +#endif diff --git a/contrib/sendmail/src/queue.c b/contrib/sendmail/src/queue.c new file mode 100644 index 0000000..24b789a --- /dev/null +++ b/contrib/sendmail/src/queue.c @@ -0,0 +1,2402 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +# include "sendmail.h" + +#ifndef lint +#if QUEUE +static char sccsid[] = "@(#)queue.c 8.202 (Berkeley) 6/15/98 (with queueing)"; +#else +static char sccsid[] = "@(#)queue.c 8.202 (Berkeley) 6/15/98 (without queueing)"; +#endif +#endif /* not lint */ + +# include +# include + +# if QUEUE + +/* +** 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 of message */ + struct work *w_next; /* next in queue */ +}; + +typedef struct work WORK; + +WORK *WorkQ; /* queue of things to be done */ + +#define QF_VERSION 2 /* version number of this queue format */ + +extern int orderq __P((bool)); + /* +** QUEUEUP -- queue a message up for future transmission. +** +** Parameters: +** e -- the envelope to queue up. +** announce -- if TRUE, tell when you are queueing up. +** +** Returns: +** none. +** +** Side Effects: +** The current request are saved in a control file. +** The queue file is left locked. +*/ + +void +queueup(e, announce) + register ENVELOPE *e; + bool announce; +{ + char *qf; + register FILE *tfp; + register HDR *h; + register ADDRESS *q; + int fd; + int i; + bool newid; + register char *p; + MAILER nullmailer; + MCI mcibuf; + char tf[MAXQFNAME]; + char buf[MAXLINE]; + extern void printctladdr __P((ADDRESS *, FILE *)); + + /* + ** Create control file. + */ + + newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); + + /* if newid, queuename will create a locked qf file in e->lockfp */ + strcpy(tf, queuename(e, 't')); + tfp = e->e_lockfp; + if (tfp == NULL) + newid = FALSE; + + /* if newid, just write the qf file directly (instead of tf file) */ + if (!newid) + { + /* get a locked tf file */ + for (i = 0; i < 128; i++) + { + fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); + if (fd < 0) + { + if (errno != EEXIST) + break; + if (LogLevel > 0 && (i % 32) == 0) + sm_syslog(LOG_ALERT, e->e_id, + "queueup: cannot create %s, uid=%d: %s", + tf, geteuid(), errstring(errno)); + } + else + { + if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB)) + break; + else if (LogLevel > 0 && (i % 32) == 0) + sm_syslog(LOG_ALERT, e->e_id, + "queueup: cannot lock %s: %s", + tf, errstring(errno)); + close(fd); + } + + if ((i % 32) == 31) + { + /* save the old temp file away */ + (void) rename(tf, queuename(e, 'T')); + } + else + sleep(i % 32); + } + if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL) + { + printopenfds(TRUE); + syserr("!queueup: cannot create queue temp file %s, uid=%d", + tf, geteuid()); + } + } + + if (tTd(40, 1)) + printf("\n>>>>> queueing %s%s >>>>>\n", e->e_id, + newid ? " (new id)" : ""); + if (tTd(40, 3)) + { + extern void printenvflags __P((ENVELOPE *)); + + printf(" e_flags="); + printenvflags(e); + } + if (tTd(40, 32)) + { + printf(" sendq="); + printaddr(e->e_sendqueue, TRUE); + } + if (tTd(40, 9)) + { + printf(" tfp="); + dumpfd(fileno(tfp), TRUE, FALSE); + printf(" lockfp="); + if (e->e_lockfp == NULL) + printf("NULL\n"); + else + dumpfd(fileno(e->e_lockfp), TRUE, FALSE); + } + + /* + ** If there is no data file yet, create one. + */ + + if (!bitset(EF_HAS_DF, e->e_flags)) + { + register FILE *dfp = NULL; + char dfname[MAXQFNAME]; + struct stat stbuf; + + strcpy(dfname, queuename(e, 'd')); + fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode); + if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL) + syserr("!queueup: cannot create data temp file %s, uid=%d", + dfname, geteuid()); + if (fstat(fd, &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; + bzero(&mcibuf, sizeof mcibuf); + mcibuf.mci_out = dfp; + mcibuf.mci_mailer = FileMailer; + (*e->e_putbody)(&mcibuf, e, NULL); + (void) xfclose(dfp, "queueup dfp", e->e_id); + e->e_putbody = putbody; + } + + /* + ** Output future work requests. + ** Priority and creation time should be first, since + ** they are required by orderq. + */ + + /* output queue version number (must be first!) */ + fprintf(tfp, "V%d\n", QF_VERSION); + + /* output creation time */ + fprintf(tfp, "T%ld\n", (long) e->e_ctime); + + /* output last delivery time */ + fprintf(tfp, "K%ld\n", (long) e->e_dtime); + + /* output number of delivery attempts */ + fprintf(tfp, "N%d\n", e->e_ntries); + + /* output message priority */ + fprintf(tfp, "P%ld\n", e->e_msgpriority); + + /* output inode number of data file */ + /* XXX should probably include device major/minor too */ + if (e->e_dfino != -1) + { + if (sizeof e->e_dfino > sizeof(long)) + fprintf(tfp, "I%d/%d/%s\n", + major(e->e_dfdev), minor(e->e_dfdev), + quad_to_string(e->e_dfino)); + else + fprintf(tfp, "I%d/%d/%lu\n", + major(e->e_dfdev), minor(e->e_dfdev), + (unsigned long) e->e_dfino); + } + + /* output body type */ + if (e->e_bodytype != NULL) + fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE)); + +#if _FFR_SAVE_CHARSET + if (e->e_charset != NULL) + fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE)); +#endif + + /* message from envelope, if it exists */ + if (e->e_message != NULL) + fprintf(tfp, "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'; + *p++ = '\0'; + if (buf[0] != '\0') + fprintf(tfp, "F%s\n", buf); + + /* $r and $s and $_ macro values */ + if ((p = macvalue('r', e)) != NULL) + fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE)); + if ((p = macvalue('s', e)) != NULL) + fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE)); + if ((p = macvalue('_', e)) != NULL) + fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE)); + + /* output name of sender */ + if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) + p = e->e_sender; + else + p = e->e_from.q_paddr; + fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE)); + + /* output ESMTP-supplied "original" information */ + if (e->e_envid != NULL) + fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE)); + + /* output list of recipient addresses */ + printctladdr(NULL, NULL); + for (q = e->e_sendqueue; q != NULL; q = q->q_next) + { + if (bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags)) + { +#if XDEBUG + if (bitset(QQUEUEUP, q->q_flags)) + sm_syslog(LOG_DEBUG, e->e_id, + "dropenvelope: q_flags = %x, paddr = %s", + q->q_flags, q->q_paddr); +#endif + continue; + } + printctladdr(q, tfp); + if (q->q_orcpt != NULL) + fprintf(tfp, "Q%s\n", + denlstring(q->q_orcpt, TRUE, FALSE)); + putc('R', tfp); + if (bitset(QPRIMARY, q->q_flags)) + putc('P', tfp); + if (bitset(QHASNOTIFY, q->q_flags)) + putc('N', tfp); + if (bitset(QPINGONSUCCESS, q->q_flags)) + putc('S', tfp); + if (bitset(QPINGONFAILURE, q->q_flags)) + putc('F', tfp); + if (bitset(QPINGONDELAY, q->q_flags)) + putc('D', tfp); + putc(':', tfp); + fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE)); + if (announce) + { + e->e_to = q->q_paddr; + message("queued"); + if (LogLevel > 8) + logdelivery(q->q_mailer, NULL, "queued", + NULL, (time_t) 0, e); + e->e_to = NULL; + } + if (tTd(40, 1)) + { + printf("queueing "); + printaddr(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. + */ + + bzero((char *) &nullmailer, sizeof nullmailer); + nullmailer.m_re_rwset = nullmailer.m_rh_rwset = + nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; + nullmailer.m_eol = "\n"; + bzero(&mcibuf, sizeof mcibuf); + mcibuf.mci_mailer = &nullmailer; + mcibuf.mci_out = tfp; + + define('g', "\201f", e); + for (h = e->e_header; h != NULL; h = h->h_link) + { + extern bool bitzerop __P((BITMAP)); + + /* don't output null headers */ + if (h->h_value == NULL || h->h_value[0] == '\0') + 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; + } + + /* output this header */ + fprintf(tfp, "H"); + + /* if conditional, output the set of conditions */ + if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) + { + int j; + + (void) putc('?', tfp); + for (j = '\0'; j <= '\177'; j++) + if (bitnset(j, h->h_mflags)) + (void) putc(j, tfp); + (void) putc('?', tfp); + } + + /* output the header: expand macros, convert addresses */ + if (bitset(H_DEFAULT, h->h_flags)) + { + fprintf(tfp, "%s: %s\n", + h->h_field, + denlstring(buf, FALSE, TRUE)); + } + else if (bitset(H_FROM|H_RCPT, h->h_flags)) + { + bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); + FILE *savetrace = TrafficLogFile; + + TrafficLogFile = NULL; + + if (bitset(H_FROM, h->h_flags)) + oldstyle = FALSE; + + commaize(h, h->h_value, oldstyle, &mcibuf, e); + + TrafficLogFile = savetrace; + } + else + { + fprintf(tfp, "%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. + */ + + fprintf(tfp, ".\n"); + + if (fflush(tfp) < 0 || + (SuperSafe && fsync(fileno(tfp)) < 0) || + ferror(tfp)) + { + if (newid) + syserr("!552 Error writing control file %s", tf); + else + syserr("!452 Error writing control file %s", tf); + } + + if (!newid) + { + /* rename (locked) tf to be (locked) qf */ + qf = queuename(e, 'q'); + if (rename(tf, qf) < 0) + syserr("cannot rename(%s, %s), uid=%d", + tf, qf, geteuid()); + + /* close and unlock old (locked) qf */ + if (e->e_lockfp != NULL) + (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id); + e->e_lockfp = tfp; + } + else + qf = tf; + errno = 0; + e->e_flags |= EF_INQUEUE; + + /* save log info */ + if (LogLevel > 79) + sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf); + + if (tTd(40, 1)) + printf("<<<<< done queueing %s <<<<<\n\n", e->e_id); + return; +} + +void +printctladdr(a, tfp) + register ADDRESS *a; + FILE *tfp; +{ + char *uname; + 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) + fprintf(tfp, "C\n"); + lastctladdr = NULL; + lastuid = 0; + return; + } + + /* find the active uid */ + q = getctladdr(a); + if (q == NULL) + { + uname = NULL; + uid = 0; + gid = 0; + } + else + { + uname = 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 || uname == NULL || uname[0] == '\0') + fprintf(tfp, "C"); + else + fprintf(tfp, "C%s:%ld:%ld", + denlstring(uname, TRUE, FALSE), (long) uid, (long) gid); + fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE)); +} + /* +** 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. +** verbose -- if TRUE, print out status information. +** +** Returns: +** TRUE if the queue run successfully began. +** +** Side Effects: +** runs things in the mail queue. +*/ + +ENVELOPE QueueEnvelope; /* the queue run envelope */ +extern int get_num_procs_online __P((void)); + +bool +runqueue(forkflag, verbose) + bool forkflag; + bool verbose; +{ + register ENVELOPE *e; + int njobs; + int sequenceno = 0; + time_t current_la_time; + extern ENVELOPE BlankEnvelope; + extern void clrdaemon __P((void)); + extern void runqueueevent __P((void)); + + DoQueueRun = FALSE; + + /* + ** If no work will ever be selected, don't even bother reading + ** the queue. + */ + + CurrentLA = getla(); /* get load average */ + current_la_time = curtime(); + + if (shouldqueue(WkRecipFact, current_la_time)) + { + char *msg = "Skipping queue run -- load average too high"; + + if (verbose) + message("458 %s\n", msg); + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, + "runqueue: %s", + msg); + if (forkflag && QueueIntvl != 0) + (void) setevent(QueueIntvl, runqueueevent, 0); + return FALSE; + } + + /* + ** See if we already have too many children. + */ + + if (forkflag && QueueIntvl != 0 && + MaxChildren > 0 && CurChildren >= MaxChildren) + { + (void) setevent(QueueIntvl, runqueueevent, 0); + return FALSE; + } + + /* + ** See if we want to go off and do other useful work. + */ + + if (forkflag) + { + pid_t pid; + extern SIGFUNC_DECL intsig __P((int)); + extern SIGFUNC_DECL reapchild __P((int)); + + blocksignal(SIGCHLD); + (void) setsignal(SIGCHLD, reapchild); + + pid = dofork(); + if (pid == -1) + { + const char *msg = "Skipping queue run -- fork() failed"; + const char *err = errstring(errno); + + if (verbose) + message("458 %s: %s\n", msg, err); + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, + "runqueue: %s: %s", + msg, err); + if (QueueIntvl != 0) + (void) setevent(QueueIntvl, runqueueevent, 0); + (void) releasesignal(SIGCHLD); + return FALSE; + } + if (pid != 0) + { + /* parent -- pick up intermediate zombie */ + (void) blocksignal(SIGALRM); + proc_list_add(pid); + (void) releasesignal(SIGALRM); + releasesignal(SIGCHLD); + if (QueueIntvl != 0) + (void) setevent(QueueIntvl, runqueueevent, 0); + return TRUE; + } + /* child -- double fork and clean up signals */ + proc_list_clear(); + releasesignal(SIGCHLD); + (void) setsignal(SIGCHLD, SIG_DFL); + (void) setsignal(SIGHUP, intsig); + } + + setproctitle("running queue: %s", QueueDir); + + if (LogLevel > 69) + sm_syslog(LOG_DEBUG, NOQID, + "runqueue %s, pid=%d, forkflag=%d", + QueueDir, getpid(), forkflag); + + /* + ** Release any resources used by the daemon code. + */ + +# if DAEMON + clrdaemon(); +# endif /* DAEMON */ + + /* 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; + e = newenvelope(&QueueEnvelope, CurEnv); + e->e_flags = BlankEnvelope.e_flags; + + /* make sure we have disconnected from parent */ + if (forkflag) + { + disconnect(1, e); + QuickAbort = FALSE; + } + + /* + ** Make sure the alias database is open. + */ + + initmaps(FALSE, e); + + /* + ** If we are running part of the queue, always ignore stored + ** host status. + */ + + if (QueueLimitId != NULL || QueueLimitSender != NULL || + QueueLimitRecipient != NULL) + { + IgnoreHostStatus = TRUE; + MinQueueAge = 0; + } + + /* + ** 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. + */ + + /* order the existing work requests */ + njobs = orderq(FALSE); + + /* process them once at a time */ + while (WorkQ != NULL) + { + WORK *w = WorkQ; + + WorkQ = WorkQ->w_next; + e->e_to = NULL; + + /* + ** Ignore jobs that are too expensive for the moment. + ** + ** Get new load average every 30 seconds. + */ + + if (current_la_time < curtime() - 30) + { + CurrentLA = getla(); + current_la_time = curtime(); + } + 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; + } + sequenceno++; + if (shouldqueue(w->w_pri, w->w_ctime)) + { + if (Verbose) + message(""); + if (QueueSortOrder == QS_BYPRIORITY) + { + if (Verbose) + message("Skipping %s (sequence %d of %d) and flushing rest of queue", + w->w_name + 2, + sequenceno, + njobs); + if (LogLevel > 8) + sm_syslog(LOG_INFO, NOQID, + "runqueue: Flushing queue from %s (pri %ld, LA %d, %d of %d)", + w->w_name + 2, + w->w_pri, + CurrentLA, + sequenceno, + njobs); + break; + } + else if (Verbose) + message("Skipping %s (sequence %d of %d)", + w->w_name + 2, sequenceno, njobs); + } + else + { + pid_t pid; + extern pid_t dowork __P((char *, bool, bool, ENVELOPE *)); + + if (Verbose) + { + message(""); + message("Running %s (sequence %d of %d)", + w->w_name + 2, sequenceno, njobs); + } + pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e); + errno = 0; + if (pid != 0) + (void) waitfor(pid); + } + free(w->w_name); + if (w->w_host) + free(w->w_host); + free((char *) w); + } + + /* exit without the usual cleanup */ + e->e_id = NULL; + finis(); + /*NOTREACHED*/ + return TRUE; +} + + +/* +** RUNQUEUEEVENT -- stub for use in setevent +*/ + +void +runqueueevent() +{ + DoQueueRun = TRUE; +} + /* +** ORDERQ -- order the work queue. +** +** Parameters: +** doall -- if set, include everything in the queue (even +** the jobs that cannot be run because the load +** average is too high). Otherwise, exclude those +** jobs. +** +** Returns: +** The number of request in the queue (not necessarily +** the number of requests in WorkQ however). +** +** Side Effects: +** Sets WorkQ to the queue of available work, in order. +*/ + +# define NEED_P 001 +# define NEED_T 002 +# define NEED_R 004 +# define NEED_S 010 + +static WORK *WorkList = NULL; +static int WorkListSize = 0; + +int +orderq(doall) + bool doall; +{ + register struct dirent *d; + register WORK *w; + register char *p; + DIR *f; + register int i; + int wn = -1; + int wc; + QUEUE_CHAR *check; + + if (tTd(41, 1)) + { + printf("orderq:\n"); + + check = QueueLimitId; + while (check != NULL) + { + printf("\tQueueLimitId = %s\n", + check->queue_match); + check = check->queue_next; + } + + check = QueueLimitSender; + while (check != NULL) + { + printf("\tQueueLimitSender = %s\n", + check->queue_match); + check = check->queue_next; + } + + check = QueueLimitRecipient; + while (check != NULL) + { + printf("\tQueueLimitRecipient = %s\n", + check->queue_match); + check = check->queue_next; + } + } + + /* clear out old WorkQ */ + for (w = WorkQ; w != NULL; ) + { + register WORK *nw = w->w_next; + + WorkQ = nw; + free(w->w_name); + if (w->w_host) + free(w->w_host); + free((char *) w); + w = nw; + } + + /* open the queue directory */ + f = opendir("."); + if (f == NULL) + { + syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); + return (0); + } + + /* + ** Read the work directory. + */ + + while ((d = readdir(f)) != NULL) + { + FILE *cf; + int qfver = 0; + char lbuf[MAXNAME + 1]; + extern bool strcontainedin __P((char *, char *)); + + if (tTd(41, 50)) + printf("orderq: checking %s\n", d->d_name); + + /* is this an interesting entry? */ + if (d->d_name[0] != 'q' || d->d_name[1] != 'f') + continue; + + if (strlen(d->d_name) > MAXQFNAME) + continue; + + check = QueueLimitId; + while (check != NULL) + { + if (strcontainedin(check->queue_match, d->d_name)) + break; + else + check = check->queue_next; + } + if (QueueLimitId != NULL && check == NULL) + continue; + +#ifdef PICKY_QF_NAME_CHECK + /* + ** Check queue name for plausibility. This handles + ** both old and new type ids. + */ + + p = d->d_name + 2; + if (isupper(p[0]) && isupper(p[2])) + p += 3; + else if (isupper(p[1])) + p += 2; + else + p = d->d_name; + for (i = 0; isdigit(*p); p++) + i++; + if (i < 5 || *p != '\0') + { + if (Verbose) + printf("orderq: bogus qf name %s\n", d->d_name); + if (LogLevel > 0) + sm_syslog(LOG_ALERT, NOQID, + "orderq: bogus qf name %s", + d->d_name); + if (strlen(d->d_name) > (SIZE_T) MAXNAME) + d->d_name[MAXNAME] = '\0'; + strcpy(lbuf, d->d_name); + lbuf[0] = 'Q'; + (void) rename(d->d_name, lbuf); + continue; + } +#endif + + /* open control file (if not too many files) */ + if (++wn >= MaxQueueRun && MaxQueueRun > 0) + { + if (wn == MaxQueueRun && LogLevel > 0) + sm_syslog(LOG_ALERT, NOQID, + "WorkList for %s maxed out at %d", + QueueDir, MaxQueueRun); + continue; + } + if (wn >= WorkListSize) + { + extern void grow_wlist __P((void)); + + grow_wlist(); + if (wn >= WorkListSize) + continue; + } + + cf = fopen(d->d_name, "r"); + if (cf == NULL) + { + /* this may be some random person sending hir msgs */ + /* syserr("orderq: cannot open %s", cbuf); */ + if (tTd(41, 2)) + printf("orderq: cannot open %s: %s\n", + d->d_name, errstring(errno)); + errno = 0; + wn--; + continue; + } + w = &WorkList[wn]; + w->w_name = newstr(d->d_name); + w->w_host = NULL; + w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB); + w->w_tooyoung = FALSE; + + /* make sure jobs in creation don't clog queue */ + w->w_pri = 0x7fffffff; + w->w_ctime = 0; + + /* extract useful information */ + i = NEED_P | NEED_T; + if (QueueLimitSender != NULL) + i |= NEED_S; + if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL) + i |= NEED_R; + while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) + { + int c; + time_t age; + extern bool strcontainedin __P((char *, char *)); + + p = strchr(lbuf, '\n'); + if (p != NULL) + *p = '\0'; + else + { + /* flush rest of overly long line */ + while ((c = getc(cf)) != 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 'R': + if (w->w_host == NULL && + (p = strrchr(&lbuf[1], '@')) != NULL) + w->w_host = newstr(&p[1]); + if (QueueLimitRecipient == NULL) + { + i &= ~NEED_R; + break; + } + if (qfver > 0) + { + p = strchr(&lbuf[1], ':'); + if (p == NULL) + p = &lbuf[1]; + } + else + p = &lbuf[1]; + check = QueueLimitRecipient; + while (check != NULL) + { + if (strcontainedin(check->queue_match, + p)) + break; + else + check = check->queue_next; + } + if (check != NULL) + i &= ~NEED_R; + break; + + case 'S': + check = QueueLimitSender; + while (check != NULL) + { + if (strcontainedin(check->queue_match, + &lbuf[1])) + break; + else + check = check->queue_next; + } + if (check != NULL) + i &= ~NEED_S; + 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; + } + } + (void) fclose(cf); + + if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || + bitset(NEED_R|NEED_S, i)) + { + /* don't even bother sorting this job in */ + if (tTd(41, 49)) + printf("skipping %s (%x)\n", w->w_name, i); + free(w->w_name); + if (w->w_host) + free(w->w_host); + wn--; + } + } + (void) closedir(f); + wn++; + + wc = min(wn, WorkListSize); + if (wc > MaxQueueRun && MaxQueueRun > 0) + wc = MaxQueueRun; + + if (QueueSortOrder == QS_BYHOST) + { + extern int workcmpf1(); + extern int workcmpf2(); + + /* + ** 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) + { + extern int sm_strcasecmp __P((char *, char *)); + + 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 == QS_BYTIME) + { + extern int workcmpf3(); + + /* + ** Simple sort based on submission time only. + */ + + qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3); + } + else + { + extern int workcmpf0(); + + /* + ** Simple sort based on queue priority only. + */ + + qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0); + } + + /* + ** Convert the work list into canonical form. + ** Should be turning it into a list of envelopes here perhaps. + */ + + WorkQ = NULL; + for (i = wc; --i >= 0; ) + { + w = (WORK *) xalloc(sizeof *w); + 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_next = WorkQ; + WorkQ = w; + } + if (WorkList != NULL) + free(WorkList); + WorkList = NULL; + WorkListSize = 0; + + if (tTd(40, 1)) + { + for (w = WorkQ; w != NULL; w = w->w_next) + printf("%32s: pri=%ld\n", w->w_name, w->w_pri); + } + + return (wn); +} + /* +** GROW_WLIST -- make the work list larger +** +** Parameters: +** none. +** +** 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. +*/ + +void +grow_wlist() +{ + if (tTd(41, 1)) + printf("grow_wlist: WorkListSize=%d\n", WorkListSize); + if (WorkList == NULL) + { + WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1)); + WorkListSize = QUEUESEGSIZE; + } + else + { + int newsize = WorkListSize + QUEUESEGSIZE; + WORK *newlist = (WORK *) realloc((char *)WorkList, + (unsigned)sizeof(WORK) * (newsize + 1)); + + if (newlist != NULL) + { + WorkListSize = newsize; + WorkList = newlist; + if (LogLevel > 1) + { + sm_syslog(LOG_NOTICE, NOQID, + "grew WorkList for %s to %d", + QueueDir, WorkListSize); + } + } + else if (LogLevel > 0) + { + sm_syslog(LOG_ALERT, NOQID, + "FAILED to grow WorkList for %s to %d", + QueueDir, newsize); + } + } + if (tTd(41, 1)) + printf("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 +** +** Side Effects: +** none. +*/ + +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 +** +** Side Effects: +** none. +*/ + +int +workcmpf1(a, b) + register WORK *a; + register WORK *b; +{ + int i; + extern int sm_strcasecmp __P((char *, char *)); + + /* host name */ + if (a->w_host != NULL && b->w_host == NULL) + 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 a->w_pri - b->w_pri; +} + /* +** 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 +** +** Side Effects: +** none. +*/ + +int +workcmpf2(a, b) + register WORK *a; + register WORK *b; +{ + int i; + extern int sm_strcasecmp __P((char *, char *)); + + /* lock status */ + if (a->w_lock != b->w_lock) + 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 a->w_pri - b->w_pri; +} + /* +** 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 +** +** Side Effects: +** none. +*/ + +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; +} + /* +** DOWORK -- do a work request. +** +** Parameters: +** 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(id, forkflag, requeueflag, e) + char *id; + bool forkflag; + bool requeueflag; + register ENVELOPE *e; +{ + register pid_t pid; + extern bool readqf __P((ENVELOPE *)); + + if (tTd(40, 1)) + printf("dowork(%s)\n", id); + + /* + ** Fork for work. + */ + + if (forkflag) + { + 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 + { + /* 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. + */ + + /* set basic modes, etc. */ + (void) alarm(0); + clearenvelope(e, FALSE); + e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; + e->e_sendmode = SM_DELIVER; + e->e_errormode = EM_MAIL; + e->e_id = id; + GrabTo = UseErrorsTo = FALSE; + ExitStat = EX_OK; + if (forkflag) + { + disconnect(1, e); + OpMode = MD_DELIVER; + } + setproctitle("%s: from queue", id); + if (LogLevel > 76) + sm_syslog(LOG_DEBUG, e->e_id, + "dowork, pid=%d", + getpid()); + + /* don't use the headers from sendmail.cf... */ + e->e_header = NULL; + + /* read the queue control file -- return if locked */ + if (!readqf(e)) + { + if (tTd(40, 4) && e->e_id != NULL) + printf("readqf(%s) failed\n", e->e_id); + e->e_id = NULL; + if (forkflag) + exit(EX_OK); + else + return 0; + } + + e->e_flags |= EF_INQUEUE; + eatheader(e, requeueflag); + + if (requeueflag) + queueup(e, FALSE); + + /* do the delivery */ + sendall(e, SM_DELIVER); + + /* finish up and exit */ + if (forkflag) + finis(); + else + dropenvelope(e, TRUE); + } + e->e_id = NULL; + return pid; +} + /* +** READQF -- read queue file and set up environment. +** +** Parameters: +** e -- the envelope of the job to run. +** +** Returns: +** TRUE if it successfully read the queue file. +** FALSE otherwise. +** +** Side Effects: +** The queue file is returned locked. +*/ + +bool +readqf(e) + register ENVELOPE *e; +{ + register FILE *qfp; + ADDRESS *ctladdr; + struct stat st; + char *bp; + int qfver = 0; + long hdrsize = 0; + register char *p; + char *orcpt = NULL; + bool nomore = FALSE; + char qf[MAXQFNAME]; + char buf[MAXLINE]; + extern ADDRESS *setctluser __P((char *, int)); + + /* + ** Read and process the file. + */ + + strcpy(qf, queuename(e, 'q')); + qfp = fopen(qf, "r+"); + if (qfp == NULL) + { + if (tTd(40, 8)) + printf("readqf(%s): fopen failure (%s)\n", + qf, errstring(errno)); + if (errno != ENOENT) + syserr("readqf: no control file %s", qf); + return FALSE; + } + + if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) + { + /* being processed by another queuer */ + if (Verbose || tTd(40, 8)) + printf("%s: locked\n", e->e_id); + if (LogLevel > 19) + sm_syslog(LOG_DEBUG, e->e_id, "locked"); + (void) fclose(qfp); + return FALSE; + } + + /* + ** Check the queue file for plausibility to avoid attacks. + */ + + if (fstat(fileno(qfp), &st) < 0) + { + /* must have been being processed by someone else */ + if (tTd(40, 8)) + printf("readqf(%s): fstat failure (%s)\n", + qf, errstring(errno)); + fclose(qfp); + return FALSE; + } + + if ((st.st_uid != geteuid() && geteuid() != RealUid) || + bitset(S_IWOTH|S_IWGRP, st.st_mode)) + { + if (LogLevel > 0) + { + sm_syslog(LOG_ALERT, e->e_id, + "bogus queue file, uid=%d, mode=%o", + st.st_uid, st.st_mode); + } + if (tTd(40, 8)) + printf("readqf(%s): bogus file\n", qf); + loseqfile(e, "bogus file uid in mqueue"); + fclose(qfp); + return FALSE; + } + + if (st.st_size == 0) + { + /* must be a bogus file -- if also old, just remove it */ + if (st.st_ctime + 10 * 60 < curtime()) + { + qf[0] = 'd'; + (void) unlink(qf); + qf[0] = 'q'; + (void) unlink(qf); + } + fclose(qfp); + 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. + */ + + fclose(qfp); + return FALSE; + } + + /* good file -- save this lock */ + e->e_lockfp = qfp; + + /* do basic system initialization */ + initsys(e); + define('i', e->e_id, e); + + LineNumber = 0; + e->e_flags |= EF_GLOBALERRS; + OpMode = MD_DELIVER; + ctladdr = NULL; + e->e_dfino = -1; + e->e_msgsize = -1; + while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) + { + register char *p; + u_long qflags; + ADDRESS *q; + int mid; + auto char *ep; + + if (tTd(40, 4)) + printf("+++++ %s\n", bp); + if (nomore) + { + /* hack attack */ + syserr("SECURITY ALERT: extra data in qf: %s", bp); + fclose(qfp); + loseqfile(e, "bogus queue line"); + return FALSE; + } + switch (bp[0]) + { + case 'V': /* queue file version number */ + qfver = atoi(&bp[1]); + if (qfver <= QF_VERSION) + break; + syserr("Version number in qf (%d) greater than max (%d)", + qfver, QF_VERSION); + fclose(qfp); + loseqfile(e, "unsupported qf file version"); + return FALSE; + + case 'C': /* specify controlling user */ + ctladdr = setctluser(&bp[1], qfver); + break; + + case 'Q': /* original recipient */ + orcpt = newstr(&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; + } + } + } + else + qflags |= QPRIMARY; + q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e); + if (q != NULL) + { + q->q_alias = ctladdr; + if (qfver >= 1) + q->q_flags &= ~Q_PINGFLAGS; + q->q_flags |= qflags; + q->q_orcpt = orcpt; + (void) recipient(q, &e->e_sendqueue, 0, e); + } + orcpt = NULL; + break; + + case 'E': /* specify error recipient */ + /* no longer used */ + break; + + case 'H': /* header */ + (void) chompheader(&bp[1], FALSE, NULL, e); + hdrsize += strlen(&bp[1]); + break; + + case 'L': /* Solaris Content-Length: */ + case 'M': /* message */ + /* ignore this; we want a new message next time */ + break; + + case 'S': /* sender */ + setsender(newstr(&bp[1]), e, NULL, '\0', TRUE); + break; + + case 'B': /* body type */ + e->e_bodytype = newstr(&bp[1]); + break; + +#if _FFR_SAVE_CHARSET + case 'X': /* character set */ + e->e_charset = newstr(&bp[1]); + break; +#endif + + case 'D': /* data file name */ + /* obsolete -- ignore */ + break; + + case 'T': /* init time */ + e->e_ctime = atol(&bp[1]); + break; + + case 'I': /* data file's inode number */ + /* regenerated below */ + break; + + case 'K': /* time of last deliver attempt */ + e->e_dtime = atol(&buf[1]); + break; + + case 'N': /* number of delivery attempts */ + e->e_ntries = atoi(&buf[1]); + + /* if this has been tried recently, let it be */ + if (e->e_ntries > 0 && + MinQueueAge > 0 && e->e_dtime <= curtime() && + curtime() < e->e_dtime + MinQueueAge) + { + char *howlong = pintvl(curtime() - e->e_dtime, TRUE); + extern void unlockqueue __P((ENVELOPE *)); + + if (Verbose || tTd(40, 8)) + printf("%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); + return FALSE; + } + break; + + case 'P': /* message priority */ + e->e_msgpriority = atol(&bp[1]) + WkTimeFact; + break; + + case 'F': /* flag bits */ + if (strncmp(bp, "From ", 5) == 0) + { + /* we are being spoofed! */ + syserr("SECURITY ALERT: bogus qf line %s", bp); + fclose(qfp); + loseqfile(e, "bogus queue line"); + return FALSE; + } + for (p = &bp[1]; *p != '\0'; p++) + { + switch (*p) + { + case 'w': /* warning sent */ + e->e_flags |= EF_WARNING; + break; + + case 'r': /* response */ + e->e_flags |= EF_RESPONSE; + break; + + 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; + } + } + break; + + case 'Z': /* original envelope id from ESMTP */ + e->e_envid = newstr(&bp[1]); + break; + + case '$': /* define macro */ + mid = macid(&bp[1], &ep); + define(mid, newstr(ep), e); + break; + + case '.': /* terminate file */ + nomore = TRUE; + break; + + default: + syserr("readqf: %s: line %d: bad line \"%s\"", + qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); + fclose(qfp); + loseqfile(e, "unrecognized line"); + return FALSE; + } + + if (bp != buf) + 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; + } + + /* + ** Arrange to read the data file. + */ + + p = queuename(e, 'd'); + e->e_dfp = fopen(p, "r"); + if (e->e_dfp == NULL) + { + syserr("readqf: cannot open %s", p); + } + else + { + e->e_flags |= EF_HAS_DF; + if (fstat(fileno(e->e_dfp), &st) >= 0) + { + e->e_msgsize = st.st_size + hdrsize; + e->e_dfdev = st.st_dev; + e->e_dfino = st.st_ino; + } + } + + return TRUE; +} + /* +** 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() +{ + register WORK *w; + FILE *f; + int nrequests; + char buf[MAXLINE]; + + /* + ** 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 + + if (stat(QueueDir, &st) < 0) + { + syserr("Cannot stat %s", QueueDir); + return; + } +# ifdef NGROUPS_MAX + n = NGROUPS_MAX; + while (--n >= 0) + { + if (InitialGidSet[n] == st.st_gid) + break; + } + if (n < 0 && RealGid != st.st_gid) +# else + if (RealGid != st.st_gid) +# endif + { + usrerr("510 You are not permitted to see the queue"); + setstat(EX_NOPERM); + return; + } + } + + /* + ** Read and order the queue. + */ + + nrequests = orderq(TRUE); + + /* + ** Print the work list that we have read. + */ + + /* first see if there is anything */ + if (nrequests <= 0) + { + printf("Mail queue is empty\n"); + return; + } + + CurrentLA = getla(); /* get load average */ + + printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); + if (MaxQueueRun > 0 && nrequests > MaxQueueRun) + printf(", only %d printed", MaxQueueRun); + if (Verbose) + printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); + else + printf(")\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 statmsg[MAXLINE]; + char bodytype[MAXNAME + 1]; + + printf("%8s", w->w_name + 2); + f = fopen(w->w_name, "r"); + if (f == NULL) + { + printf(" (job completed)\n"); + errno = 0; + continue; + } + w->w_name[0] = 'd'; + if (stat(w->w_name, &st) >= 0) + dfsize = st.st_size; + else + dfsize = -1; + if (w->w_lock) + printf("*"); + else if (w->w_tooyoung) + printf("-"); + else if (shouldqueue(w->w_pri, w->w_ctime)) + printf("X"); + else + printf(" "); + errno = 0; + + statmsg[0] = bodytype[0] = '\0'; + qfver = 0; + while (fgets(buf, sizeof buf, f) != NULL) + { + register int i; + register char *p; + + 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; + bcopy(&buf[1], statmsg, i); + statmsg[i] = '\0'; + break; + + case 'B': /* body type */ + if ((i = strlen(&buf[1])) >= sizeof bodytype) + i = sizeof bodytype - 1; + bcopy(&buf[1], bodytype, i); + bodytype[i] = '\0'; + break; + + case 'S': /* sender name */ + if (Verbose) + printf("%8ld %10ld%c%.12s %.78s", + dfsize, + w->w_pri, + bitset(EF_WARNING, flags) ? '+' : ' ', + ctime(&submittime) + 4, + &buf[1]); + else + printf("%8ld %.16s %.45s", dfsize, + ctime(&submittime), &buf[1]); + if (statmsg[0] != '\0' || bodytype[0] != '\0') + { + printf("\n %10.10s", bodytype); + if (statmsg[0] != '\0') + printf(" (%.*s)", + Verbose ? 100 : 60, + statmsg); + } + break; + + case 'C': /* controlling user */ + if (Verbose) + printf("\n\t\t\t\t (---%.74s---)", + &buf[1]); + break; + + case 'R': /* recipient name */ + p = &buf[1]; + if (qfver >= 1) + { + p = strchr(p, ':'); + if (p == NULL) + break; + p++; + } + if (Verbose) + printf("\n\t\t\t\t\t %.78s", p); + else + printf("\n\t\t\t\t %.45s", p); + 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) + printf(" (no control file)"); + printf("\n"); + (void) fclose(f); + } +} + +# endif /* QUEUE */ + /* +** QUEUENAME -- build a file name in the queue directory for this envelope. +** +** Assigns an id code if one does not already exist. +** This code is very careful to avoid trashing existing files +** under any circumstances. +** +** Parameters: +** e -- envelope to build it in/from. +** type -- the file type, used as the first character +** of the file name. +** +** Returns: +** a pointer to the new file name (in a static buffer). +** +** Side Effects: +** If no id code is already assigned, queuename will +** assign an id code, create a qf file, and leave a +** locked, open-for-write file pointer in the envelope. +*/ + +#ifndef ENOLCK +# define ENOLCK -1 +#endif +#ifndef ENOSPC +# define ENOSPC -1 +#endif + +char * +queuename(e, type) + register ENVELOPE *e; + int type; +{ + static pid_t pid = -1; + static char c0; + static char c1; + static char c2; + time_t now; + struct tm *tm; + static char buf[MAXNAME + 1]; + + if (e->e_id == NULL) + { + char qf[MAXQFNAME]; + + /* find a unique id */ + if (pid != getpid()) + { + /* new process -- start back at "AA" */ + pid = getpid(); + now = curtime(); + tm = localtime(&now); + c0 = 'A' + tm->tm_hour; + c1 = 'A'; + c2 = 'A' - 1; + } + (void) snprintf(qf, sizeof qf, "qf%cAA%05d", c0, pid); + + while (c1 < '~' || c2 < 'Z') + { + int i; + int attempts = 0; + + if (c2 >= 'Z') + { + c1++; + c2 = 'A' - 1; + } + qf[3] = c1; + qf[4] = ++c2; + if (tTd(7, 20)) + printf("queuename: trying \"%s\"\n", qf); + + i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); + if (i < 0) + { + if (errno == EEXIST) + continue; + syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", + qf, QueueDir, geteuid()); + exit(EX_UNAVAILABLE); + } + do + { + if (attempts > 0) + sleep(attempts); + e->e_lockfp = 0; + if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB)) + { + e->e_lockfp = fdopen(i, "w"); + break; + } + } while ((errno == ENOLCK || errno == ENOSPC) && + attempts++ < 4); + + /* Successful lock */ + if (e->e_lockfp != 0) + break; + +#if !HASFLOCK + if (errno != EAGAIN && errno != EACCES) +#else + if (errno != EWOULDBLOCK) +#endif + { + syserr("queuename: Cannot lock \"%s\" in \"%s\" (euid=%d)", + qf, QueueDir, geteuid()); + exit(EX_OSERR); + } + + /* a reader got the file; abandon it and try again */ + (void) close(i); + } + if (c1 >= '~' && c2 >= 'Z') + { + syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", + qf, QueueDir, geteuid()); + exit(EX_OSERR); + } + e->e_id = newstr(&qf[2]); + define('i', e->e_id, e); + if (tTd(7, 1)) + printf("queuename: assigned id %s, env=%lx\n", + e->e_id, (u_long) e); + if (tTd(7, 9)) + { + printf(" lockfd="); + dumpfd(fileno(e->e_lockfp), TRUE, FALSE); + } + if (LogLevel > 93) + sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); + } + + if (type == '\0') + return (NULL); + (void) snprintf(buf, sizeof buf, "%cf%s", type, e->e_id); + if (tTd(7, 2)) + printf("queuename: %s\n", buf); + return (buf); +} + /* +** 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)) + printf("unlockqueue(%s)\n", + e->e_id == NULL ? "NOQUEUE" : e->e_id); + + /* if there is a lock file in the envelope, close it */ + if (e->e_lockfp != NULL) + xfclose(e->e_lockfp, "unlockqueue", e->e_id); + 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)) + xunlink(queuename(e, 'x')); + +} + /* +** 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 qf file. +** +** Returns: +** An address descriptor for the controlling user. +** +** Side Effects: +** none. +*/ + +ADDRESS * +setctluser(user, qfver) + char *user; + int qfver; +{ + 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 *) xalloc(sizeof *a); + bzero((char *) a, sizeof *a); + + if (*user == '\0') + { + p = NULL; + a->q_user = newstr(DefUser); + } + else if (*user == ':') + { + p = &user[1]; + a->q_user = newstr(p); + } + else + { + p = strtok(user, ":"); + a->q_user = newstr(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) + a->q_flags |= QGOODUID; + } + else if ((pw = sm_getpwnam(user)) != NULL) + { + if (strcmp(pw->pw_dir, "/") == 0) + a->q_home = ""; + else + a->q_home = newstr(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 = a->q_user; + else + a->q_paddr = newstr(p); + return a; +} + /* +** LOSEQFILE -- save the qf as Qf and 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; +{ + char *p; + char buf[MAXQFNAME + 1]; + + if (e == NULL || e->e_id == NULL) + return; + p = queuename(e, 'q'); + if (strlen(p) > MAXQFNAME) + { + syserr("loseqfile: queuename (%s) too long", p); + return; + } + strcpy(buf, p); + p = queuename(e, 'Q'); + if (rename(buf, p) < 0) + syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); + else if (LogLevel > 0) + sm_syslog(LOG_ALERT, e->e_id, + "Losing %s: %s", buf, why); +} diff --git a/contrib/sendmail/src/readcf.c b/contrib/sendmail/src/readcf.c new file mode 100644 index 0000000..db71937 --- /dev/null +++ b/contrib/sendmail/src/readcf.c @@ -0,0 +1,2868 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)readcf.c 8.230 (Berkeley) 6/5/98"; +#endif /* not lint */ + +# include "sendmail.h" +# include +#if NAMED_BIND +# include +#endif + +/* +** 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. +** Pname=value Set precedence name to value. +** 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; +{ + FILE *cf; + int ruleset = 0; + char *q; + struct rewrite *rwp = NULL; + char *bp; + auto char *ep; + int nfuzzy; + char *file; + bool optional; + int mid; + register char *p; + int sff = SFF_OPENASROOT; + struct stat statb; + char buf[MAXLINE]; + char exbuf[MAXLINE]; + char pvpbuf[MAXLINE + MAXATOM]; + static char *null_list[1] = { NULL }; + extern char **copyplist __P((char **, bool)); + extern char *munchstring __P((char *, char **, int)); + extern void fileclass __P((int, char *, char *, bool, bool)); + extern void toomany __P((int, int)); + extern void translate_dollars __P((char *)); + extern void inithostmaps __P((void)); + + FileName = cfname; + LineNumber = 0; + + if (DontLockReadFiles) + sff |= SFF_NOLOCK; + cf = safefopen(cfname, O_RDONLY, 0444, sff); + if (cf == NULL) + { + syserr("cannot open"); + exit(EX_OSFILE); + } + + if (fstat(fileno(cf), &statb) < 0) + { + syserr("cannot fstat"); + exit(EX_OSFILE); + } + + if (!S_ISREG(statb.st_mode)) + { + syserr("not a plain file"); + exit(EX_OSFILE); + } + + if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) + { + if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS) + fprintf(stderr, "%s: WARNING: dangerous write permissions\n", + FileName); + if (LogLevel > 0) + sm_syslog(LOG_CRIT, NOQID, + "%s: WARNING: dangerous write permissions", + FileName); + } + +#ifdef XLA + xla_zero(); +#endif + + while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) + { + if (bp[0] == '#') + { + if (bp != buf) + free(bp); + continue; + } + + /* do macro expansion mappings */ + translate_dollars(bp); + + /* interpret this line */ + errno = 0; + switch (bp[0]) + { + case '\0': + case '#': /* comment */ + break; + + case 'R': /* rewriting rule */ + 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, NULL); + nfuzzy = 0; + if (rwp->r_lhs != NULL) + { + register char **ap; + + rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); + + /* count the number of fuzzy matches in LHS */ + for (ap = rwp->r_lhs; *ap != NULL; ap++) + { + char *botch; + + botch = NULL; + switch (**ap & 0377) + { + case MATCHZANY: + case MATCHANY: + case MATCHONE: + case MATCHCLASS: + case MATCHNCLASS: + nfuzzy++; + break; + + case MATCHREPL: + botch = "$0-$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); + } + } + else + { + syserr("R line: null LHS"); + 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, NULL); + if (rwp->r_rhs != NULL) + { + register char **ap; + + rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); + + /* check no out-of-bounds replacements */ + nfuzzy += '0'; + for (ap = rwp->r_rhs; *ap != NULL; ap++) + { + char *botch; + + botch = NULL; + switch (**ap & 0377) + { + case MATCHREPL: + if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) + { + syserr("replacement $%c out of bounds", + (*ap)[1]); + } + break; + + case MATCHZANY: + botch = "$*"; + break; + + case MATCHANY: + botch = "$+"; + break; + + case MATCHONE: + botch = "$-"; + break; + + case MATCHCLASS: + botch = "$="; + break; + + case MATCHNCLASS: + botch = "$~"; + break; + } + if (botch != NULL) + syserr("Inappropriate use of %s on RHS", + botch); + } + } + 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 || tTd(37, 1)) + printf("WARNING: Ruleset %s has multiple definitions\n", + &bp[1]); + while (rwp->r_next != NULL) + rwp = rwp->r_next; + } + break; + + case 'D': /* macro definition */ + mid = macid(&bp[1], &ep); + p = munchstring(ep, NULL, '\0'); + define(mid, newstr(p), e); + break; + + case 'H': /* required header line */ + (void) chompheader(&bp[1], TRUE, NULL, e); + break; + + case 'C': /* word class */ + case 'T': /* trusted user (set class `t') */ + if (bp[0] == 'C') + { + mid = macid(&bp[1], &ep); + 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(&bp[1], &ep); + 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++; + } + else + optional = FALSE; + file = p; + if (*file == '|') + p = "%s"; + else + { + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p == '\0') + p = "%s"; + else + { + *p = '\0'; + while (isascii(*++p) && isspace(*p)) + continue; + } + } + fileclass(mid, file, p, safe, optional); + break; + +#ifdef XLA + case 'L': /* extended load average description */ + xla_init(&bp[1]); + break; +#endif + +#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 + + 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 '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'; + define('w', macvalue('w', e), e); + } + if (ConfigLevel >= 6) + { + ColonOkInAddr = FALSE; + } + + /* + ** Look for vendor code. + */ + + if (*ep++ == '/') + { + extern bool setvendor __P((char *)); + + /* 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'; + setuserenv(&bp[1], p); + break; + + default: + badline: + syserr("unknown configuration line \"%s\"", bp); + } + if (bp != buf) + free(bp); + } + if (ferror(cf)) + { + syserr("I/O read error"); + exit(EX_OSFILE); + } + fclose(cf); + FileName = NULL; + + /* initialize host maps from local service tables */ + inithostmaps(); + + /* 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; + } + } + +#ifdef HESIOD + nmaps = switch_map_find("passwd", maptype, mapreturn); + UseHesiod = FALSE; + if (nmaps > 0 && nmaps <= MAXMAPSTACK) + { + register int mapno; + + for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++) + { + if (strcmp(maptype[mapno], "hesiod") == 0) + UseHesiod = TRUE; + } + } +#endif + } +} + /* +** TRANSLATE_DOLLARS -- convert $x into internal form +** +** Actually does all appropriate pre-processing of a config line +** to turn it into internal form. +** +** Parameters: +** bp -- the buffer to translate. +** +** Returns: +** None. The buffer is translated in place. Since the +** translations always make the buffer shorter, this is +** safe without a size parameter. +*/ + +void +translate_dollars(bp) + char *bp; +{ + register char *p; + auto char *ep; + + for (p = bp; *p != '\0'; p++) + { + if (*p == '#' && p > bp && ConfigLevel >= 3) + { + /* this is an on-line comment */ + register char *e; + + switch (*--p & 0377) + { + case MACROEXPAND: + /* it's from $# -- let it go through */ + p++; + break; + + case '\\': + /* it's backslash escaped */ + (void) strcpy(p, p + 1); + break; + + default: + /* delete preceeding white space */ + while (isascii(*p) && isspace(*p) && + *p != '\n' && p > bp) + p--; + if ((e = strchr(++p, '\n')) != NULL) + (void) strcpy(p, e); + else + *p-- = '\0'; + break; + } + continue; + } + + if (*p != '$' || p[1] == '\0') + continue; + + if (p[1] == '$') + { + /* actual dollar sign.... */ + (void) strcpy(p, p + 1); + 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(p, &ep); + if (ep != p) + strcpy(p + 1, ep); + } + + /* strip trailing white space from the line */ + while (--p > bp && isascii(*p) && isspace(*p)) + *p = '\0'; +} + /* +** 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. +*/ + +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. +** 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. +*/ + +void +fileclass(class, filename, fmt, safe, optional) + int class; + char *filename; + char *fmt; + bool safe; + bool optional; +{ + FILE *f; + int sff; + pid_t pid; + register char *p; + char buf[MAXLINE]; + + if (tTd(37, 2)) + printf("fileclass(%s, fmt=%s)\n", filename, fmt); + + if (filename[0] == '|') + { + auto int fd; + int i; + char *argv[MAXPV + 1]; + + i = 0; + for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t")) + { + if (i >= MAXPV) + break; + argv[i++] = p; + } + argv[i] = NULL; + pid = prog_open(argv, &fd, CurEnv); + if (pid < 0) + f = NULL; + else + f = fdopen(fd, "r"); + } + else + { + pid = -1; + sff = SFF_REGONLY; + if (!bitset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail)) + sff |= SFF_SAFEDIRPATH; + if (!bitset(DBS_LINKEDCLASSFILEINWRITABLEDIR, DontBlameSendmail)) + sff |= SFF_NOWLINK; + if (safe) + sff |= SFF_OPENASROOT; + 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 (fgets(buf, sizeof buf, f) != NULL) + { + register char *p; +# if SCANF + char wordbuf[MAXLINE + 1]; +# endif + + if (buf[0] == '#') + continue; +# if SCANF + if (sscanf(buf, fmt, wordbuf) != 1) + continue; + p = wordbuf; +# else /* SCANF */ + p = buf; +# endif /* SCANF */ + + /* + ** Break up the match into words. + */ + + while (*p != '\0') + { + register char *q; + + /* strip leading spaces */ + while (isascii(*p) && isspace(*p)) + p++; + if (*p == '\0') + break; + + /* find the end of the word */ + q = p; + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p != '\0') + *p++ = '\0'; + + /* enter the word in the symbol table */ + setclass(class, q); + } + } + + (void) fclose(f); + 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 +** R -- the recipient rewriting set +** S -- the sender rewriting set +** T -- the mailer type (for DSNs) +** U -- the uid to run as +** 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; + extern int NextMailer; + extern char **makeargv __P((char *)); + extern char *munchstring __P((char *, char **, int)); + + /* allocate a mailer and set up defaults */ + m = (struct mailer *) xalloc(sizeof *m); + bzero((char *) m, sizeof *m); + + /* 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"); + m->m_name = newstr(line); + + /* 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') + syserr("mailer %s: empty path name", m->m_name); + m->m_mailer = newstr(p); + break; + + case 'F': /* flags */ + for (; *p != '\0'; p++) + if (!(isascii(*p) && isspace(*p))) + setbitn(*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); + m->m_eol = newstr(p); + break; + + case 'A': /* argument vector */ + if (*p == '\0') + syserr("mailer %s: null argument vector", + m->m_name); + m->m_argv = makeargv(p); + break; + + case 'M': /* maximum message size */ + m->m_maxsize = atol(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); + m->m_execdir = newstr(p); + break; + + case 'C': /* default charset */ + if (*p == '\0') + syserr("mailer %s: null charset", m->m_name); + m->m_defcharset = newstr(p); + 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); + pw = sm_getpwnam(q); + if (pw == NULL) + syserr("readcf: mailer U= flag: unknown user %s", q); + 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); + gr = getgrnam(q); + if (gr == NULL) + syserr("readcf: mailer U= flag: unknown group %s", q); + else + m->m_gid = gr->gr_gid; + } + else + { + m->m_gid = strtol(p, NULL, 0); + } + break; + } + + p = delimptr; + } + + /* 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; + } + + /* 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, "[IPC]") == 0 || + strcmp(m->m_mailer, "[TCP]") == 0) + { + if (m->m_mtatype == NULL) + m->m_mtatype = "dns"; + if (m->m_addrtype == NULL) + m->m_addrtype = "rfc822"; + if (m->m_diagtype == NULL) + m->m_diagtype = "smtp"; + } + + 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"); + } + else if (strcmp(m->m_argv[0], "FILE") != 0) + { + syserr("M%s: first argument in [FILE] mailer must be FILE", + m->m_name); + } + } + + 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++) + { + char *p; + + 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; + free(s->s_mailer); + } + 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. +*/ + +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); +} + /* +** MAKEARGV -- break up a string into words +** +** Parameters: +** p -- the string to break up. +** +** Returns: +** a char **argv (dynamically allocated) +** +** Side Effects: +** munges p. +*/ + +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); + bcopy((char *) argv, (char *) avp, 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; + printf("\n----Rule Set %d:", ruleset); + + for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) + { + printf("\nLHS:"); + printav(rwp->r_lhs); + printf("RHS:"); + printav(rwp->r_rhs); + } + } +} + /* +** PRINTMAILER -- print mailer structure (for debugging) +** +** Parameters: +** m -- the mailer to print +** +** Returns: +** none. +*/ + +void +printmailer(m) + register MAILER *m; +{ + int j; + + printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=", + m->m_mno, m->m_name, + m->m_mailer, m->m_se_rwset, m->m_sh_rwset, + m->m_re_rwset, m->m_rh_rwset, m->m_maxsize, + (int) m->m_uid, (int) m->m_gid); + for (j = '\0'; j <= '\177'; j++) + if (bitnset(j, m->m_flags)) + (void) putchar(j); + printf(" L=%d E=", m->m_linelimit); + xputs(m->m_eol); + if (m->m_defcharset != NULL) + printf(" C=%s", m->m_defcharset); + printf(" T=%s/%s/%s", + m->m_mtatype == NULL ? "" : m->m_mtatype, + m->m_addrtype == NULL ? "" : m->m_addrtype, + m->m_diagtype == NULL ? "" : m->m_diagtype); + if (m->m_argv != NULL) + { + char **a = m->m_argv; + + printf(" A="); + while (*a != NULL) + { + if (a != m->m_argv) + printf(" "); + xputs(*a++); + } + } + printf("\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 BITMAP StickyOpt; /* set if option is stuck */ +extern void settimeout __P((char *, char *)); + + +#if NAMED_BIND + +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 }, + { "true", 0 }, /* avoid error on old syntax */ + { NULL, 0 } +}; + +#endif + +struct optioninfo +{ + char *o_name; /* long name of option */ + u_char o_code; /* short name of option */ + bool o_safe; /* safe for random people to use */ +} OptionTab[] = +{ + { "SevenBitInput", '7', TRUE }, +#if MIME8TO7 + { "EightBitMode", '8', TRUE }, +#endif + { "AliasFile", 'A', FALSE }, + { "AliasWait", 'a', FALSE }, + { "BlankSub", 'B', FALSE }, + { "MinFreeBlocks", 'b', TRUE }, + { "CheckpointInterval", 'C', TRUE }, + { "HoldExpensive", 'c', FALSE }, + { "AutoRebuildAliases", 'D', FALSE }, + { "DeliveryMode", 'd', TRUE }, + { "ErrorHeader", 'E', FALSE }, + { "ErrorMode", 'e', TRUE }, + { "TempFileMode", 'F', FALSE }, + { "SaveFromLine", 'f', FALSE }, + { "MatchGECOS", 'G', FALSE }, + { "HelpFile", 'H', FALSE }, + { "MaxHopCount", 'h', FALSE }, + { "ResolverOptions", 'I', FALSE }, + { "IgnoreDots", 'i', TRUE }, + { "ForwardPath", 'J', FALSE }, + { "SendMimeErrors", 'j', TRUE }, + { "ConnectionCacheSize", 'k', FALSE }, + { "ConnectionCacheTimeout", 'K', FALSE }, + { "UseErrorsTo", 'l', FALSE }, + { "LogLevel", 'L', TRUE }, + { "MeToo", 'm', TRUE }, + { "CheckAliases", 'n', FALSE }, + { "OldStyleHeaders", 'o', TRUE }, + { "DaemonPortOptions", 'O', FALSE }, + { "PrivacyOptions", 'p', TRUE }, + { "PostmasterCopy", 'P', FALSE }, + { "QueueFactor", 'q', FALSE }, + { "QueueDirectory", 'Q', FALSE }, + { "DontPruneRoutes", 'R', FALSE }, + { "Timeout", 'r', FALSE }, + { "StatusFile", 'S', FALSE }, + { "SuperSafe", 's', TRUE }, + { "QueueTimeout", 'T', FALSE }, + { "TimeZoneSpec", 't', FALSE }, + { "UserDatabaseSpec", 'U', FALSE }, + { "DefaultUser", 'u', FALSE }, + { "FallbackMXhost", 'V', FALSE }, + { "Verbose", 'v', TRUE }, + { "TryNullMXList", 'w', FALSE }, + { "QueueLA", 'x', FALSE }, + { "RefuseLA", 'X', FALSE }, + { "RecipientFactor", 'y', FALSE }, + { "ForkEachJob", 'Y', FALSE }, + { "ClassFactor", 'z', FALSE }, + { "RetryFactor", 'Z', FALSE }, +#define O_QUEUESORTORD 0x81 + { "QueueSortOrder", O_QUEUESORTORD, TRUE }, +#define O_HOSTSFILE 0x82 + { "HostsFile", O_HOSTSFILE, FALSE }, +#define O_MQA 0x83 + { "MinQueueAge", O_MQA, TRUE }, +#define O_DEFCHARSET 0x85 + { "DefaultCharSet", O_DEFCHARSET, TRUE }, +#define O_SSFILE 0x86 + { "ServiceSwitchFile", O_SSFILE, FALSE }, +#define O_DIALDELAY 0x87 + { "DialDelay", O_DIALDELAY, TRUE }, +#define O_NORCPTACTION 0x88 + { "NoRecipientAction", O_NORCPTACTION, TRUE }, +#define O_SAFEFILEENV 0x89 + { "SafeFileEnvironment", O_SAFEFILEENV, FALSE }, +#define O_MAXMSGSIZE 0x8a + { "MaxMessageSize", O_MAXMSGSIZE, FALSE }, +#define O_COLONOKINADDR 0x8b + { "ColonOkInAddr", O_COLONOKINADDR, TRUE }, +#define O_MAXQUEUERUN 0x8c + { "MaxQueueRunSize", O_MAXQUEUERUN, TRUE }, +#define O_MAXCHILDREN 0x8d + { "MaxDaemonChildren", O_MAXCHILDREN, FALSE }, +#define O_KEEPCNAMES 0x8e + { "DontExpandCnames", O_KEEPCNAMES, FALSE }, +#define O_MUSTQUOTE 0x8f + { "MustQuoteChars", O_MUSTQUOTE, FALSE }, +#define O_SMTPGREETING 0x90 + { "SmtpGreetingMessage", O_SMTPGREETING, FALSE }, +#define O_UNIXFROM 0x91 + { "UnixFromLine", O_UNIXFROM, FALSE }, +#define O_OPCHARS 0x92 + { "OperatorChars", O_OPCHARS, FALSE }, +#define O_DONTINITGRPS 0x93 + { "DontInitGroups", O_DONTINITGRPS, FALSE }, +#define O_SLFH 0x94 + { "SingleLineFromHeader", O_SLFH, TRUE }, +#define O_ABH 0x95 + { "AllowBogusHELO", O_ABH, TRUE }, +#define O_CONNTHROT 0x97 + { "ConnectionRateThrottle", O_CONNTHROT, FALSE }, +#define O_UGW 0x99 + { "UnsafeGroupWrites", O_UGW, FALSE }, +#define O_DBLBOUNCE 0x9a + { "DoubleBounceAddress", O_DBLBOUNCE, FALSE }, +#define O_HSDIR 0x9b + { "HostStatusDirectory", O_HSDIR, FALSE }, +#define O_SINGTHREAD 0x9c + { "SingleThreadDelivery", O_SINGTHREAD, FALSE }, +#define O_RUNASUSER 0x9d + { "RunAsUser", O_RUNASUSER, FALSE }, +#if _FFR_DSN_RRT_OPTION +#define O_DSN_RRT 0x9e + { "RrtImpliesDsn", O_DSN_RRT, FALSE }, +#endif +#if _FFR_PIDFILE_OPTION +#define O_PIDFILE 0x9f + { "PidFile", O_PIDFILE, FALSE }, +#endif +#define O_DONTBLAMESENDMAIL 0xa0 + { "DontBlameSendmail", O_DONTBLAMESENDMAIL, FALSE }, +#define O_DPI 0xa1 + { "DontProbeInterfaces", O_DPI, FALSE }, +#define O_MAXRCPT 0xa2 + { "MaxRecipientsPerMessage", O_MAXRCPT, FALSE }, +#if _FFR_DEADLETTERDROP_OPTION +#define O_DEADLETTER 0xa3 + { "DeadLetterDrop", O_DEADLETTER, FALSE }, +#endif +#if _FFR_DONTLOCKFILESFORREAD_OPTION +#define O_DONTLOCK 0xa4 + { "DontLockFilesForRead", O_DONTLOCK, FALSE }, +#endif +#if _FFR_MAXALIASRECURSION_OPTION +#define O_MAXALIASRCSN 0xa5 + { "MaxAliasRecursion", O_MAXALIASRCSN, FALSE }, +#endif +#if _FFR_CONNECTONLYTO_OPTION +#define O_CNCTONLYTO 0xa6 + { "ConnectOnlyTo", O_CNCTONLYTO, FALSE }, +#endif +#if _FFR_TRUSTED_FILE_OWNER +#define O_TRUSTFILEOWN 0xa7 + { "TrustedFileOwner", O_TRUSTFILEOWN, FALSE }, +#endif + + { NULL, '\0', FALSE } +}; + + + +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 atobool __P((char *)); + extern time_t convtime __P((char *, char)); + extern int QueueLA; + extern int RefuseLA; + extern bool Warn_Q_option; + extern void setalias __P((char *)); + extern int atooct __P((char *)); + extern void setdefuser __P((void)); + extern void setdaemonoptions __P((char *)); + + 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 (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; + } + subopt = NULL; + } + + if (tTd(37, 1)) + { + printf(isascii(opt) && isprint(opt) ? + "setoption %s (%c).%s=" : + "setoption %s (0x%x).%s=", + o->o_name == NULL ? "" : o->o_name, + opt, + subopt == NULL ? "" : subopt); + xputs(val); + } + + /* + ** See if this option is preset for us. + */ + + if (!sticky && bitnset(opt, StickyOpt)) + { + if (tTd(37, 1)) + printf(" (ignored)\n"); + return; + } + + /* + ** Check to see if this option can be specified by this user. + */ + + if (!safe && RealUid == 0) + safe = TRUE; + if (!safe && !o->o_safe) + { + if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) + { + if (tTd(37, 1)) + printf(" (unsafe)"); + (void) drop_privileges(TRUE); + } + } + if (tTd(37, 1)) + printf("\n"); + + switch (opt & 0xff) + { + case '7': /* force seven-bit input */ + SevenBitInput = atobool(val); + break; + +#if MIME8TO7 + case '8': /* handling of 8-bit input */ + switch (*val) + { + case 'm': /* convert 8-bit, convert MIME */ + MimeMode = MM_CVTMIME|MM_MIME8BIT; + break; + + case 'p': /* pass 8 bit, convert MIME */ + MimeMode = MM_CVTMIME|MM_PASS8BIT; + break; + + case '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 + + default: + syserr("Unknown 8-bit mode %c", *val); + exit(EX_USAGE); + } + break; +#endif + + case 'A': /* set default alias file */ + if (val[0] == '\0') + setalias("aliases"); + else + setalias(val); + break; + + case 'a': /* look N minutes for "@:@" in alias file */ + if (val[0] == '\0') + SafeAlias = 5 * 60; /* five 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 */ + CheckpointInterval = atoi(val); + break; + + case 'd': /* delivery mode */ + switch (*val) + { + case '\0': + e->e_sendmode = SM_DELIVER; + break; + + case SM_QUEUE: /* queue only */ + case SM_DEFER: /* queue only and defer map lookups */ +#if !QUEUE + syserr("need QUEUE to set -odqueue or -oddefer"); +#endif /* QUEUE */ + /* fall through..... */ + + case SM_DELIVER: /* do everything */ + case SM_FORK: /* fork after verification */ + e->e_sendmode = *val; + break; + + default: + syserr("Unknown delivery mode %c", *val); + exit(EX_USAGE); + } + buf[0] = (char)e->e_sendmode; + buf[1] = '\0'; + define(macid("{deliveryMode}", NULL), newstr(buf), e); + break; + + case 'D': /* rebuild alias database as needed */ + AutoRebuild = atobool(val); + 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') + HelpFile = "sendmail.hf"; + else + 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 (strcasecmp(q, "HasWildcardMX") == 0) + { + HasWildcardMX = !clearmode; + continue; + } + for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) + { + if (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)) + printf("_res.options = %x, HasWildcardMX = %d\n", + (u_int) _res.options, HasWildcardMX); +#else + usrerr("name server (I option) specified but BIND not compiled in"); +#endif + 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 */ + 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 */ + mid = macid(val, &ep); + p = newstr(ep); + if (!safe) + cleanstrcpy(p, p, MAXNAME); + define(mid, p, CurEnv); + sticky = FALSE; + 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 DAEMON + setdaemonoptions(val); +#else + syserr("DaemonPortOptions (O option) set but DAEMON not compiled in"); +#endif + 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 (strcasecmp(val, pv->pv_name) == 0) + break; + } + if (pv->pv_name == NULL) + syserr("readcf: Op line: %s unrecognized", val); + 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); + else + settimeout(subopt, val); + break; + + case 'S': /* status file */ + if (val[0] == '\0') + StatFile = "sendmail.st"; + else + StatFile = newstr(val); + break; + + case 's': /* be super safe, even if expensive */ + SuperSafe = atobool(val); + break; + + case 'T': /* queue timeout */ + p = strchr(val, '/'); + if (p != NULL) + { + *p++ = '\0'; + settimeout("queuewarn", p); + } + settimeout("queuereturn", val); + 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 (*p == '.' || *p == '/' || *p == ':') + { + *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); + 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", + DefUid, UID_MAX); + } +#endif + + /* 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 '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; + + case O_QUEUESORTORD: /* queue sorting order */ + switch (*val) + { + case 'h': /* Host first */ + case 'H': + QueueSortOrder = QS_BYHOST; + break; + + case 'p': /* Priority order */ + case 'P': + QueueSortOrder = QS_BYPRIORITY; + break; + + case 't': /* Submission time */ + case 'T': + QueueSortOrder = QS_BYTIME; + break; + + default: + syserr("Invalid queue sort order \"%s\"", val); + } + break; + + case O_HOSTSFILE: /* pathname of /etc/hosts file */ + 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 */ + 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 (strcasecmp(val, "none") == 0) + NoRecipientAction = NRA_NO_ACTION; + else if (strcasecmp(val, "add-to") == 0) + NoRecipientAction = NRA_ADD_TO; + else if (strcasecmp(val, "add-apparently-to") == 0) + NoRecipientAction = NRA_ADD_APPARENTLY_TO; + else if (strcasecmp(val, "add-bcc") == 0) + NoRecipientAction = NRA_ADD_BCC; + else if (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 */ + 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 = atol(val); + break; + + case O_MAXCHILDREN: /* max # of children of daemon */ + MaxChildren = atoi(val); + break; + + case O_KEEPCNAMES: /* don't expand CNAME records */ + DontExpandCnames = atobool(val); + break; + + case O_MUSTQUOTE: /* must quote these characters in phrases */ + strcpy(buf, "@,;:\\()[]"); + if (strlen(val) < (SIZE_T) sizeof buf - 10) + strcat(buf, val); + 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) */ + 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)) + DontBlameSendmail |= DBS_GROUPWRITABLEFORWARDFILESAFE|DBS_GROUPWRITABLEINCLUDEFILESAFE; + break; + + case O_DBLBOUNCE: /* address to which to send double bounces */ + if (val[0] != '\0') + DoubleBounceAddr = newstr(val); + else + syserr("readcf: option DoubleBounceAddress: value required"); + break; + + case O_HSDIR: /* persistent host status directory */ + if (val[0] != '\0') + 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 (*p == '.' || *p == '/' || *p == ':') + { + *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); + else if (can_setuid) + { + if (*p == '\0') + RunAsUserName = newstr(val); + RunAsUid = pw->pw_uid; + RunAsGid = pw->pw_gid; + } + } +#ifdef UID_MAX + if (RunAsUid > UID_MAX) + { + syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored", + RunAsUid, UID_MAX); + } +#endif + if (*p != '\0') + { + if (isascii(*p) && isdigit(*p)) + { + if (can_setuid) + RunAsGid = atoi(p); + } + else + { + register struct group *gr; + + gr = getgrnam(p); + if (gr == NULL) + syserr("readcf: option RunAsUser: unknown group %s", + p); + else if (can_setuid) + RunAsGid = gr->gr_gid; + } + } + if (tTd(47, 5)) + printf("readcf: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid); + break; + +#if _FFR_DSN_RRT_OPTION + case O_DSN_RRT: + RrtImpliesDsn = atobool(val); + break; +#endif + +#if _FFR_PIDFILE_OPTION + case O_PIDFILE: + free(PidFile); + PidFile = newstr(val); + break; +#endif + + 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 (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) + DontBlameSendmail = DBS_SAFE; + else + DontBlameSendmail |= dbs->dbs_flag; + } + sticky = FALSE; + break; + + case O_DPI: + DontProbeInterfaces = atobool(val); + break; + + case O_MAXRCPT: + MaxRcptPerMsg = atoi(val); + break; + +#if _FFR_DEADLETTERDROP_OPTION + case O_DEADLETTER: + if (DeadLetterDrop != NULL) + free(DeadLetterDrop); + DeadLetterDrop = newstr(val); + break; +#endif + +#if _FFR_DONTLOCKFILESFORREAD_OPTION + case O_DONTLOCK: + DontLockReadFiles = atobool(val); + break; +#endif + +#if _FFR_MAXALIASRECURSION_OPTION + case O_MAXALIASRCSN: + MaxAliasRecursion = atoi(val); + break; +#endif + +#if _FFR_CONNECTONLYTO_OPTION + case O_CNCTONLYTO: + /* XXX should probably use gethostbyname */ + ConnectOnlyTo = inet_addr(val); + break; +#endif + +#if _FFR_TRUSTED_FILE_OWNER + case O_TRUSTFILEOWN: + if (isascii(*val) && isdigit(*val)) + TrustedFileUid = atoi(val); + else + { + register struct passwd *pw; + + TrustedFileUid = 0; + pw = sm_getpwnam(val); + if (pw == NULL) + syserr("readcf: option TrustedFileOwner: unknown user %s", val); + else + TrustedFileUid = pw->pw_uid; + } + +#ifdef UID_MAX + if (TrustedFileUid > UID_MAX) + { + syserr("readcf: option TrustedFileOwner: uid value (%ld) > UID_MAX (%ld)", + TrustedFileUid, UID_MAX); + TrustedFileUid = 0; + } +#endif + break; +#endif + + default: + if (tTd(37, 1)) + { + if (isascii(opt) && isprint(opt)) + printf("Warning: option %c unknown\n", opt); + else + printf("Warning: option 0x%x unknown\n", opt); + } + break; + } + if (sticky) + 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 (tTd(37, 8)) + printf("setclass(%s, %s)\n", macname(class), str); + s = stab(str, ST_CLASS, ST_ENTER); + setbitn(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)) + { + printf("map %s, class %s, flags %lx, file %s,\n", + s->s_map.map_mname, s->s_map.map_class->map_cname, + s->s_map.map_mflags, + s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); + printf("\tapp %s, domain %s, rebuild %s\n", + s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, + s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, + s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); + } + + 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; + + 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; + } + } + return ruleset; +} + /* +** INITTIMEOUTS -- parse and set timeout values +** +** Parameters: +** val -- a pointer to the values. If NULL, do initial +** settings. +** +** Returns: +** none. +** +** Side Effects: +** Initializes the TimeOuts structure +*/ + +#define SECONDS +#define MINUTES * 60 +#define HOUR * 3600 + +void +inittimeouts(val) + register char *val; +{ + register char *p; + extern time_t convtime __P((char *, char)); + + if (tTd(37, 2)) + printf("inittimeouts(%s)\n", val == NULL ? "" : val); + if (val == NULL) + { + TimeOuts.to_connect = (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) 30 SECONDS; +#else + TimeOuts.to_ident = (time_t) 0 SECONDS; +#endif + TimeOuts.to_fileopen = (time_t) 60 SECONDS; + if (tTd(37, 5)) + { + printf("Timeouts:\n"); + printf(" connect = %ld\n", (long)TimeOuts.to_connect); + printf(" initial = %ld\n", (long)TimeOuts.to_initial); + printf(" helo = %ld\n", (long)TimeOuts.to_helo); + printf(" mail = %ld\n", (long)TimeOuts.to_mail); + printf(" rcpt = %ld\n", (long)TimeOuts.to_rcpt); + printf(" datainit = %ld\n", (long)TimeOuts.to_datainit); + printf(" datablock = %ld\n", (long)TimeOuts.to_datablock); + printf(" datafinal = %ld\n", (long)TimeOuts.to_datafinal); + printf(" rset = %ld\n", (long)TimeOuts.to_rset); + printf(" quit = %ld\n", (long)TimeOuts.to_quit); + printf(" nextcommand = %ld\n", (long)TimeOuts.to_nextcommand); + printf(" miscshort = %ld\n", (long)TimeOuts.to_miscshort); + printf(" ident = %ld\n", (long)TimeOuts.to_ident); + printf(" fileopen = %ld\n", (long)TimeOuts.to_fileopen); + } + 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; + continue; + } + else + { + register char *q = strchr(val, ':'); + + if (q == NULL && (q = strchr(val, '=')) == NULL) + { + /* syntax error */ + continue; + } + *q++ = '\0'; + settimeout(val, q); + } + } +} + /* +** SETTIMEOUT -- set an individual timeout +** +** Parameters: +** name -- the name of the timeout. +** val -- the value of the timeout. +** +** Returns: +** none. +*/ + +void +settimeout(name, val) + char *name; + char *val; +{ + register char *p; + time_t to; + extern time_t convtime __P((char *, char)); + + if (tTd(37, 2)) + printf("settimeout(%s = %s)\n", name, val); + + to = convtime(val, 'm'); + p = strchr(name, '.'); + if (p != NULL) + *p++ = '\0'; + + if (strcasecmp(name, "initial") == 0) + TimeOuts.to_initial = to; + else if (strcasecmp(name, "mail") == 0) + TimeOuts.to_mail = to; + else if (strcasecmp(name, "rcpt") == 0) + TimeOuts.to_rcpt = to; + else if (strcasecmp(name, "datainit") == 0) + TimeOuts.to_datainit = to; + else if (strcasecmp(name, "datablock") == 0) + TimeOuts.to_datablock = to; + else if (strcasecmp(name, "datafinal") == 0) + TimeOuts.to_datafinal = to; + else if (strcasecmp(name, "command") == 0) + TimeOuts.to_nextcommand = to; + else if (strcasecmp(name, "rset") == 0) + TimeOuts.to_rset = to; + else if (strcasecmp(name, "helo") == 0) + TimeOuts.to_helo = to; + else if (strcasecmp(name, "quit") == 0) + TimeOuts.to_quit = to; + else if (strcasecmp(name, "misc") == 0) + TimeOuts.to_miscshort = to; + else if (strcasecmp(name, "ident") == 0) + TimeOuts.to_ident = to; + else if (strcasecmp(name, "fileopen") == 0) + TimeOuts.to_fileopen = to; + else if (strcasecmp(name, "connect") == 0) + TimeOuts.to_connect = to; + else if (strcasecmp(name, "iconnect") == 0) + TimeOuts.to_iconnect = to; + else if (strcasecmp(name, "queuewarn") == 0) + { + to = convtime(val, 'h'); + if (p == NULL || strcmp(p, "*") == 0) + { + TimeOuts.to_q_warning[TOC_NORMAL] = to; + TimeOuts.to_q_warning[TOC_URGENT] = to; + TimeOuts.to_q_warning[TOC_NONURGENT] = to; + } + else if (strcasecmp(p, "normal") == 0) + TimeOuts.to_q_warning[TOC_NORMAL] = to; + else if (strcasecmp(p, "urgent") == 0) + TimeOuts.to_q_warning[TOC_URGENT] = to; + else if (strcasecmp(p, "non-urgent") == 0) + TimeOuts.to_q_warning[TOC_NONURGENT] = to; + else + syserr("settimeout: invalid queuewarn subtimeout %s", p); + } + else if (strcasecmp(name, "queuereturn") == 0) + { + to = convtime(val, 'd'); + if (p == NULL || strcmp(p, "*") == 0) + { + TimeOuts.to_q_return[TOC_NORMAL] = to; + TimeOuts.to_q_return[TOC_URGENT] = to; + TimeOuts.to_q_return[TOC_NONURGENT] = to; + } + else if (strcasecmp(p, "normal") == 0) + TimeOuts.to_q_return[TOC_NORMAL] = to; + else if (strcasecmp(p, "urgent") == 0) + TimeOuts.to_q_return[TOC_URGENT] = to; + else if (strcasecmp(p, "non-urgent") == 0) + TimeOuts.to_q_return[TOC_NONURGENT] = to; + else + syserr("settimeout: invalid queuereturn subtimeout %s", p); + } + else if (strcasecmp(name, "hoststatus") == 0) + MciInfoTimeout = convtime(val, 'm'); + else + syserr("settimeout: invalid timeout %s", name); +} diff --git a/contrib/sendmail/src/recipient.c b/contrib/sendmail/src/recipient.c new file mode 100644 index 0000000..9a73b6a --- /dev/null +++ b/contrib/sendmail/src/recipient.c @@ -0,0 +1,1431 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)recipient.c 8.154 (Berkeley) 6/24/98"; +#endif /* not lint */ + +# include "sendmail.h" +# include + +/* +** 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. +** +** Side Effects: +** none. +*/ + +/* 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 *al; /* list of addresses to send to */ + char delimiter; /* the address delimiter */ + int naddrs; + int i; + char *oldto = e->e_to; + char *bufp; + char buf[MAXNAME + 1]; + + if (list == NULL) + { + syserr("sendtolist: null list"); + return 0; + } + + if (tTd(25, 1)) + { + printf("sendto: %s\n ctladdr=", list); + printaddr(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; + else + bufp = xalloc(i); + strcpy(bufp, denlstring(list, FALSE, TRUE)); + + for (p = bufp; *p != '\0'; ) + { + auto char *delimptr; + register ADDRESS *a; + + /* parse the address */ + while ((isascii(*p) && isspace(*p)) || *p == ',') + p++; + a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e); + p = delimptr; + if (a == NULL) + continue; + a->q_next = al; + a->q_alias = ctladdr; + + /* arrange to inherit attributes from parent */ + if (ctladdr != NULL) + { + ADDRESS *b; + extern ADDRESS *self_reference __P((ADDRESS *, ENVELOPE *)); + + /* self reference test */ + if (sameaddr(ctladdr, a)) + { + if (tTd(27, 5)) + { + printf("sendtolist: QSELFREF "); + printaddr(ctladdr, FALSE); + } + ctladdr->q_flags |= QSELFREF; + } + + /* check for address loops */ + b = self_reference(a, e); + if (b != NULL) + { + b->q_flags |= QSELFREF; + if (tTd(27, 5)) + { + printf("sendtolist: QSELFREF "); + printaddr(b, FALSE); + } + if (a != b) + { + if (tTd(27, 5)) + { + printf("sendtolist: QDONTSEND "); + printaddr(a, FALSE); + } + a->q_flags |= QDONTSEND; + 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; + + /* original recipient information */ + 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++; + } + + e->e_to = oldto; + if (bufp != buf) + free(bufp); + return (naddrs); +} + /* +** RECIPIENT -- Designate a message recipient +** +** Saves the named person for future mailing. +** +** Parameters: +** a -- the (preparsed) address header for the recipient. +** sendq -- a pointer to the head of a queue to put the +** recipient in. Duplicate supression is done +** 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. +** +** Side Effects: +** none. +*/ + +ADDRESS * +recipient(a, sendq, aliaslevel, e) + register ADDRESS *a; + register ADDRESS **sendq; + int aliaslevel; + register ENVELOPE *e; +{ + register ADDRESS *q; + ADDRESS **pq; + register struct mailer *m; + register char *p; + bool quoted = FALSE; /* set if the addr has a quote bit */ + int findusercount = 0; + bool initialdontsend = bitset(QDONTSEND, a->q_flags); + int i; + char *buf; + char buf0[MAXNAME + 1]; /* unquoted image of the user name */ + extern void alias __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); + + e->e_to = a->q_paddr; + m = a->q_mailer; + errno = 0; + if (aliaslevel == 0) + a->q_flags |= QPRIMARY; + if (tTd(26, 1)) + { + printf("\nrecipient (%d): ", aliaslevel); + printaddr(a, FALSE); + } + + /* if this is primary, add it to the original recipient list */ + if (a->q_alias == NULL) + { + if (e->e_origrcpt == NULL) + e->e_origrcpt = a->q_paddr; + else if (e->e_origrcpt != a->q_paddr) + e->e_origrcpt = ""; + } + + /* break aliasing loops */ + if (aliaslevel > MaxAliasRecursion) + { + a->q_flags |= QBADADDR; + a->q_status = "5.4.6"; + usrerr("554 aliasing/forwarding loop broken (%d aliases deep; %d max)", + aliaslevel, MaxAliasRecursion); + return (a); + } + + /* + ** Finish setting up address structure. + */ + + /* get unquoted user for file, program or user.name check */ + i = strlen(a->q_user); + if (i >= sizeof buf0) + buf = xalloc(i + 1); + else + buf = buf0; + (void) strcpy(buf, a->q_user); + for (p = buf; *p != '\0' && !quoted; p++) + { + if (*p == '\\') + quoted = TRUE; + } + stripquotes(buf); + + /* check for direct mailing to restricted mailers */ + if (m == ProgMailer) + { + if (a->q_alias == NULL) + { + a->q_flags |= QBADADDR; + a->q_status = "5.7.1"; + usrerr("550 Cannot mail directly to programs"); + } + else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) + { + a->q_flags |= QBADADDR; + a->q_status = "5.7.1"; + if (a->q_alias->q_ruser == NULL) + usrerr("550 UID %d is an unknown user: cannot mail to programs", + a->q_alias->q_uid); + else + usrerr("550 User %s@%s doesn't have a valid shell for mailing to programs", + a->q_alias->q_ruser, MyHostName); + } + else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) + { + a->q_flags |= QBADADDR; + a->q_status = "5.7.1"; + a->q_rstatus = newstr("Unsafe for mailing to programs"); + usrerr("550 Address %s is unsafe for mailing to programs", + a->q_alias->q_paddr); + } + } + + /* + ** 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 QDONTSEND bit will be set in the send list. + ** [Please note: the emphasis is on "hack."] + */ + + for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) + { + if (sameaddr(q, a) && + (bitset(QRCPTOK, q->q_flags) || + !bitset(QPRIMARY, q->q_flags))) + { + if (tTd(26, 1)) + { + printf("%s in sendq: ", a->q_paddr); + printaddr(q, FALSE); + } + if (!bitset(QPRIMARY, q->q_flags)) + { + if (!bitset(QDONTSEND, a->q_flags)) + message("duplicate suppressed"); + q->q_flags |= a->q_flags; + } + else if (bitset(QSELFREF, q->q_flags)) + q->q_flags |= a->q_flags & ~QDONTSEND; + a = q; + goto done; + } + } + + /* add address on list */ + *pq = a; + a->q_next = NULL; + + /* + ** Alias the name and handle special mailer types. + */ + + trylocaluser: + if (tTd(29, 7)) + printf("at trylocaluser %s\n", a->q_user); + + if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) + goto testselfdestruct; + + if (m == InclMailer) + { + a->q_flags |= QDONTSEND; + if (a->q_alias == NULL) + { + a->q_flags |= QBADADDR; + a->q_status = "5.7.1"; + usrerr("550 Cannot mail directly to :include:s"); + } + else + { + int ret; + + message("including file %s", a->q_user); + ret = include(a->q_user, FALSE, a, sendq, aliaslevel, e); + if (transienterror(ret)) + { + if (LogLevel > 2) + sm_syslog(LOG_ERR, e->e_id, + "include %s: transient error: %s", + shortenstring(a->q_user, MAXSHORTSTR), + errstring(ret)); + a->q_flags |= QQUEUEUP; + a->q_flags &= ~QDONTSEND; + usrerr("451 Cannot open %s: %s", + shortenstring(a->q_user, MAXSHORTSTR), + errstring(ret)); + } + else if (ret != 0) + { + a->q_flags |= QBADADDR; + a->q_status = "5.2.4"; + usrerr("550 Cannot open %s: %s", + shortenstring(a->q_user, MAXSHORTSTR), + errstring(ret)); + } + } + } + else if (m == FileMailer) + { + extern bool writable __P((char *, ADDRESS *, int)); + + /* check if writable or creatable */ + if (a->q_alias == NULL) + { + a->q_flags |= QBADADDR; + a->q_status = "5.7.1"; + usrerr("550 Cannot mail directly to files"); + } + else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) + { + a->q_flags |= QBADADDR; + a->q_status = "5.7.1"; + if (a->q_alias->q_ruser == NULL) + usrerr("550 UID %d is an unknown user: cannot mail to files", + a->q_alias->q_uid); + else + usrerr("550 User %s@%s doesn't have a valid shell for mailing to files", + a->q_alias->q_ruser, MyHostName); + } + else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) + { + a->q_flags |= QBADADDR; + a->q_status = "5.7.1"; + a->q_rstatus = newstr("Unsafe for mailing to files"); + usrerr("550 Address %s is unsafe for mailing to files", + a->q_alias->q_paddr); + } + else if (strcmp(buf, "/dev/null") == 0) + { + /* /dev/null is always accepted */ + } + else if (!writable(buf, a->q_alias, SFF_CREAT)) + { + a->q_flags |= QBADADDR; + giveresponse(EX_CANTCREAT, m, NULL, a->q_alias, + (time_t) 0, e); + } + } + + /* try aliasing */ + if (!quoted && !bitset(QDONTSEND, a->q_flags) && + bitnset(M_ALIASABLE, m->m_flags)) + alias(a, sendq, aliaslevel, e); + +# if USERDB + /* if not aliased, look it up in the user database */ + if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags) && + bitnset(M_CHECKUDB, m->m_flags)) + { + extern int udbexpand __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); + + if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL) + { + a->q_flags |= QQUEUEUP; + if (e->e_message == NULL) + e->e_message = newstr("Deferred: user database error"); + if (LogLevel > 8) + sm_syslog(LOG_INFO, e->e_id, + "deferred: udbexpand: %s", + errstring(errno)); + message("queued (user database error): %s", + errstring(errno)); + e->e_nrcpts++; + goto testselfdestruct; + } + } +# endif + + /* + ** If we have a level two config file, then pass the name through + ** Ruleset 5 before sending it off. Ruleset 5 has the right + ** to send rewrite it to another mailer. This gives us a hook + ** after local aliasing has been done. + */ + + if (tTd(29, 5)) + { + printf("recipient: testing local? cl=%d, rr5=%lx\n\t", + ConfigLevel, (u_long) RewriteRules[5]); + printaddr(a, FALSE); + } + if (!bitset(QNOTREMOTE|QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) && + ConfigLevel >= 2 && RewriteRules[5] != NULL && + bitnset(M_TRYRULESET5, m->m_flags)) + { + extern void maplocaluser __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); + + maplocaluser(a, sendq, aliaslevel + 1, e); + } + + /* + ** If it didn't get rewritten to another mailer, go ahead + ** and deliver it. + */ + + if (!bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) && + bitnset(M_HASPWENT, m->m_flags)) + { + auto bool fuzzy; + register struct passwd *pw; + extern void forward __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); + + /* warning -- finduser may trash buf */ + pw = finduser(buf, &fuzzy); + if (pw == NULL || strlen(pw->pw_name) > MAXNAME) + { + a->q_flags |= QBADADDR; + a->q_status = "5.1.1"; + giveresponse(EX_NOUSER, m, NULL, a->q_alias, + (time_t) 0, e); + } + else + { + char nbuf[MAXNAME + 1]; + + if (fuzzy) + { + /* name was a fuzzy match */ + a->q_user = newstr(pw->pw_name); + if (findusercount++ > 3) + { + a->q_flags |= QBADADDR; + a->q_status = "5.4.6"; + usrerr("554 aliasing/forwarding loop for %s broken", + pw->pw_name); + goto done; + } + + /* see if it aliases */ + (void) strcpy(buf, pw->pw_name); + goto trylocaluser; + } + if (strcmp(pw->pw_dir, "/") == 0) + a->q_home = ""; + else + a->q_home = newstr(pw->pw_dir); + a->q_uid = pw->pw_uid; + a->q_gid = pw->pw_gid; + a->q_ruser = newstr(pw->pw_name); + a->q_flags |= QGOODUID; + buildfname(pw->pw_gecos, pw->pw_name, nbuf, sizeof nbuf); + if (nbuf[0] != '\0') + a->q_fullname = newstr(nbuf); + if (!usershellok(pw->pw_name, pw->pw_shell)) + { + a->q_flags |= QBOGUSSHELL; + } + if (bitset(EF_VRFYONLY, e->e_flags)) + { + /* don't do any more now */ + a->q_flags |= QVERIFIED; + } + else if (!quoted) + forward(a, sendq, aliaslevel, e); + } + } + if (!bitset(QDONTSEND, a->q_flags)) + e->e_nrcpts++; + + testselfdestruct: + a->q_flags |= QTHISPASS; + if (tTd(26, 8)) + { + printf("testselfdestruct: "); + printaddr(a, FALSE); + if (tTd(26, 10)) + { + printf("SENDQ:\n"); + printaddr(*sendq, TRUE); + printf("----\n"); + } + } + if (a->q_alias == NULL && a != &e->e_from && + bitset(QDONTSEND, a->q_flags)) + { + for (q = *sendq; q != NULL; q = q->q_next) + { + if (!bitset(QDONTSEND, q->q_flags)) + break; + } + if (q == NULL) + { + a->q_flags |= QBADADDR; + a->q_status = "5.4.6"; + usrerr("554 aliasing/forwarding loop broken"); + } + } + + done: + a->q_flags |= QTHISPASS; + if (buf != buf0) + free(buf); + + /* + ** 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) && + !bitset(QDONTSEND|QBADADDR, q->q_flags)) + { + 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; + a->q_flags |= QEXPANDED; + if (e->e_xfp != NULL && bitset(QPINGONSUCCESS, a->q_flags)) + fprintf(e->e_xfp, + "%s... expanded to multiple addresses\n", + a->q_paddr); + } + } + a->q_flags |= QRCPTOK; + return (a); +} + /* +** 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. +** +** Returns: +** A pointer to a pw struct. +** NULL if name is unknown or ambiguous. +** +** Side Effects: +** may modify name. +*/ + +struct passwd * +finduser(name, fuzzyp) + char *name; + bool *fuzzyp; +{ + register struct passwd *pw; + register char *p; + bool tryagain; + + if (tTd(29, 4)) + printf("finduser(%s): ", name); + + *fuzzyp = FALSE; + +#ifdef 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)) + printf("failed (numeric input)\n"); + return NULL; + } +#endif + + /* look up this login name using fast path */ + if ((pw = sm_getpwnam(name)) != NULL) + { + if (tTd(29, 4)) + printf("found (non-fuzzy)\n"); + return (pw); + } + + /* 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 && (pw = sm_getpwnam(name)) != NULL) + { + if (tTd(29, 4)) + printf("found (lower case)\n"); + *fuzzyp = TRUE; + return pw; + } + +#if MATCHGECOS + /* see if fuzzy matching allowed */ + if (!MatchGecos) + { + if (tTd(29, 4)) + printf("not found (fuzzy disabled)\n"); + return NULL; + } + + /* 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 (strcasecmp(pw->pw_name, name) == 0) + { + if (tTd(29, 4)) + printf("found (case wrapped)\n"); + break; + } +# endif + + buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf); + if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name)) + { + if (tTd(29, 4)) + printf("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)) + printf("no fuzzy match found\n"); +# if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */ + endpwent(); +# endif + return pw; +#else + if (tTd(29, 4)) + printf("not found (fuzzy disabled)\n"); + return NULL; +#endif +} + /* +** 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; + int flags; +{ + uid_t euid; + gid_t egid; + char *uname; + + if (tTd(44, 5)) + printf("writable(%s, 0x%x)\n", filename, flags); + + /* + ** File does exist -- check that it is writable. + */ + + if (geteuid() != 0) + { + euid = geteuid(); + egid = getegid(); + uname = NULL; + } + else if (ctladdr != NULL) + { + euid = ctladdr->q_uid; + egid = ctladdr->q_gid; + uname = ctladdr->q_user; + } + else if (bitset(SFF_RUNASREALUID, flags)) + { + euid = RealUid; + egid = RealGid; + uname = RealUserName; + } + else if (FileMailer != NULL && !bitset(SFF_ROOTOK, flags)) + { + euid = FileMailer->m_uid; + egid = FileMailer->m_gid; + uname = NULL; + } + else + { + euid = egid = 0; + uname = NULL; + } + if (!bitset(SFF_ROOTOK, flags)) + { + if (euid == 0) + { + euid = DefUid; + uname = DefUser; + } + if (egid == 0) + egid = DefGid; + } + if (geteuid() == 0 && + (ctladdr == NULL || !bitset(QGOODUID, ctladdr->q_flags))) + flags |= SFF_SETUIDOK; + + if (!bitset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) + flags |= SFF_NOSLINK; + if (!bitset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) + flags |= SFF_NOHLINK; + + errno = safefile(filename, euid, egid, uname, 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; +static void includetimeout __P((void)); + +int +include(fname, forwarding, ctladdr, sendq, aliaslevel, e) + char *fname; + bool forwarding; + ADDRESS *ctladdr; + ADDRESS **sendq; + int aliaslevel; + ENVELOPE *e; +{ + FILE *volatile fp = NULL; + char *oldto = e->e_to; + char *oldfilename = FileName; + int oldlinenumber = LineNumber; + register EVENT *ev = NULL; + int nincludes; + int mode; + register ADDRESS *ca; + volatile uid_t saveduid, uid; + volatile gid_t savedgid, gid; + char *volatile uname; + int rval = 0; + volatile int sfflags = SFF_REGONLY; + register char *p; + bool safechown = FALSE; + volatile bool safedir = FALSE; + struct stat st; + char buf[MAXLINE]; + extern bool chownsafe __P((int, bool)); + + if (tTd(27, 2)) + printf("include(%s)\n", fname); + if (tTd(27, 4)) + printf(" ruid=%d euid=%d\n", (int) getuid(), (int) geteuid()); + if (tTd(27, 14)) + { + printf("ctladdr "); + printaddr(ctladdr, FALSE); + } + + if (tTd(27, 9)) + printf("include: old uid = %d/%d\n", + (int) getuid(), (int) geteuid()); + + if (forwarding) + sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOSLINK; + + ca = getctladdr(ctladdr); + if (ca == NULL) + { + uid = DefUid; + gid = DefGid; + uname = DefUser; + } + else + { + uid = ca->q_uid; + gid = ca->q_gid; + uname = ca->q_user; + } +#if HASSETREUID || USESETEUID + saveduid = geteuid(); + savedgid = getegid(); + if (saveduid == 0) + { + if (!DontInitGroups) + { + if (initgroups(uname, gid) == -1) + syserr("include: initgroups(%s, %d) failed", + uname, gid); + } + else + { + GIDSET_T gidset[1]; + + gidset[0] = gid; + if (setgroups(1, gidset) == -1) + syserr("include: setgroups() failed"); + } + + if (gid != 0 && setgid(gid) < -1) + syserr("setgid(%d) failure", gid); + if (uid != 0) + { +# if USESETEUID + if (seteuid(uid) < 0) + syserr("seteuid(%d) failure (real=%d, eff=%d)", + uid, getuid(), geteuid()); +# else + if (setreuid(0, uid) < 0) + syserr("setreuid(0, %d) failure (real=%d, eff=%d)", + uid, getuid(), geteuid()); +# endif + } + } +#endif + + if (tTd(27, 9)) + printf("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_flags |= QQUEUEUP; + errno = 0; + + /* return pseudo-error code */ + rval = E_SM_OPENTIMEOUT; + goto resetuid; + } + if (TimeOuts.to_fileopen > 0) + ev = 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, uname, sfflags|SFF_SAFEDIRPATH); + if (ret == 0) + { + /* in safe directory: relax chown & link rules */ + safedir = TRUE; + sfflags |= SFF_NOPATHCHECK; + } + else + { + if (bitset((forwarding ? + DBS_FORWARDFILEINUNSAFEDIRPATH : + DBS_INCLUDEFILEINUNSAFEDIRPATH), + DontBlameSendmail)) + sfflags |= SFF_NOPATHCHECK; + else if (bitset((forwarding ? + DBS_FORWARDFILEINGROUPWRITABLEDIRPATH : + DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH), + DontBlameSendmail) && + ret == E_SM_GWDIR) + { + DontBlameSendmail |= DBS_GROUPWRITABLEDIRPATHSAFE; + ret = safedirpath(fname, uid, + gid, uname, + sfflags|SFF_SAFEDIRPATH); + DontBlameSendmail &= ~DBS_GROUPWRITABLEDIRPATHSAFE; + if (ret == 0) + sfflags |= SFF_NOPATHCHECK; + else + sfflags |= SFF_SAFEDIRPATH; + } + else + sfflags |= SFF_SAFEDIRPATH; + if (ret > E_PSEUDOBASE && + !bitset((forwarding ? + DBS_FORWARDFILEINUNSAFEDIRPATHSAFE : + DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE), + DontBlameSendmail)) + { + if (LogLevel >= 12) + 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 && + !bitset((forwarding ? + DBS_LINKEDFORWARDFILEINWRITABLEDIR : + DBS_LINKEDINCLUDEFILEINWRITABLEDIR), + DontBlameSendmail)) + sfflags |= SFF_NOLINK; + + rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD, &st); + if (rval != 0) + { + /* don't use this :include: file */ + if (tTd(27, 4)) + printf("include: not safe (uid=%d): %s\n", + (int) uid, errstring(rval)); + } + else if ((fp = fopen(fname, "r")) == NULL) + { + rval = errno; + if (tTd(27, 4)) + printf("include: open: %s\n", errstring(rval)); + } + else if (filechanged(fname, fileno(fp), &st)) + { + rval = E_SM_FILECHANGE; + if (tTd(27, 4)) + printf("include: file changed after open\n"); + } + if (ev != NULL) + 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)", + getuid(), geteuid()); +# else + if (setreuid(-1, 0) < 0) + syserr("setreuid(-1, 0) failure (real=%d, eff=%d)", + getuid(), geteuid()); + if (setreuid(RealUid, 0) < 0) + syserr("setreuid(%d, 0) failure (real=%d, eff=%d)", + RealUid, getuid(), geteuid()); +# endif + } + setgid(savedgid); + } +#endif + + if (tTd(27, 9)) + printf("include: reset uid = %d/%d\n", + (int) getuid(), (int) geteuid()); + + if (rval == E_SM_OPENTIMEOUT) + usrerr("451 open timeout on %s", fname); + + if (fp == NULL) + return rval; + + if (fstat(fileno(fp), &st) < 0) + { + rval = errno; + syserr("Cannot fstat %s!", fname); + return rval; + } + + /* if path was writable, check to avoid file giveaway tricks */ + safechown = chownsafe(fileno(fp), safedir); + if (tTd(27, 6)) + printf("include: parent of %s is %s, chown is %ssafe\n", + fname, + safedir ? "safe" : "dangerous", + safechown ? "" : "un"); + + if (ca == NULL && safechown) + { + 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_flags |= QBOGUSSHELL; + else + { + char *sh; + + ctladdr->q_ruser = newstr(pw->pw_name); + if (safechown) + sh = pw->pw_shell; + else + sh = "/SENDMAIL/ANY/SHELL/"; + if (!usershellok(pw->pw_name, sh)) + { + if (LogLevel >= 12) + sm_syslog(LOG_INFO, e->e_id, + "%s: user %s has bad shell %s, marked %s", + shortenstring(fname, MAXSHORTSTR), + pw->pw_name, sh, + safechown ? "bogus" : "unsafe"); + 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_flags |= QVERIFIED; + e->e_nrcpts++; + xfclose(fp, "include", fname); + 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 (!bitset((forwarding ? + DBS_GROUPWRITABLEFORWARDFILESAFE : + DBS_GROUPWRITABLEINCLUDEFILESAFE), + DontBlameSendmail)) + mode |= S_IWGRP; + + if (bitset(mode, st.st_mode)) + { + if (tTd(27, 6)) + printf("include: %s is %s writable, marked unsafe\n", + shortenstring(fname, MAXSHORTSTR), + bitset(S_IWOTH, st.st_mode) ? "world" : "group"); + if (LogLevel >= 12) + sm_syslog(LOG_INFO, e->e_id, + "%s: %s writable %s file, marked unsafe", + shortenstring(fname, MAXSHORTSTR), + bitset(S_IWOTH, st.st_mode) ? "world" : "group", + forwarding ? "forward" : ":include:"); + 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 (fgets(buf, sizeof buf, fp) != NULL) + { + register char *p = strchr(buf, '\n'); + + LineNumber++; + if (p != NULL) + *p = '\0'; + 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[-1] = '\0'; + break; + } + } + if (buf[0] == '\0') + continue; + + e->e_to = NULL; + message("%s to %s", + forwarding ? "forwarding" : "sending", buf); + if (forwarding && LogLevel > 9) + sm_syslog(LOG_INFO, e->e_id, + "forward %.200s => %s", + oldto, shortenstring(buf, MAXSHORTSTR)); + + nincludes += sendtolist(buf, ctladdr, sendq, aliaslevel + 1, e); + } + + if (ferror(fp) && tTd(27, 3)) + printf("include: read error: %s\n", errstring(errno)); + if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags)) + { + if (tTd(27, 5)) + { + printf("include: QDONTSEND "); + printaddr(ctladdr, FALSE); + } + ctladdr->q_flags |= QDONTSEND; + } + + (void) xfclose(fp, "include", fname); + FileName = oldfilename; + LineNumber = oldlinenumber; + e->e_to = oldto; + return rval; +} + +static void +includetimeout() +{ + 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. +** +** Side Effects: +** none. +*/ + +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. +** e -- the current envelope. +** +** Returns: +** The address that should be retained. +*/ + +ADDRESS * +self_reference(a, e) + ADDRESS *a; + ENVELOPE *e; +{ + ADDRESS *b; /* top entry in self ref loop */ + ADDRESS *c; /* entry that point to a real mail box */ + + if (tTd(27, 1)) + printf("self_reference(%s)\n", a->q_paddr); + + for (b = a->q_alias; b != NULL; b = b->q_alias) + { + if (sameaddr(a, b)) + break; + } + + if (b == NULL) + { + if (tTd(27, 1)) + printf("\t... no self ref\n"); + return NULL; + } + + /* + ** Pick the first address that resolved to a real mail box + ** i.e has a pw entry. The returned value will be marked + ** QSELFREF in recipient(), which in turn will disable alias() + ** from marking it QDONTSEND, which mean it will be used + ** as a deliverable address. + ** + ** The 2 key thing to note here are: + ** 1) we are in a recursive call sequence: + ** alias->sentolist->recipient->alias + ** 2) normally, when we return back to alias(), the address + ** will be marked QDONTSEND, since alias() assumes the + ** 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 (bitnset(M_HASPWENT, c->q_mailer->m_flags)) + { + if (tTd(27, 2)) + printf("\t... getpwnam(%s)... ", c->q_user); + if (sm_getpwnam(c->q_user) != NULL) + { + if (tTd(27, 2)) + printf("found\n"); + + /* ought to cache results here */ + if (sameaddr(b, c)) + return b; + else + return c; + } + if (tTd(27, 2)) + printf("failed\n"); + } + c = c->q_alias; + } + + if (tTd(27, 1)) + printf("\t... cannot break loop for \"%s\"\n", a->q_paddr); + + return NULL; +} diff --git a/contrib/sendmail/src/safefile.c b/contrib/sendmail/src/safefile.c new file mode 100644 index 0000000..16f3f39 --- /dev/null +++ b/contrib/sendmail/src/safefile.c @@ -0,0 +1,751 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)safefile.c 8.40 (Berkeley) 6/5/98"; +#endif /* not lint */ + +# include "sendmail.h" + /* +** SAFEFILE -- return true if a file exists and is safe for a user. +** +** Parameters: +** fn -- filename to check. +** uid -- user id to compare against. +** gid -- group id to compare against. +** uname -- user name to compare against (used for group +** sets). +** flags -- modifiers: +** SFF_MUSTOWN -- "uid" must own this file. +** SFF_NOSLINK -- file cannot be a symbolic link. +** mode -- mode bits that must match. +** st -- if set, points to a stat structure that will +** get the stat info for the file. +** +** Returns: +** 0 if fn exists, is owned by uid, and matches mode. +** An errno otherwise. The actual errno is cleared. +** +** Side Effects: +** none. +*/ + +#include + +int +safefile(fn, uid, gid, uname, flags, mode, st) + char *fn; + UID_T uid; + GID_T gid; + char *uname; + int flags; + int mode; + struct stat *st; +{ + register char *p; + register struct group *gr = NULL; + int file_errno = 0; + bool checkpath; + struct stat stbuf; + struct stat fstbuf; + char fbuf[MAXPATHLEN + 1]; + + if (tTd(44, 4)) + printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n", + fn, (int) uid, (int) gid, flags, mode); + errno = 0; + if (st == NULL) + st = &fstbuf; + if (strlen(fn) > sizeof fbuf - 1) + { + if (tTd(44, 4)) + printf("\tpathname too long\n"); + return ENAMETOOLONG; + } + strcpy(fbuf, fn); + fn = fbuf; + + /* ignore SFF_SAFEDIRPATH if we are debugging */ + if (RealUid != 0 && RunAsUid == RealUid) + flags &= ~SFF_SAFEDIRPATH; + + /* first check to see if the file exists at all */ +#ifdef HASLSTAT + if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) + : stat(fn, st)) < 0) +#else + if (stat(fn, st) < 0) +#endif + { + file_errno = errno; + } + else if (bitset(SFF_SETUIDOK, flags) && + !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) && + S_ISREG(st->st_mode)) + { + /* + ** If final file is setuid, run as the owner of that + ** file. Gotta be careful not to reveal anything too + ** soon here! + */ + +#ifdef SUID_ROOT_FILES_OK + if (bitset(S_ISUID, st->st_mode)) +#else + if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 && + st->st_uid != TrustedFileUid) +#endif + { + uid = st->st_uid; + uname = NULL; + } +#ifdef SUID_ROOT_FILES_OK + if (bitset(S_ISGID, st->st_mode)) +#else + if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) +#endif + gid = st->st_gid; + } + + checkpath = !bitset(SFF_NOPATHCHECK, flags) || + (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)); + if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags)) + { + int ret; + + /* check the directory */ + p = strrchr(fn, '/'); + if (p == NULL) + { + ret = safedirpath(".", uid, gid, uname, flags|SFF_SAFEDIRPATH); + } + else + { + *p = '\0'; + ret = safedirpath(fn, uid, gid, uname, flags|SFF_SAFEDIRPATH); + *p = '/'; + } + if (ret == 0) + { + /* directory is safe */ + checkpath = FALSE; + } + else + { +#ifdef HASLSTAT + /* Need lstat() information if called stat() before */ + if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0) + { + ret = errno; + if (tTd(44, 4)) + printf("\t%s\n", errstring(ret)); + return ret; + } +#endif + /* directory is writable: disallow links */ + flags |= SFF_NOLINK; + } + } + + if (checkpath) + { + int ret; + + p = strrchr(fn, '/'); + if (p == NULL) + { + ret = safedirpath(".", uid, gid, uname, flags); + } + else + { + *p = '\0'; + ret = safedirpath(fn, uid, gid, uname, flags); + *p = '/'; + } + if (ret != 0) + return ret; + } + + /* + ** If the target file doesn't exist, check the directory to + ** ensure that it is writable by this user. + */ + + if (file_errno != 0) + { + int ret = file_errno; + char *dir = fn; + + if (tTd(44, 4)) + printf("\t%s\n", errstring(ret)); + + errno = 0; + if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT) + return ret; + + /* check to see if legal to create the file */ + p = strrchr(dir, '/'); + if (p == NULL) + dir = "."; + else if (p == dir) + dir = "/"; + else + *p = '\0'; + if (stat(dir, &stbuf) >= 0) + { + int md = S_IWRITE|S_IEXEC; + + if (stbuf.st_uid == uid) + ; + else if (uid == 0 && TrustedFileUid != 0 && stbuf.st_uid == TrustedFileUid) + ; + else + { + md >>= 3; + if (stbuf.st_gid == gid) + ; +#ifndef NO_GROUP_SET + else if (uname != NULL && !DontInitGroups && + ((gr != NULL && + gr->gr_gid == stbuf.st_gid) || + (gr = getgrgid(stbuf.st_gid)) != NULL)) + { + register char **gp; + + for (gp = gr->gr_mem; *gp != NULL; gp++) + if (strcmp(*gp, uname) == 0) + break; + if (*gp == NULL) + md >>= 3; + } +#endif + else + md >>= 3; + } + if ((stbuf.st_mode & md) != md) + errno = EACCES; + } + ret = errno; + if (tTd(44, 4)) + printf("\t[final dir %s uid %d mode %lo] %s\n", + dir, (int) stbuf.st_uid, (u_long) stbuf.st_mode, + errstring(ret)); + if (p != NULL) + *p = '/'; + st->st_mode = ST_MODE_NOFILE; + return ret; + } + +#ifdef S_ISLNK + if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) + { + if (tTd(44, 4)) + printf("\t[slink mode %lo]\tE_SM_NOSLINK\n", + (u_long) st->st_mode); + return E_SM_NOSLINK; + } +#endif + if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) + { + if (tTd(44, 4)) + printf("\t[non-reg mode %lo]\tE_SM_REGONLY\n", + (u_long) st->st_mode); + return E_SM_REGONLY; + } + if (bitset(SFF_NOGWFILES, flags) && + bitset(S_IWGRP, st->st_mode)) + { + if (tTd(44, 4)) + printf("\t[write bits %lo]\tE_SM_GWFILE\n", + (u_long) st->st_mode); + return E_SM_GWFILE; + } + if (bitset(SFF_NOWWFILES, flags) && + bitset(S_IWOTH, st->st_mode)) + { + if (tTd(44, 4)) + printf("\t[write bits %lo]\tE_SM_WWFILE\n", + (u_long) st->st_mode); + return E_SM_WWFILE; + } + if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && + bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) + { + if (tTd(44, 4)) + printf("\t[exec bits %lo]\tE_SM_ISEXEC]\n", + (u_long) st->st_mode); + return E_SM_ISEXEC; + } + if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1) + { + if (tTd(44, 4)) + printf("\t[link count %d]\tE_SM_NOHLINK\n", + (int) st->st_nlink); + return E_SM_NOHLINK; + } + + if (uid == 0 && bitset(SFF_OPENASROOT, flags)) + ; + else if (uid == 0 && !bitset(SFF_ROOTOK, flags)) + mode >>= 6; + else if (st->st_uid == uid) + ; + else if (uid == 0 && TrustedFileUid != 0 && st->st_uid == TrustedFileUid) + ; + else + { + mode >>= 3; + if (st->st_gid == gid) + ; +#ifndef NO_GROUP_SET + else if (uname != NULL && !DontInitGroups && + ((gr != NULL && gr->gr_gid == st->st_gid) || + (gr = getgrgid(st->st_gid)) != NULL)) + { + register char **gp; + + for (gp = gr->gr_mem; *gp != NULL; gp++) + if (strcmp(*gp, uname) == 0) + break; + if (*gp == NULL) + mode >>= 3; + } +#endif + else + mode >>= 3; + } + if (tTd(44, 4)) + printf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", + (int) st->st_uid, (int) st->st_nlink, + (u_long) st->st_mode, (u_long) mode); + if ((st->st_uid == uid || st->st_uid == 0 || + st->st_uid == TrustedFileUid || + !bitset(SFF_MUSTOWN, flags)) && + (st->st_mode & mode) == mode) + { + if (tTd(44, 4)) + printf("\tOK\n"); + return 0; + } + if (tTd(44, 4)) + printf("\tEACCES\n"); + return EACCES; +} + /* +** SAFEDIRPATH -- check to make sure a path to a directory is safe +** +** Safe means not writable and owned by the right folks. +** +** Parameters: +** fn -- filename to check. +** uid -- user id to compare against. +** gid -- group id to compare against. +** uname -- user name to compare against (used for group +** sets). +** flags -- modifiers: +** SFF_ROOTOK -- ok to use root permissions to open. +** SFF_SAFEDIRPATH -- writable directories are considered +** to be fatal errors. +** +** Returns: +** 0 -- if the directory path is "safe". +** else -- an error number associated with the path. +*/ + +int +safedirpath(fn, uid, gid, uname, flags) + char *fn; + UID_T uid; + GID_T gid; + char *uname; + int flags; +{ + char *p; + register struct group *gr = NULL; + int ret = 0; + int mode = S_IWOTH; + struct stat stbuf; + + /* special case root directory */ + if (*fn == '\0') + fn = "/"; + + if (tTd(44, 4)) + printf("safedirpath(%s, uid=%ld, gid=%ld, flags=%x):\n", + fn, (long) uid, (long) gid, flags); + + if (!bitset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail)) + mode |= S_IWGRP; + + p = fn; + do + { + if (*p == '\0') + *p = '/'; + p = strchr(++p, '/'); + if (p != NULL) + *p = '\0'; + if (stat(fn, &stbuf) < 0) + { + ret = errno; + break; + } + if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) && + bitset(mode, stbuf.st_mode)) + { + if (tTd(44, 4)) + printf("\t[dir %s] mode %lo\n", + fn, (u_long) stbuf.st_mode); + if (bitset(SFF_SAFEDIRPATH, flags)) + { + if (bitset(S_IWOTH, stbuf.st_mode)) + ret = E_SM_WWDIR; + else + ret = E_SM_GWDIR; + break; + } + if (Verbose > 1) + message("051 WARNING: %s writable directory %s", + bitset(S_IWOTH, stbuf.st_mode) + ? "World" + : "Group", + fn); + } + if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)) + { + if (bitset(S_IXOTH, stbuf.st_mode)) + continue; + ret = EACCES; + break; + } + + /* + ** Let OS determine access to file if we are not + ** running as a privileged user. This allows ACLs + ** to work. + */ + if (geteuid() != 0) + continue; + + if (stbuf.st_uid == uid && + bitset(S_IXUSR, stbuf.st_mode)) + continue; + if (stbuf.st_gid == gid && + bitset(S_IXGRP, stbuf.st_mode)) + continue; +#ifndef NO_GROUP_SET + if (uname != NULL && !DontInitGroups && + ((gr != NULL && gr->gr_gid == stbuf.st_gid) || + (gr = getgrgid(stbuf.st_gid)) != NULL)) + { + register char **gp; + + for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) + if (strcmp(*gp, uname) == 0) + break; + if (gp != NULL && *gp != NULL && + bitset(S_IXGRP, stbuf.st_mode)) + continue; + } +#endif + if (!bitset(S_IXOTH, stbuf.st_mode)) + { + ret = EACCES; + break; + } + } while (p != NULL); + if (ret != 0 && tTd(44, 4)) + printf("\t[dir %s] %s\n", fn, errstring(ret)); + if (p != NULL) + *p = '/'; + return ret; +} + /* +** SAFEOPEN -- do a file open with extra checking +** +** Parameters: +** fn -- the file name to open. +** omode -- the open-style mode flags. +** cmode -- the create-style mode flags. +** sff -- safefile flags. +** +** Returns: +** Same as open. +*/ + +#ifndef O_ACCMODE +# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) +#endif + +int +safeopen(fn, omode, cmode, sff) + char *fn; + int omode; + int cmode; + int sff; +{ + int rval; + int fd; + int smode; + struct stat stb; + + if (bitset(O_CREAT, omode)) + sff |= SFF_CREAT; + omode &= ~O_CREAT; + smode = 0; + switch (omode & O_ACCMODE) + { + case O_RDONLY: + smode = S_IREAD; + break; + + case O_WRONLY: + smode = S_IWRITE; + break; + + case O_RDWR: + smode = S_IREAD|S_IWRITE; + break; + + default: + smode = 0; + break; + } + if (bitset(SFF_OPENASROOT, sff)) + rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName, + sff, smode, &stb); + else + rval = safefile(fn, RealUid, RealGid, RealUserName, + sff, smode, &stb); + if (rval != 0) + { + errno = rval; + return -1; + } + if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff)) + omode |= O_EXCL|O_CREAT; + + fd = dfopen(fn, omode, cmode, sff); + if (fd < 0) + return fd; + if (filechanged(fn, fd, &stb)) + { + syserr("554 cannot open: file %s changed after open", fn); + close(fd); + errno = E_SM_FILECHANGE; + return -1; + } + return fd; +} + /* +** SAFEFOPEN -- do a file open with extra checking +** +** Parameters: +** fn -- the file name to open. +** omode -- the open-style mode flags. +** cmode -- the create-style mode flags. +** sff -- safefile flags. +** +** Returns: +** Same as fopen. +*/ + +FILE * +safefopen(fn, omode, cmode, sff) + char *fn; + int omode; + int cmode; + int sff; +{ + int fd; + FILE *fp; + char *fmode; + + switch (omode & O_ACCMODE) + { + case O_RDONLY: + fmode = "r"; + break; + + case O_WRONLY: + if (bitset(O_APPEND, omode)) + fmode = "a"; + else + fmode = "w"; + break; + + case O_RDWR: + if (bitset(O_TRUNC, omode)) + fmode = "w+"; + else if (bitset(O_APPEND, omode)) + fmode = "a+"; + else + fmode = "r+"; + break; + + default: + syserr("safefopen: unknown omode %o", omode); + fmode = "x"; + } + fd = safeopen(fn, omode, cmode, sff); + if (fd < 0) + { + if (tTd(44, 10)) + printf("safefopen: safeopen failed: %s\n", + errstring(errno)); + return NULL; + } + fp = fdopen(fd, fmode); + if (fp != NULL) + return fp; + + if (tTd(44, 10)) + { + printf("safefopen: fdopen(%s, %s) failed: omode=%x, sff=%x, err=%s\n", + fn, fmode, omode, sff, errstring(errno)); +#ifndef NOT_SENDMAIL + dumpfd(fd, TRUE, FALSE); +#endif + } + (void) close(fd); + return NULL; +} + /* +** FILECHANGED -- check to see if file changed after being opened +** +** Parameters: +** fn -- pathname of file to check. +** fd -- file descriptor to check. +** stb -- stat structure from before open. +** +** Returns: +** TRUE -- if a problem was detected. +** FALSE -- if this file is still the same. +*/ + +bool +filechanged(fn, fd, stb) + char *fn; + int fd; + struct stat *stb; +{ + struct stat sta; + + if (stb->st_mode == ST_MODE_NOFILE) + { +#if HASLSTAT && BOGUS_O_EXCL + /* only necessary if exclusive open follows symbolic links */ + if (lstat(fn, stb) < 0 || stb->st_nlink != 1) + return TRUE; +#else + return FALSE; +#endif + } + if (fstat(fd, &sta) < 0) + return TRUE; + + if (sta.st_nlink != stb->st_nlink || + sta.st_dev != stb->st_dev || + sta.st_ino != stb->st_ino || +#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ + sta.st_gen != stb->st_gen || +#endif + sta.st_uid != stb->st_uid || + sta.st_gid != stb->st_gid) + { + if (tTd(44, 8)) + { + printf("File changed after opening:\n"); + printf(" nlink = %ld/%ld\n", + (long) stb->st_nlink, (long) sta.st_nlink); + printf(" dev = %ld/%ld\n", + (long) stb->st_dev, (long) sta.st_dev); + if (sizeof sta.st_ino > sizeof (long)) + { + printf(" ino = %s/", + quad_to_string(stb->st_ino)); + printf("%s\n", + quad_to_string(sta.st_ino)); + } + else + printf(" ino = %lu/%lu\n", + (unsigned long) stb->st_ino, + (unsigned long) sta.st_ino); +#if HAS_ST_GEN + printf(" gen = %ld/%ld\n", + (long) stb->st_gen, (long) sta.st_gen); +#endif + printf(" uid = %ld/%ld\n", + (long) stb->st_uid, (long) sta.st_uid); + printf(" gid = %ld/%ld\n", + (long) stb->st_gid, (long) sta.st_gid); + } + return TRUE; + } + + return FALSE; +} + /* +** DFOPEN -- determined file open +** +** This routine has the semantics of open, except that it will +** keep trying a few times to make this happen. The idea is that +** on very loaded systems, we may run out of resources (inodes, +** whatever), so this tries to get around it. +*/ + +int +dfopen(filename, omode, cmode, sff) + char *filename; + int omode; + int cmode; + int sff; +{ + register int tries; + int fd; + struct stat st; + + for (tries = 0; tries < 10; tries++) + { + sleep((unsigned) (10 * tries)); + errno = 0; + fd = open(filename, omode, cmode); + if (fd >= 0) + break; + switch (errno) + { + case ENFILE: /* system file table full */ + case EINTR: /* interrupted syscall */ +#ifdef ETXTBSY + case ETXTBSY: /* Apollo: net file locked */ +#endif + continue; + } + break; + } + if (!bitset(SFF_NOLOCK, sff) && + fd >= 0 && + fstat(fd, &st) >= 0 && + S_ISREG(st.st_mode)) + { + int locktype; + + /* lock the file to avoid accidental conflicts */ + if ((omode & O_ACCMODE) != O_RDONLY) + locktype = LOCK_EX; + else + locktype = LOCK_SH; + (void) lockfile(fd, filename, NULL, locktype); + errno = 0; + } + return fd; +} diff --git a/contrib/sendmail/src/savemail.c b/contrib/sendmail/src/savemail.c new file mode 100644 index 0000000..4fbfd67 --- /dev/null +++ b/contrib/sendmail/src/savemail.c @@ -0,0 +1,1487 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)savemail.c 8.138 (Berkeley) 6/17/98"; +#endif /* not lint */ + +# include "sendmail.h" + +/* +** 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: +** none +** +** 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 /* messages have already been returned */ +# define ESM_DEADLETTER 3 /* save in ~/dead.letter */ +# define ESM_POSTMASTER 4 /* return to postmaster */ +# define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ +# define ESM_PANIC 6 /* leave the locked queue/transcript files */ +# define ESM_DONE 7 /* the message is successfully delivered */ + + +void +savemail(e, sendbody) + register ENVELOPE *e; + bool sendbody; +{ + register struct passwd *pw; + register FILE *fp; + int state; + auto ADDRESS *q = NULL; + register char *p; + MCI mcibuf; + int flags; + char buf[MAXLINE+1]; + extern char *ttypath __P((void)); + extern bool writable __P((char *, ADDRESS *, int)); + + if (tTd(6, 1)) + { + printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", + e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, + ExitStat); + printaddr(&e->e_from, FALSE); + } + + if (e->e_id == NULL) + { + /* can't return a message with no id */ + return; + } + + /* + ** 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) == NULL) + { + syserr("553 Cannot parse Postmaster!"); + ExitStat = EX_SOFTWARE; + finis(); + } + } + 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_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; + + default: + syserr("554 savemail: bogus errormode x%x\n", 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; + } + state = ESM_POSTMASTER; + } + + while (state != ESM_DONE) + { + if (tTd(6, 5)) + printf(" 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). + */ + + p = ttypath(); + if (p == NULL || freopen(p, "w", stdout) == NULL) + { + state = ESM_MAIL; + break; + } + + expand("\201n", buf, sizeof buf, e); + printf("\r\nMessage from %s...\r\n", buf); + printf("Errors occurred while sending mail.\r\n"); + if (e->e_xfp != NULL) + { + (void) fflush(e->e_xfp); + fp = fopen(queuename(e, 'x'), "r"); + } + else + fp = NULL; + if (fp == NULL) + { + syserr("Cannot open %s", queuename(e, 'x')); + printf("Transcript of session is unavailable.\r\n"); + } + else + { + printf("Transcript follows:\r\n"); + while (fgets(buf, sizeof buf, fp) != NULL && + !ferror(stdout)) + fputs(buf, stdout); + (void) xfclose(fp, "savemail transcript", e->e_id); + } + printf("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]; + extern bool pruneroute __P((char *)); + + if (strlen(e->e_from.q_paddr) + 1 > sizeof from) + { + state = ESM_POSTMASTER; + break; + } + strcpy(from, e->e_from.q_paddr); + + if (!DontPruneRoutes && pruneroute(from)) + { + ADDRESS *a; + + for (a = e->e_errorqueue; a != NULL; + a = a->q_next) + { + if (sameaddr(a, &e->e_from)) + a->q_flags |= QDONTSEND; + } + } + (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 (!bitset(QBADADDR|QDONTSEND, q->q_flags)) + 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; + if (sendtolist(DoubleBounceAddr, + NULLADDR, &q, 0, e) <= 0) + { + syserr("553 cannot parse %s!", DoubleBounceAddr); + ExitStat = EX_SOFTWARE; + state = ESM_USRTMP; + 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_USRTMP; + 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 ((pw = sm_getpwnam(e->e_from.q_user)) != NULL) + p = pw->pw_dir; + } + 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 */ + define('z', p, e); + expand("\201z/dead.letter", buf, sizeof buf, e); + flags = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; + if (RealUid == 0) + flags |= SFF_ROOTOK; + e->e_to = buf; + if (mailfile(buf, FileMailer, NULL, flags, e) == EX_OK) + { + int oldverb = Verbose; + + Verbose = 1; + message("Saved message in %s", buf); + Verbose = oldverb; + state = ESM_DONE; + break; + } + state = ESM_MAIL; + break; + + case ESM_USRTMP: + /* + ** Log the mail in /usr/tmp/dead.letter. + */ + + if (e->e_class < 0) + { + state = ESM_DONE; + break; + } + + if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') || + DeadLetterDrop == NULL || DeadLetterDrop[0] == '\0') + { + state = ESM_PANIC; + break; + } + + flags = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN; + if (!writable(DeadLetterDrop, NULL, flags) || + (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND, + FileMode, flags)) == NULL) + { + state = ESM_PANIC; + break; + } + + bzero(&mcibuf, sizeof mcibuf); + mcibuf.mci_out = fp; + mcibuf.mci_mailer = FileMailer; + if (bitnset(M_7BITS, FileMailer->m_flags)) + mcibuf.mci_flags |= MCIF_7BIT; + mcibuf.mci_contentlen = 0; + + putfromline(&mcibuf, e); + (*e->e_puthdr)(&mcibuf, e->e_header, e); + (*e->e_putbody)(&mcibuf, e, NULL); + putline("\n", &mcibuf); + (void) fflush(fp); + if (ferror(fp)) + state = ESM_PANIC; + else + { + int oldverb = Verbose; + + Verbose = 1; + 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; + } + (void) xfclose(fp, "savemail", DeadLetterDrop); + break; + + default: + syserr("554 savemail: unknown state %d", state); + + /* fall through ... */ + + case ESM_PANIC: + /* leave the locked queue & transcript files around */ + loseqfile(e, "savemail panic"); + syserr("!554 savemail: cannot save rejected email anywhere"); + } + } +} + /* +** 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 100 /* 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]; + extern void errbody __P((MCI *, ENVELOPE *, char *)); + + if (returnq == NULL) + return (-1); + + if (msg == NULL) + msg = "Unable to deliver mail"; + + if (tTd(6, 1)) + { + printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=", + msg, returndepth, (u_long) e); + printaddr(returnq, TRUE); + if (tTd(6, 20)) + { + printf("Sendq="); + printaddr(e->e_sendqueue, TRUE); + } + } + + if (++returndepth >= MAXRETURNS) + { + if (returndepth != MAXRETURNS) + syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr); + /* don't "unrecurse" and fake a clean exit */ + /* returndepth--; */ + return (0); + } + + define('g', e->e_from.q_paddr, e); + define('u', NULL, e); + + /* initialize error envelope */ + ee = newenvelope(&errenvelope, e); + define('a', "\201b", ee); + define('r', "internal", ee); + define('s', "localhost", ee); + define('_', "localhost", 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; + ee->e_sendqueue = returnq; + ee->e_msgsize = ERRORFUDGE; + if (bitset(RTSF_SEND_BODY, flags)) + ee->e_msgsize += e->e_msgsize; + else + ee->e_flags |= EF_NO_BODY_RETN; + initsys(ee); + for (q = returnq; q != NULL; q = q->q_next) + { + if (bitset(QBADADDR, q->q_flags)) + continue; + + q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); + q->q_flags |= QPINGONFAILURE; + + if (!bitset(QDONTSEND, q->q_flags)) + ee->e_nrcpts++; + + if (q->q_alias == NULL) + addheader("To", q->q_paddr, &ee->e_header); + } + + if (LogLevel > 5) + { + if (bitset(EF_RESPONSE|EF_WARNING, e->e_flags)) + p = "return to sender"; + 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", &ee->e_header); + + (void) snprintf(buf, sizeof buf, "%s.%ld/%.100s", + ee->e_id, curtime(), MyHostName); + ee->e_msgboundary = newstr(buf); + (void) snprintf(buf, sizeof buf, +#if DSN + "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"", +#else + "multipart/mixed; boundary=\"%s\"", +#endif + ee->e_msgboundary); + addheader("Content-Type", buf, &ee->e_header); + + p = hvalue("Content-Transfer-Encoding", e->e_header); + if (p != NULL && 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, &ee->e_header); + } + if (strncmp(msg, "Warning:", 8) == 0) + { + addheader("Subject", msg, &ee->e_header); + p = "warning-timeout"; + } + else if (strncmp(msg, "Postmaster warning:", 19) == 0) + { + addheader("Subject", msg, &ee->e_header); + p = "postmaster-warning"; + } + else if (strcmp(msg, "Return receipt") == 0) + { + addheader("Subject", msg, &ee->e_header); + p = "return-receipt"; + } + else if (bitset(RTSF_PM_BOUNCE, flags)) + { + snprintf(buf, sizeof buf, "Postmaster notify: %.*s", + sizeof buf - 20, msg); + addheader("Subject", buf, &ee->e_header); + p = "postmaster-notification"; + } + else + { + snprintf(buf, sizeof buf, "Returned mail: %.*s", + sizeof buf - 20, msg); + addheader("Subject", buf, &ee->e_header); + p = "failure"; + } + (void) snprintf(buf, sizeof buf, "auto-generated (%s)", p); + addheader("Auto-Submitted", buf, &ee->e_header); + + /* 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) == NULL) + { + syserr("553 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; + define('f', "\201n", ee); + define('x', "Mail Delivery Subsystem", ee); + eatheader(ee, TRUE); + + /* mark statistics */ + markstats(ee, NULLADDR, FALSE); + + /* actually deliver the error message */ + sendall(ee, SM_DELIVER); + + /* restore state */ + dropenvelope(ee, TRUE); + 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 (bitset(QQUEUEUP|QSENT, q->q_flags)) + 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. +** +** Returns: +** none +** +** Side Effects: +** Outputs the body of an error message. +*/ + +void +errbody(mci, e, separator) + register MCI *mci; + register ENVELOPE *e; + char *separator; +{ + register FILE *xfile; + char *p; + register ADDRESS *q = NULL; + bool printheader; + bool sendbody; + bool pm_notify; + char buf[MAXLINE]; + + if (bitset(MCIF_INHEADER, mci->mci_flags)) + { + putline("", mci); + mci->mci_flags &= ~MCIF_INHEADER; + } + if (e->e_parent == NULL) + { + syserr("errbody: null parent"); + putline(" ----- Original message lost -----\n", mci); + return; + } + + /* + ** Output MIME header. + */ + + if (e->e_msgboundary != NULL) + { + putline("This is a MIME-encapsulated message", mci); + putline("", mci); + (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); + putline(buf, mci); + putline("", mci); + } + + /* + ** 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 (bitset(QBADADDR, q->q_flags)) + break; + } + if (!pm_notify && q == NULL && + !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) + { + putline(" **********************************************", + mci); + putline(" ** THIS IS A WARNING MESSAGE ONLY **", + mci); + putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", + mci); + putline(" **********************************************", + mci); + putline("", mci); + } + snprintf(buf, sizeof buf, "The original message was received at %s", + arpadate(ctime(&e->e_parent->e_ctime))); + putline(buf, mci); + expand("from \201_", buf, sizeof buf, e->e_parent); + putline(buf, mci); + putline("", mci); + + /* + ** Output error message header (if specified and available). + */ + + if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) + { + if (*ErrMsgFile == '/') + { + int sff = SFF_ROOTOK|SFF_REGONLY; + + if (DontLockReadFiles) + sff |= SFF_NOLOCK; + if (!bitset(DBS_ERRORHEADERINUNSAFEDIRPATH, DontBlameSendmail)) + sff |= SFF_SAFEDIRPATH; + xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff); + if (xfile != NULL) + { + while (fgets(buf, sizeof buf, xfile) != NULL) + { + extern void translate_dollars __P((char *)); + + translate_dollars(buf); + expand(buf, buf, sizeof buf, e); + putline(buf, mci); + } + (void) fclose(xfile); + putline("\n", mci); + } + } + else + { + expand(ErrMsgFile, buf, sizeof buf, e); + putline(buf, mci); + putline("", mci); + } + } + + /* + ** Output message introduction + */ + + printheader = TRUE; + for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) + { + if (!bitset(QBADADDR, q->q_flags) || + !bitset(QPINGONFAILURE, q->q_flags)) + continue; + + if (printheader) + { + putline(" ----- The following addresses had permanent fatal errors -----", + mci); + printheader = FALSE; + } + + snprintf(buf, sizeof buf, "%s", + shortenstring(q->q_paddr, MAXSHORTSTR)); + putline(buf, mci); + if (q->q_alias != NULL) + { + snprintf(buf, sizeof buf, " (expanded from: %s)", + shortenstring(q->q_alias->q_paddr, MAXSHORTSTR)); + putline(buf, mci); + } + } + if (!printheader) + putline("", mci); + + printheader = TRUE; + for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) + { + if (bitset(QBADADDR, q->q_flags) || + !bitset(QPRIMARY, q->q_flags) || + !bitset(QDELAYED, q->q_flags)) + continue; + + if (printheader) + { + putline(" ----- The following addresses had transient non-fatal errors -----", + mci); + printheader = FALSE; + } + + snprintf(buf, sizeof buf, "%s", + shortenstring(q->q_paddr, MAXSHORTSTR)); + putline(buf, mci); + if (q->q_alias != NULL) + { + snprintf(buf, sizeof buf, " (expanded from: %s)", + shortenstring(q->q_alias->q_paddr, MAXSHORTSTR)); + putline(buf, mci); + } + } + if (!printheader) + putline("", mci); + + printheader = TRUE; + for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) + { + if (bitset(QBADADDR, q->q_flags) || + !bitset(QPRIMARY, q->q_flags) || + bitset(QDELAYED, q->q_flags)) + continue; + 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) + { + putline(" ----- The following addresses had successful delivery notifications -----", + mci); + printheader = FALSE; + } + + snprintf(buf, sizeof buf, "%s (%s)", + shortenstring(q->q_paddr, MAXSHORTSTR), p); + putline(buf, mci); + if (q->q_alias != NULL) + { + snprintf(buf, sizeof buf, " (expanded from: %s)", + shortenstring(q->q_alias->q_paddr, MAXSHORTSTR)); + putline(buf, mci); + } + } + if (!printheader) + putline("", mci); + + /* + ** Output transcript of errors + */ + + (void) fflush(stdout); + p = queuename(e->e_parent, 'x'); + if ((xfile = fopen(p, "r")) == NULL) + { + syserr("Cannot open %s", p); + putline(" ----- Transcript of session is unavailable -----\n", mci); + } + else + { + printheader = TRUE; + if (e->e_xfp != NULL) + (void) fflush(e->e_xfp); + while (fgets(buf, sizeof buf, xfile) != NULL) + { + if (printheader) + putline(" ----- Transcript of session follows -----\n", mci); + printheader = FALSE; + putline(buf, mci); + } + (void) xfclose(xfile, "errbody xscript", p); + } + errno = 0; + +#if DSN + /* + ** Output machine-readable version. + */ + + if (e->e_msgboundary != NULL) + { + putline("", mci); + (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); + putline(buf, mci); + putline("Content-Type: message/delivery-status", mci); + putline("", mci); + + /* + ** Output per-message information. + */ + + /* original envelope id from MAIL FROM: line */ + if (e->e_parent->e_envid != NULL) + { + (void) snprintf(buf, sizeof buf, "Original-Envelope-Id: %.800s", + xuntextify(e->e_parent->e_envid)); + putline(buf, mci); + } + + /* Reporting-MTA: is us (required) */ + (void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", MyHostName); + putline(buf, mci); + + /* 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) snprintf(buf, sizeof buf, "Received-From-MTA: %s; %.800s", + p, RealHostName); + putline(buf, mci); + } + + /* Arrival-Date: -- when it arrived here */ + (void) snprintf(buf, sizeof buf, "Arrival-Date: %s", + arpadate(ctime(&e->e_parent->e_ctime))); + putline(buf, mci); + + /* + ** Output per-address information. + */ + + for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) + { + register ADDRESS *r; + char *action; + + if (bitset(QBADADDR, q->q_flags)) + 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 + continue; + + putline("", mci); + + /* Original-Recipient: -- passed from on high */ + if (q->q_orcpt != NULL) + { + (void) snprintf(buf, sizeof buf, "Original-Recipient: %.800s", + q->q_orcpt); + putline(buf, mci); + } + + /* Final-Recipient: -- the name from the RCPT command */ + p = e->e_parent->e_from.q_mailer->m_addrtype; + if (p == NULL) + p = "rfc822"; + for (r = q; r->q_alias != NULL; r = r->q_alias) + continue; + if (strchr(r->q_user, '@') != NULL) + { + (void) snprintf(buf, sizeof buf, + "Final-Recipient: %s; %.800s", + p, r->q_user); + } + else if (strchr(r->q_paddr, '@') != NULL) + { + (void) snprintf(buf, sizeof buf, + "Final-Recipient: %s; %.800s", + p, r->q_paddr); + } + else + { + (void) snprintf(buf, sizeof buf, + "Final-Recipient: %s; %.700s@%.100s", + p, r->q_user, MyHostName); + } + putline(buf, mci); + + /* X-Actual-Recipient: -- the real problem address */ + if (r != q && q->q_user[0] != '\0') + { + if (strchr(q->q_user, '@') == NULL) + { + (void) snprintf(buf, sizeof buf, + "X-Actual-Recipient: %s; %.700s@%.100s", + p, q->q_user, MyHostName); + } + else + { + (void) snprintf(buf, sizeof buf, + "X-Actual-Recipient: %s; %.800s", + p, q->q_user); + } + putline(buf, mci); + } + + /* Action: -- what happened? */ + snprintf(buf, sizeof buf, "Action: %s", action); + putline(buf, mci); + + /* Status: -- what _really_ happened? */ + if (q->q_status != NULL) + p = q->q_status; + else if (bitset(QBADADDR, q->q_flags)) + p = "5.0.0"; + else if (bitset(QQUEUEUP, q->q_flags)) + p = "4.0.0"; + else + p = "2.0.0"; + snprintf(buf, sizeof buf, "Status: %s", p); + putline(buf, mci); + + /* 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) snprintf(buf, sizeof buf, + "Remote-MTA: %s; %.800s", + p, q->q_statmta); + p = &buf[strlen(buf) - 1]; + if (*p == '.') + *p = '\0'; + putline(buf, mci); + } + + /* Diagnostic-Code: -- actual result from other end */ + if (q->q_rstatus != NULL) + { + p = q->q_mailer->m_diagtype; + if (p == NULL) + p = "smtp"; + (void) snprintf(buf, sizeof buf, + "Diagnostic-Code: %s; %.800s", + p, q->q_rstatus); + putline(buf, mci); + } + + /* Last-Attempt-Date: -- fine granularity */ + if (q->q_statdate == (time_t) 0L) + q->q_statdate = curtime(); + (void) snprintf(buf, sizeof buf, + "Last-Attempt-Date: %s", + arpadate(ctime(&q->q_statdate))); + putline(buf, mci); + + /* Will-Retry-Until: -- for delayed messages only */ + if (bitset(QQUEUEUP, q->q_flags) && + !bitset(QBADADDR, q->q_flags)) + { + time_t xdate; + + xdate = e->e_parent->e_ctime + + TimeOuts.to_q_return[e->e_parent->e_timeoutclass]; + snprintf(buf, sizeof buf, + "Will-Retry-Until: %s", + arpadate(ctime(&xdate))); + putline(buf, mci); + } + } + } +#endif + + /* + ** Output text of original message + */ + + putline("", mci); + 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 (sendbody) + putline(" ----- Original message follows -----\n", mci); + else + putline(" ----- Message header follows -----\n", mci); + (void) fflush(mci->mci_out); + } + else + { + (void) snprintf(buf, sizeof buf, "--%s", + e->e_msgboundary); + + putline(buf, mci); + (void) snprintf(buf, sizeof buf, "Content-Type: %s", + sendbody ? "message/rfc822" + : "text/rfc822-headers"); + putline(buf, mci); + + p = hvalue("Content-Transfer-Encoding", e->e_parent->e_header); + if (p != NULL && strcasecmp(p, "binary") != 0) + p = NULL; + if (p == NULL && bitset(EF_HAS8BIT, e->e_parent->e_flags)) + p = "8bit"; + if (p != NULL) + { + (void) snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %s", + p); + putline(buf, mci); + } + } + putline("", mci); + putheader(mci, e->e_parent->e_header, e->e_parent); + if (sendbody) + putbody(mci, e->e_parent, e->e_msgboundary); + else if (e->e_msgboundary == NULL) + { + putline("", mci); + putline(" ----- Message body suppressed -----", mci); + } + } + else if (e->e_msgboundary == NULL) + { + putline(" ----- No message was collected -----\n", mci); + } + + if (e->e_msgboundary != NULL) + { + putline("", mci); + (void) snprintf(buf, sizeof buf, "--%s--", e->e_msgboundary); + putline(buf, mci); + } + putline("", mci); + + /* + ** Cleanup and exit + */ + + if (errno != 0) + syserr("errbody: I/O error"); +} + /* +** SMTPTODSN -- convert SMTP to DSN status code +** +** Parameters: +** smtpstat -- the smtp status code (e.g., 550). +** +** Returns: +** The DSN version of the status code. +*/ + +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 ((smtpstat / 100) == 2) + return "2.0.0"; + if ((smtpstat / 100) == 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) + return t; + l += nbogus * 2 + 1; + + /* now allocate space if necessary for the new string */ + if (l > bplen) + { + if (bp != NULL) + free(bp); + bp = xalloc(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) + free(bp); + 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 +*/ + +bool +pruneroute(addr) + char *addr; +{ +#if NAMED_BIND + char *start, *at, *comma; + char c; + 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; + start = strchr(addr, ':'); + at = strrchr(addr, '@'); + if (start == NULL || at == NULL || at < start) + return FALSE; + + /* slice off the angle brackets */ + i = strlen(at + 1); + if (i >= (SIZE_T) sizeof hostbuf) + return FALSE; + strcpy(hostbuf, at + 1); + hostbuf[i - 1] = '\0'; + + while (start) + { + if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0) + { + strcpy(addr + 1, start + 1); + return TRUE; + } + c = *start; + *start = '\0'; + comma = strrchr(addr, ','); + if (comma != NULL && comma[1] == '@' && + strlen(comma + 2) < (SIZE_T) sizeof hostbuf) + strcpy(hostbuf, comma + 2); + else + comma = NULL; + *start = c; + start = comma; + } +#endif + return FALSE; +} diff --git a/contrib/sendmail/src/sendmail.8 b/contrib/sendmail/src/sendmail.8 new file mode 100644 index 0000000..5105e83 --- /dev/null +++ b/contrib/sendmail/src/sendmail.8 @@ -0,0 +1,577 @@ +.\" Copyright (c) 1998 Sendmail, Inc. 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. +.\" +.\" +.\" @(#)sendmail.8 8.19 (Berkeley) 5/19/98 +.\" +.Dd May 19, 1998 +.Dt SENDMAIL 8 +.Os BSD 4 +.Sh NAME +.Nm sendmail +.Nd an electronic mail transport agent +.Sh SYNOPSIS +.Nm sendmail +.Op Ar flags +.Op Ar address ... +.Nm newaliases +.Nm mailq +.Op Fl v +.Sh DESCRIPTION +.Nm Sendmail +sends a message to one or more +.Em recipients , +routing the message over whatever networks +are necessary. +.Nm Sendmail +does internetwork forwarding as necessary +to deliver the message to the correct place. +.Pp +.Nm Sendmail +is not intended as a user interface routine; +other programs provide user-friendly +front ends; +.Nm sendmail +is used only to deliver pre-formatted messages. +.Pp +With no flags, +.Nm 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. +Normally the sender is not included in any alias +expansions, e.g., +if `john' sends to `group', +and `group' includes `john' in the expansion, +then the letter will not be delivered to `john'. +.Ss Parameters +.Bl -tag -width Fl +.It Fl B Ns Ar type +Set the body type to +.Ar type . +Current legal values +.Li 7BIT +or +.Li 8BITMIME . +.It Fl ba +Go into +.Tn 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. +.It Fl bd +Run as a daemon. This requires Berkeley +.Tn IPC . +.Nm Sendmail +will fork and run in background +listening on socket 25 for incoming +.Tn SMTP +connections. +This is normally run from +.Pa /etc/rc . +.It Fl bD +Same as +.Fl bd +except runs in foreground. +.It Fl bh +Print the persistent host status database. +.It Fl bH +Purge the persistent host status database. +.It Fl bi +Initialize the alias database. +.It Fl bm +Deliver mail in the usual way (default). +.It Fl bp +Print a listing of the queue. +.It Fl bs +Use the +.Tn SMTP +protocol as described in +.Tn RFC821 +on standard input and output. +This flag implies all the operations of the +.Fl ba +flag that are compatible with +.Tn SMTP . +.It Fl bt +Run in address test mode. +This mode reads addresses and shows the steps in parsing; +it is used for debugging configuration tables. +.It Fl bv +Verify names only \- do not try to collect or deliver a message. +Verify mode is normally used for validating +users or mailing lists. +.It Fl C Ns Ar file +Use alternate configuration file. +.Nm Sendmail +refuses to run as root if an alternate configuration file is specified. +.It Fl d Ns Ar X +Set debugging value to +.Ar X . +.ne 1i +.It Fl F Ns Ar fullname +Set the full name of the sender. +.It Fl f Ns Ar name +Sets the name of the ``from'' person +(i.e., the sender of the mail). +.Fl f +can only be used +by ``trusted'' users +(normally +.Em root , +.Em daemon , +and +.Em network ) +or if the person you are trying to become +is the same as the person you are. +.It Fl h Ns Ar N +Set the hop count to +.Ar 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. +.It Fl i +Ignore dots alone on lines by themselves in incoming messages. +This should be set if you are reading data from a file. +.It Fl N Ar dsn +Set delivery status notification conditions to +.Ar dsn, +which can be +.Ql never +for no notifications +or a comma separated list of the values +.Ql failure +to be notified if delivery failed, +.Ql delay +to be notified if delivery is delayed, and +.Ql success +to be notified when the message is successfully delivered. +.It Fl n +Don't do aliasing. +.It Fl O Ar option Ns = Ns Em value +Set option +.Ar option +to the specified +.Em value . +This form uses long names. +See below for more details. +.It Fl o Ns Ar x Em value +Set option +.Ar x +to the specified +.Em value . +This form uses single character names only. +The short names are not described in this manual page; +see the +.%T "Sendmail Installation and Operation Guide" +for details. +.It Fl p Ns Ar 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''. +.It Fl q Ns Bq Ar time +Processed saved messages in the queue at given intervals. +If +.Ar time +is omitted, +process the queue once. +.Xr Time +is given as a tagged number, +with +.Ql s +being seconds, +.Ql m +being minutes, +.Ql h +being hours, +.Ql d +being days, +and +.Ql w +being weeks. +For example, +.Ql \-q1h30m +or +.Ql \-q90m +would both set the timeout to one hour thirty minutes. +If +.Ar time +is specified, +.Nm sendmail +will run in background. +This option can be used safely with +.Fl bd . +.It Fl qI Ns Ar substr +Limit processed jobs to those containing +.Ar substr +as a substring of the queue id. +.It Fl qR Ns Ar substr +Limit processed jobs to those containing +.Ar substr +as a substring of one of the recipients. +.It Fl qS Ns Ar substr +Limit processed jobs to those containing +.Ar substr +as a substring of the sender. +.It Fl R Ar return +Set the amount of the message to be returned +if the message bounces. +The +.Ar return +parameter can be +.Ql full +to return the entire message or +.Ql hdrs +to return only the headers. +.It Fl r Ns Ar name +An alternate and obsolete form of the +.Fl f +flag. +.It Fl t +Read message for recipients. +To:, Cc:, and Bcc: lines will be scanned for recipient addresses. +The Bcc: line will be deleted before transmission. +.It Fl U +Initial (user) submission. +This should +.Em always +be set when called from a user agent such as +.Nm Mail +or +.Nm exmh +and +.Em never +be set when called by a network delivery agent such as +.Nm rmail . +.It Fl V Ar envid +Set the original envelope id. +This is propagated across SMTP to servers that support DSNs +and is returned in DSN-compliant error messages. +.It Fl v +Go into verbose mode. +Alias expansions will be announced, etc. +.It Fl X Ar 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. +.El +.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 +.Fl o +flag (for short names), +the +.Fl 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 +.%T "Sendmail Installation and Operation Guide" . +The options are: +.Bl -tag -width Fl +.It Li AliasFile= Ns Ar file +Use alternate alias file. +.It Li HoldExpensive +On mailers that are considered ``expensive'' to connect to, +don't initiate immediate connection. +This requires queueing. +.It Li CheckpointInterval= Ns Ar N +Checkpoint the queue file after every +.Ar N +successful deliveries (default 10). +This avoids excessive duplicate deliveries +when sending to long mailing lists +interrupted by system crashes. +.ne 1i +.It Li DeliveryMode= Ns Ar x +Set the delivery mode to +.Ar x . +Delivery modes are +.Ql i +for interactive (synchronous) delivery, +.Ql b +for background (asynchronous) delivery, +.Ql q +for queue only \- i.e., +actual delivery is done the next time the queue is run, and +.Ql d +for deferred \- the same as +.Ql q +except that database lookups (notably DNS and NIS lookups) are avoided. +.It Li ErrorMode= Ns Ar x +Set error processing to mode +.Ar x . +Valid modes are +.Ql m +to mail back the error message, +.Ql w +to ``write'' back the error message +(or mail it back if the sender is not logged in), +.Ql p +to print the errors on the terminal +(default), +.Ql q +to throw away error messages +(only exit status is returned), +and +.Ql e +to do special processing for the BerkNet. +If the text of the message is not mailed back +by +modes +.Ql m +or +.Ql w +and if the sender is local to this machine, +a copy of the message is appended to the file +.Pa dead.letter +in the sender's home directory. +.It Li SaveFromLine +Save +.Tn UNIX Ns \-style +From lines at the front of messages. +.It Li MaxHopCount= Ar N +The maximum number of times a message is allowed to ``hop'' +before we decide it is in a loop. +.It Li IgnoreDots +Do not take dots on a line by themselves +as a message terminator. +.It Li SendMimeErrors +Send error messages in MIME format. +If not set, the DSN (Delivery Status Notification) SMTP extension +is disabled. +.It Li ConnectionCacheTimeout= Ns Ar timeout +Set connection cache timeout. +.It Li ConnectionCacheSize= Ns Ar N +Set connection cache size. +.It Li LogLevel= Ns Ar n +The log level. +.It Li MeToo +Send to ``me'' (the sender) also if I am in an alias expansion. +.It Li CheckAliases +Validate the right hand side of aliases during a +.Xr newaliases 1 +command. +.It Li 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. +.It Li QueueDirectory= Ns Ar queuedir +Select the directory in which to queue messages. +.It Li StatusFile= Ns Ar file +Save statistics in the named file. +.It Li Timeout.queuereturn= Ns Ar 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. +.It Li UserDatabaseSpec= Ns Ar 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 +.Dv USERDB +option compiled in. +.It Li ForkEachJob +Fork each job during queue runs. +May be convenient on memory-poor machines. +.It Li SevenBitInput +Strip incoming messages to seven bits. +.It Li EightBitMode= Ns Ar mode +Set the handling of eight bit input to seven bit destinations to +.Ar mode : +.Li m +(mimefy) will convert to seven-bit MIME format, +.Li p +(pass) will pass it as eight bits (but violates protocols), +and +.Li s +(strict) will bounce the message. +.It Li MinQueueAge= Ns Ar timeout +Sets how long a job must ferment in the queue between attempts to send it. +.It Li DefaultCharSet= Ns Ar charset +Sets the default character set used to label 8-bit data +that is not otherwise labelled. +.It Li DialDelay= Ns Ar sleeptime +If opening a connection fails, +sleep for +.Ar sleeptime +seconds and try again. +Useful on dial-on-demand sites. +.It Li NoRecipientAction= Ns Ar action +Set the behaviour when there are no recipient headers (To:, Cc: or Bcc:) +in the message to +.Ar action : +.Li none +leaves the message unchanged, +.Li add-to +adds a To: header with the envelope recipients, +.Li add-apparently-to +adds an Apparently-To: header with the envelope recipients, +.Li add-bcc +adds an empty Bcc: header, and +.Li add-to-undisclosed +adds a header reading +.Ql "To: undisclosed-recipients:;" . +.It Li MaxDaemonChildren= Ns Ar N +Sets the maximum number of children that an incoming SMTP daemon +will allow to spawn at any time to +.Ar N . +.It Li ConnectionRateThrottle= Ns Ar N +Sets the maximum number of connections per second to the SMTP port to +.Ar N . +.El +.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 +.Nm sendmail +from suppressing the blanks from between arguments. +For example, a common alias is: +.Pp +.Bd -literal -offset indent -compact +msgs: "|/usr/bin/msgs -s" +.Ed +.Pp +Aliases may also have the syntax +.Dq :include: Ns Ar filename +to ask +.Xr sendmail +to read the named file for a list of recipients. +For example, an alias such as: +.Pp +.Bd -literal -offset indent -compact +poets: ":include:/usr/local/lib/poets.list" +.Ed +.Pp +would read +.Pa /usr/local/lib/poets.list +for the list of addresses making up the group. +.Pp +.Nm Sendmail +returns an exit status +describing what it did. +The codes are defined in +.Aq Pa sysexits.h : +.Bl -tag -width EX_UNAVAILABLE -compact -offset indent +.It Dv EX_OK +Successful completion on all addresses. +.It Dv EX_NOUSER +User name not recognized. +.It Dv EX_UNAVAILABLE +Catchall meaning necessary resources +were not available. +.It Dv EX_SYNTAX +Syntax error in address. +.It Dv EX_SOFTWARE +Internal software error, +including bad arguments. +.It Dv EX_OSERR +Temporary operating system error, +such as +.Dq cannot fork . +.It Dv EX_NOHOST +Host name not recognized. +.It Dv EX_TEMPFAIL +Message could not be sent immediately, +but was queued. +.El +.Pp +If invoked as +.Nm newaliases , +.Nm sendmail +will rebuild the alias database. +If invoked as +.Nm mailq , +.Nm sendmail +will print the contents of the mail queue. +.Sh FILES +Except for the file +.Pa /etc/sendmail.cf +itself and the daemon process ID file, +the following pathnames are all specified in +.Pa /etc/sendmail.cf. +Thus, +these values are only approximations. +.Pp +.Bl -tag -width /usr/lib/sendmail.fc -compact +.It Pa /etc/aliases +raw data for alias names +.It Pa /etc/aliases.db +data base of alias names +.It Pa /etc/sendmail.cf +configuration file +.It Pa /etc/sendmail.hf +help file +.It Pa /var/log/sendmail.st +collected statistics +.It Pa /var/spool/mqueue/* +temp files +.El +.Sh SEE ALSO +.Xr binmail 1 , +.Xr mail 1 , +.Xr rmail 1 , +.Xr syslog 3 , +.Xr aliases 5 , +.Xr mailaddr 7 , +.Xr rc 8 ; +.Pp +DARPA +Internet Request For Comments +.%T RFC819 , +.%T RFC821 , +.%T RFC822 . +.Rs +.%T "Sendmail \- An Internetwork Mail Router" +.%V SMM +.%N \&No. 9 +.Re +.Rs +.%T "Sendmail Installation and Operation Guide" +.%V SMM +.%N \&No. 8 +.Re +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/contrib/sendmail/src/sendmail.h b/contrib/sendmail/src/sendmail.h new file mode 100644 index 0000000..7bfe11b --- /dev/null +++ b/contrib/sendmail/src/sendmail.h @@ -0,0 +1,1500 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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 8.280 (Berkeley) 6/5/98 + */ + +/* +** SENDMAIL.H -- Global definitions for sendmail. +*/ + +# ifdef _DEFINE +# define EXTERN +# ifndef lint +static char SmailSccsId[] = "@(#)sendmail.h 8.280 6/5/98"; +# endif +# else /* _DEFINE */ +# define EXTERN extern +# endif /* _DEFINE */ + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# ifdef EX_OK +# undef EX_OK /* for SVr4.2 SMP */ +# endif +# include + +# include "conf.h" +# include "useful.h" + +# ifdef LOG +# include +# endif /* LOG */ + +# if NETINET || NETUNIX || NETISO || NETNS || NETX25 +# include +# endif +# if NETUNIX +# include +# endif +# if NETINET +# include +# endif +# if NETISO +# include +# endif +# if NETNS +# include +# endif +# if NETX25 +# include +# endif + +#if NAMED_BIND +# include +# ifdef NOERROR +# undef NOERROR /* avoid conflict */ +# endif +#endif + +#ifdef HESIOD +# include +# if !defined(HES_ER_OK) || defined(HESIOD_INTERFACES) +# define HESIOD_INIT /* support for the new interface */ +EXTERN void *HesiodContext; +# endif +#endif + +/* +** 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 +#ifndef INT16SZ +# define INT16SZ 2 /* size of a 16 bit integer in bytes */ +#endif +#ifndef INT32SZ +# define INT32SZ 4 /* size of a 32 bit integer in bytes */ +#endif + + + +/* forward references for prototypes */ +typedef struct envelope ENVELOPE; +typedef struct mailer MAILER; + + +/* +** Data structure for bit maps. +** +** Each bit in this map can be referenced by an ascii character. +** This is 256 possible bits, or 32 8-bit bytes. +*/ + +#define BITMAPBYTES 32 /* number of bytes in a bit map */ +#define BYTEBITS 8 /* number of bits in a byte */ + +/* internal macros */ +#define _BITWORD(bit) ((bit) / (BYTEBITS * sizeof (int))) +#define _BITBIT(bit) (1 << ((bit) % (BYTEBITS * sizeof (int)))) + +typedef int BITMAP[BITMAPBYTES / sizeof (int)]; + +/* test bit number N */ +#define bitnset(bit, map) ((map)[_BITWORD(bit)] & _BITBIT(bit)) + +/* set bit number N */ +#define setbitn(bit, map) (map)[_BITWORD(bit)] |= _BITBIT(bit) + +/* clear bit number N */ +#define clrbitn(bit, map) (map)[_BITWORD(bit)] &= ~_BITBIT(bit) + +/* clear an entire bit map */ +#define clrbitmap(map) bzero((char *) map, BITMAPBYTES) + + +/* +** Utility macros +*/ + +/* return number of bytes left in a buffer */ +#define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf)) + /* +** Address structure. +** Addresses are stored internally in this structure. +*/ + +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 */ + u_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 */ + 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_specificity; /* how "specific" this address is */ +}; + +typedef struct address ADDRESS; + +# define QDONTSEND 0x00000001 /* don't send to this address */ +# define QBADADDR 0x00000002 /* this address is verified bad */ +# define QGOODUID 0x00000004 /* the q_uid q_gid fields are good */ +# define QPRIMARY 0x00000008 /* set from RCPT or argv */ +# define QQUEUEUP 0x00000010 /* queue for later transmission */ +# define QSENT 0x00000020 /* has been successfully delivered */ +# define QNOTREMOTE 0x00000040 /* address not for remote forwarding */ +# define QSELFREF 0x00000080 /* this address references itself */ +# define QVERIFIED 0x00000100 /* verified, but not expanded */ +# define QBOGUSSHELL 0x00000400 /* user has no valid shell listed */ +# define QUNSAFEADDR 0x00000800 /* address aquired via unsafe path */ +# define QPINGONSUCCESS 0x00001000 /* give return on successful delivery */ +# define QPINGONFAILURE 0x00002000 /* give return on failure */ +# define QPINGONDELAY 0x00004000 /* give return on message delay */ +# define QHASNOTIFY 0x00008000 /* propogate notify parameter */ +# define QRELAYED 0x00010000 /* DSN: relayed to non-DSN aware sys */ +# define QEXPANDED 0x00020000 /* DSN: undergone list expansion */ +# define QDELIVERED 0x00040000 /* DSN: successful final delivery */ +# define QDELAYED 0x00080000 /* DSN: message delayed */ +# define QTHISPASS 0x40000000 /* temp: address set this pass */ +# define QRCPTOK 0x80000000 /* recipient() processed address */ + +# define Q_PINGFLAGS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY) + +# define NULLADDR ((ADDRESS *) NULL) + +/* functions */ +extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **, ENVELOPE *)); +extern ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *)); +extern ADDRESS *recipient __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); +extern char **prescan __P((char *, int, char[], int, char **, u_char *)); +extern int rewrite __P((char **, int, int, ENVELOPE *)); +extern char *remotename __P((char *, MAILER *, int, int *, ENVELOPE *)); +extern ADDRESS *getctladdr __P((ADDRESS *)); +extern bool sameaddr __P((ADDRESS *, ADDRESS *)); +extern bool emptyaddr __P((ADDRESS *)); +extern void printaddr __P((ADDRESS *, bool)); +extern void cataddr __P((char **, char **, char *, int, int)); +extern int sendtolist __P((char *, ADDRESS *, ADDRESS **, int, ENVELOPE *)); + /* +** 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 */ + BITMAP 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 */ + char *m_execdir; /* directory to chdir 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 */ +}; + +/* bits for m_flags */ +# define M_ESMTP 'a' /* run Extended SMTP protocol */ +# define M_ALIASABLE 'A' /* user can be LHS of an alias */ +# define M_BLANKEND 'b' /* ensure blank line at end of message */ +# define M_NOCOMMENT 'c' /* don't include comment part of address */ +# define M_CANONICAL 'C' /* make addresses canonical "u@dom" */ +# define M_NOBRACKET 'd' /* never angle bracket envelope route-addrs */ + /* '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 */ + /* 'x' CF: include Full-Name: */ +# define M_XDOT 'X' /* use hidden-dot algorithm */ +# define M_LMTP 'z' /* run Local Mail Transport Protocol */ +# define M_NOMX '0' /* turn off MX lookups */ +# define M_NONULLS '1' /* don't send null bytes */ +# define M_EBCDIC '3' /* extend Q-P encoding for EBCDIC */ +# define M_TRYRULESET5 '5' /* use ruleset 5 after local aliasing */ +# define M_7BITHDRS '6' /* strip headers to 7 bits even in 8 bit path */ +# define M_7BITS '7' /* use 7-bit path */ +# define M_8BITS '8' /* force "just send 8" behaviour */ +# define M_MAKE8BIT '9' /* convert 7 -> 8 bit if appropriate */ +# define M_CHECKINCLUDE ':' /* check for :include: files */ +# define M_CHECKPROG '|' /* check for |program addresses */ +# define M_CHECKFILE '/' /* check for /file addresses */ +# define M_CHECKUDB '@' /* user can be user database key */ +# define M_CHECKHDIR '~' /* SGI: check for valid home directory */ + +EXTERN MAILER *Mailer[MAXMAILERS+1]; + +EXTERN MAILER *LocalMailer; /* ptr to local mailer */ +EXTERN MAILER *ProgMailer; /* ptr to program mailer */ +EXTERN MAILER *FileMailer; /* ptr to *file* mailer */ +EXTERN MAILER *InclMailer; /* ptr to *include* mailer */ + /* +** Information about currently open connections to mailers, or to +** hosts that we have looked up recently. +*/ + +# define MCI struct mailer_con_info + +MCI +{ + short mci_flags; /* flag bits, see below */ + short mci_errno; /* error number on last connection */ + short mci_herrno; /* h_errno from last DNS lookup */ + short mci_exitstat; /* exit status from last connection */ + short mci_state; /* SMTP state */ + off_t mci_contentlen; /* body length for Content-Length: */ + long mci_maxsize; /* max size this server will accept */ + FILE *mci_in; /* input side of connection */ + FILE *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 */ + FILE *mci_statfile; /* long term status file */ +}; + + +/* flag bits */ +#define MCIF_VALID 0x0001 /* this entry is valid */ +#define MCIF_TEMP 0x0002 /* don't cache this connection */ +#define MCIF_CACHED 0x0004 /* currently in open cache */ +#define MCIF_ESMTP 0x0008 /* this host speaks ESMTP */ +#define MCIF_EXPN 0x0010 /* EXPN command supported */ +#define MCIF_SIZE 0x0020 /* SIZE option supported */ +#define MCIF_8BITMIME 0x0040 /* BODY=8BITMIME supported */ +#define MCIF_7BIT 0x0080 /* strip this message to 7 bits */ +#define MCIF_MULTSTAT 0x0100 /* MAIL11V3: handles MULT status */ +#define MCIF_INHEADER 0x0200 /* currently outputing header */ +#define MCIF_CVT8TO7 0x0400 /* convert from 8 to 7 bits */ +#define MCIF_DSN 0x0800 /* DSN extension supported */ +#define MCIF_8BITOK 0x1000 /* OK to send 8 bit characters */ +#define MCIF_CVT7TO8 0x2000 /* convert from 7 to 8 bits */ +#define MCIF_INMIME 0x4000 /* currently reading MIME header */ + +/* states */ +#define MCIS_CLOSED 0 /* no traffic on this connection */ +#define MCIS_OPENING 1 /* sending initial protocol */ +#define MCIS_OPEN 2 /* open, initial protocol sent */ +#define MCIS_ACTIVE 3 /* message being sent */ +#define MCIS_QUITING 4 /* running quit protocol */ +#define MCIS_SSD 5 /* service shutting down */ +#define MCIS_ERROR 6 /* I/O error on connection */ + +/* functions */ +extern MCI *mci_get __P((char *, MAILER *)); +extern void mci_cache __P((MCI *)); +extern void mci_flush __P((bool, MCI *)); +extern void mci_dump __P((MCI *, bool)); +extern void mci_dump_all __P((bool)); +extern MCI **mci_scan __P((MCI *)); +extern int mci_traverse_persistent __P((int (*)(), char *)); +extern int mci_print_persistent __P((char *, char *)); +extern int mci_purge_persistent __P((char *, char *)); +extern int mci_lock_host __P((MCI *)); +extern void mci_unlock_host __P((MCI *)); +extern int mci_lock_host_statfile __P((MCI *)); +extern void mci_store_persistent __P((MCI *)); +extern int mci_read_persistent __P((FILE *, MCI *)); + /* +** Header structure. +** This structure is used internally to store header items. +*/ + +struct header +{ + char *h_field; /* the name of the field */ + char *h_value; /* the value of that field */ + struct header *h_link; /* the next header */ + u_short h_flags; /* status bits, see below */ + BITMAP h_mflags; /* m_flags bits needed */ +}; + +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 */ + u_short hi_flags; /* status bits, see below */ + char *hi_ruleset; /* validity check ruleset */ +}; + +extern struct hdrinfo HdrInfo[]; + +/* bits for h_flags and hi_flags */ +# define H_EOH 0x0001 /* this field terminates header */ +# define H_RCPT 0x0002 /* contains recipient addresses */ +# define H_DEFAULT 0x0004 /* if another value is found, drop this */ +# define H_RESENT 0x0008 /* this address is a "Resent-..." address */ +# define H_CHECK 0x0010 /* check h_mflags against m_flags */ +# define H_ACHECK 0x0020 /* ditto, but always (not just default) */ +# define H_FORCE 0x0040 /* force this field, even if default */ +# define H_TRACE 0x0080 /* this field contains trace information */ +# define H_FROM 0x0100 /* this is a from-type field */ +# define H_VALID 0x0200 /* this field has a validated value */ +# define H_RECEIPTTO 0x0400 /* this field has return receipt info */ +# define H_ERRORSTO 0x0800 /* this field has error address info */ +# define H_CTE 0x1000 /* this field is a content-transfer-encoding */ +# define H_CTYPE 0x2000 /* this is a content-type field */ +# define H_BCC 0x4000 /* Bcc: header: strip value or delete */ +# define H_ENCODABLE 0x8000 /* field can be RFC 1522 encoded */ + +/* functions */ +extern void addheader __P((char *, char *, HDR **)); +extern char *hvalue __P((char *, HDR *)); +extern void commaize __P((HDR *, char *, bool, MCI *, ENVELOPE *)); +extern void put_vanilla_header __P((HDR *, char *, MCI *)); +extern void eatheader __P((ENVELOPE *, bool)); +extern int chompheader __P((char *, bool, HDR **, ENVELOPE *)); + /* +** 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; /* the target person */ + 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 */ + long e_msgsize; /* size of the message in bytes */ + 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 */ + void (*e_puthdr)__P((MCI *, HDR *, ENVELOPE *)); + /* function to put header of message */ + void (*e_putbody)__P((MCI *, ENVELOPE *, char *)); + /* function to put body of message */ + struct envelope *e_parent; /* the message this one encloses */ + struct envelope *e_sibling; /* the next envelope of interest */ + char *e_bodytype; /* type of message body */ + FILE *e_dfp; /* temporary file */ + char *e_id; /* code for this entry in queue */ + FILE *e_xfp; /* transcript file */ + FILE *e_lockfp; /* the lock file for this message */ + char *e_message; /* error message */ + char *e_statmsg; /* stat msg (changes per delivery) */ + char *e_msgboundary; /* MIME-style message part boundary */ + char *e_origrcpt; /* original recipient (one only) */ + char *e_envid; /* envelope id from MAIL FROM: line */ + char *e_status; /* DSN status for this message */ + time_t e_dtime; /* time of last delivery attempt */ + int e_ntries; /* number of delivery attempts */ + dev_t e_dfdev; /* df file's device, for crash recov */ + ino_t e_dfino; /* df file's ino, for crash recovery */ + char *e_macro[256]; /* macro definitions */ +}; + +/* values for e_flags */ +#define EF_OLDSTYLE 0x0000001 /* use spaces (not commas) in hdrs */ +#define EF_INQUEUE 0x0000002 /* this message is fully queued */ +#define EF_NO_BODY_RETN 0x0000004 /* omit message body on error */ +#define EF_CLRQUEUE 0x0000008 /* disk copy is no longer needed */ +#define EF_SENDRECEIPT 0x0000010 /* send a return receipt */ +#define EF_FATALERRS 0x0000020 /* fatal errors occured */ +#define EF_DELETE_BCC 0x0000040 /* delete Bcc: headers entirely */ +#define EF_RESPONSE 0x0000080 /* this is an error or return receipt */ +#define EF_RESENT 0x0000100 /* this message is being forwarded */ +#define EF_VRFYONLY 0x0000200 /* verify only (don't expand aliases) */ +#define EF_WARNING 0x0000400 /* warning message has been sent */ +#define EF_QUEUERUN 0x0000800 /* this envelope is from queue */ +#define EF_GLOBALERRS 0x0001000 /* treat errors as global */ +#define EF_PM_NOTIFY 0x0002000 /* send return mail to postmaster */ +#define EF_METOO 0x0004000 /* send to me too */ +#define EF_LOGSENDER 0x0008000 /* need to log the sender */ +#define EF_NORECEIPT 0x0010000 /* suppress all return-receipts */ +#define EF_HAS8BIT 0x0020000 /* at least one 8-bit char in body */ +#define EF_NL_NOT_EOL 0x0040000 /* don't accept raw NL as EOLine */ +#define EF_CRLF_NOT_EOL 0x0080000 /* don't accept CR-LF as EOLine */ +#define EF_RET_PARAM 0x0100000 /* RCPT command had RET argument */ +#define EF_HAS_DF 0x0200000 /* set when df file is instantiated */ +#define EF_IS_MIME 0x0400000 /* really is a MIME message */ +#define EF_DONT_MIME 0x0800000 /* never MIME this message */ +#define EF_DISCARD 0x1000000 /* discard the message */ + +EXTERN ENVELOPE *CurEnv; /* envelope currently being processed */ + +/* functions */ +extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *)); +extern void dropenvelope __P((ENVELOPE *, bool)); +extern void clearenvelope __P((ENVELOPE *, bool)); + +extern void putheader __P((MCI *, HDR *, ENVELOPE *)); +extern void putbody __P((MCI *, ENVELOPE *, char *)); + /* +** 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 struct priority Priorities[MAXPRIORITIES]; +EXTERN int NumPriorities; /* pointer into Priorities */ + /* +** Rewrite rules. +*/ + +struct rewrite +{ + char **r_lhs; /* pattern match */ + char **r_rhs; /* substitution value */ + struct rewrite *r_next;/* next in chain */ +}; + +EXTERN struct rewrite *RewriteRules[MAXRWSETS]; + +/* +** 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. +*/ + +/* left hand side items */ +# define MATCHZANY ((u_char)0220) /* match zero or more tokens */ +# define MATCHANY ((u_char)0221) /* match one or more tokens */ +# define MATCHONE ((u_char)0222) /* match exactly one token */ +# define MATCHCLASS ((u_char)0223) /* match one token in a class */ +# define MATCHNCLASS ((u_char)0224) /* match anything not in class */ +# define MATCHREPL ((u_char)0225) /* replacement on RHS for above */ + +/* right hand side items */ +# define CANONNET ((u_char)0226) /* canonical net, next token */ +# define CANONHOST ((u_char)0227) /* canonical host, next token */ +# define CANONUSER ((u_char)0230) /* canonical user, next N tokens */ +# define CALLSUBR ((u_char)0231) /* call another rewriting set */ + +/* conditionals in macros */ +# define CONDIF ((u_char)0232) /* conditional if-then */ +# define CONDELSE ((u_char)0233) /* conditional else */ +# define CONDFI ((u_char)0234) /* conditional fi */ + +/* bracket characters for host name lookup */ +# define HOSTBEGIN ((u_char)0235) /* hostname lookup begin */ +# define HOSTEND ((u_char)0236) /* hostname lookup end */ + +/* bracket characters for generalized lookup */ +# define LOOKUPBEGIN ((u_char)0205) /* generalized lookup begin */ +# define LOOKUPEND ((u_char)0206) /* generalized lookup end */ + +/* macro substitution character */ +# define MACROEXPAND ((u_char)0201) /* macro expansion */ +# define MACRODEXPAND ((u_char)0202) /* deferred macro expansion */ + +/* to make the code clearer */ +# define MATCHZERO CANONHOST + +/* external <==> internal mapping table */ +struct metamac +{ + char metaname; /* external code (after $) */ + u_char metaval; /* internal code (as above) */ +}; + +/* values for macros with external names only */ +# define MID_OPMODE 0202 /* operation mode */ + +/* functions */ +extern void expand __P((char *, char *, size_t, ENVELOPE *)); +extern void define __P((int, char *, ENVELOPE *)); +extern char *macvalue __P((int, ENVELOPE *)); +extern char *macname __P((int)); +extern int macid __P((char *, char **)); + /* +** 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 */ +}; + +/* values for nc_flags */ +#define NCF_VALID 0x0001 /* entry valid */ + /* +** 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 3 /* size of map_actions array */ + + +/* +** An actual map. +*/ + +MAP +{ + MAPCLASS *map_class; /* the 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 */ + u_char map_keycolno; /* key column number */ + u_char map_valcolno; /* value column number */ + char map_coldelim; /* column delimiter */ + 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 */ + 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 */ +# define MF_UNSAFEDB 0x00004000 /* this map is world writable */ +# define MF_APPEND 0x00008000 /* append new entry on rebuiled */ +# define MF_KEEPQUOTES 0x00010000 /* don't dequote key before lookup */ +# define MF_NODEFER 0x00020000 /* don't defer if map lookup fails */ +# define MF_REGEX_NOT 0x00040000 /* regular expression negation */ + +/* 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 */ + +/* +** 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 */ + +/* functions */ +extern char *map_rewrite __P((MAP *, const char *, size_t, char **)); +extern MAP *makemapentry __P((char *)); +extern void initmaps __P((bool, ENVELOPE *)); + /* +** Symbol table definitions +*/ + +struct symtab +{ + char *s_name; /* name to be entered */ + short s_type; /* general type (see below) */ + short s_len; /* length of this entry */ + struct symtab *s_next; /* pointer to next in chain */ + union + { + BITMAP 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 */ + char *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 */ + } s_value; +}; + +typedef struct symtab STAB; + +/* symbol types */ +# define ST_UNDEF 0 /* undefined type */ +# define ST_CLASS 1 /* class map */ +# define ST_ADDRESS 2 /* an address in parsed format */ +# define ST_MAILER 3 /* a mailer header */ +# define ST_ALIAS 4 /* an alias */ +# define ST_MAPCLASS 5 /* mapping function class */ +# define ST_MAP 6 /* mapping function */ +# define ST_HOSTSIG 7 /* host signature */ +# define ST_NAMECANON 8 /* cached canonical name */ +# define ST_MACRO 9 /* macro name to id mapping */ +# define ST_RULESET 10 /* ruleset index */ +# define ST_SERVICE 11 /* service switch entry */ +# define ST_HEADER 12 /* special header flags */ +# define ST_MCI 16 /* mailer connection info (offset) */ + +# define s_class s_value.sv_class +# define s_address s_value.sv_addr +# define s_mailer s_value.sv_mailer +# define s_alias s_value.sv_alias +# define s_mci s_value.sv_mci +# define s_mapclass s_value.sv_mapclass +# define s_hostsig s_value.sv_hostsig +# define s_map s_value.sv_map +# define s_namecanon s_value.sv_namecanon +# define s_macro s_value.sv_macro +# define s_ruleset s_value.sv_ruleset +# define s_service s_value.sv_service +# define s_header s_value.sv_header + +extern STAB *stab __P((char *, int, int)); +extern void stabapply __P((void (*)(STAB *, int), int)); + +/* opcodes to stab */ +# define ST_FIND 0 /* find entry */ +# define ST_ENTER 1 /* enter if not there */ + /* +** STRUCT EVENT -- event queue. +** +** Maintained in sorted order. +** +** We store the pid of the process that set this event to insure +** that when we fork we will not take events intended for the parent. +*/ + +struct event +{ + time_t ev_time; /* time of the function call */ + void (*ev_func)__P((int)); + /* function to call */ + int ev_arg; /* argument to ev_func */ + int ev_pid; /* pid that set this event */ + struct event *ev_link; /* link to next item */ +}; + +typedef struct event EVENT; + +EXTERN EVENT *EventQueue; /* head of event queue */ + +/* functions */ +extern EVENT *setevent __P((time_t, void(*)(), int)); +extern void clrevent __P((EVENT *)); + /* +** 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. It's 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. +*/ + +EXTERN char OpMode; /* operation mode, see below */ + +#define MD_DELIVER 'm' /* be a mail sender */ +#define MD_SMTP 's' /* run SMTP on standard input */ +#define MD_ARPAFTP 'a' /* obsolete ARPANET mode (Grey Book) */ +#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_FREEZE 'z' /* freeze the configuration file */ +#define MD_HOSTSTAT 'h' /* print persistent host stat info */ +#define MD_PURGESTAT 'H' /* purge persistent host stat info */ + +/* values for e_sendmode -- send modes */ +#define SM_DELIVER 'i' /* interactive delivery */ +#define SM_FORK 'b' /* deliver in background */ +#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) */ + +/* used only as a parameter to sendall */ +#define SM_DEFAULT '\0' /* unspecified, use SendMode */ + + +/* 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) */ + + +/* MIME processing mode */ +EXTERN int MimeMode; + +/* bit values for MimeMode */ +#define MM_CVTMIME 0x0001 /* convert 8 to 7 bit MIME */ +#define MM_PASS8BIT 0x0002 /* just send 8 bit data blind */ +#define MM_MIME8BIT 0x0004 /* convert 8-bit data to MIME */ + +/* queue sorting order algorithm */ +EXTERN int QueueSortOrder; + +#define QS_BYPRIORITY 0 /* sort by message priority */ +#define QS_BYHOST 1 /* sort by first host name */ +#define QS_BYTIME 2 /* sort by submission time */ + + +/* how to handle messages without any recipient addresses */ +EXTERN int NoRecipientAction; + +#define NRA_NO_ACTION 0 /* just leave it as is */ +#define NRA_ADD_TO 1 /* add To: header */ +#define NRA_ADD_APPARENTLY_TO 2 /* add Apparently-To: header */ +#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 */ + /* +** Additional definitions +*/ + + +/* +** Privacy flags +** These are bit values for the PrivacyFlags word. +*/ + +#define PRIV_PUBLIC 0 /* what have I got to hide? */ +#define PRIV_NEEDMAILHELO 0x0001 /* insist on HELO for MAIL, at least */ +#define PRIV_NEEDEXPNHELO 0x0002 /* insist on HELO for EXPN */ +#define PRIV_NEEDVRFYHELO 0x0004 /* insist on HELO for VRFY */ +#define PRIV_NOEXPN 0x0008 /* disallow EXPN command entirely */ +#define PRIV_NOVRFY 0x0010 /* disallow VRFY command entirely */ +#define PRIV_AUTHWARNINGS 0x0020 /* flag possible authorization probs */ +#define PRIV_NORECEIPTS 0x0040 /* disallow return receipts */ +#define PRIV_NOETRN 0x0080 /* disallow ETRN command entirely */ +#define PRIV_NOVERB 0x0100 /* disallow VERB command entirely */ +#define PRIV_RESTRICTMAILQ 0x1000 /* restrict mailq command */ +#define PRIV_RESTRICTQRUN 0x2000 /* restrict queue run */ +#define PRIV_GOAWAY 0x0fff /* don't give no info, anyway, anyhow */ + +/* struct defining such things */ +struct prival +{ + char *pv_name; /* name of privacy flag */ + int pv_flag; /* numeric level */ +}; + + +/* +** 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 + + +/* +** Flags passed to safefile/safedirpath. +*/ + +#define SFF_ANYFILE 0 /* no special restrictions */ +#define SFF_MUSTOWN 0x0001 /* user must own this file */ +#define SFF_NOSLINK 0x0002 /* file cannot be a symbolic link */ +#define SFF_ROOTOK 0x0004 /* ok for root to own this file */ +#define SFF_RUNASREALUID 0x0008 /* if no ctladdr, run as real uid */ +#define SFF_NOPATHCHECK 0x0010 /* don't bother checking dir path */ +#define SFF_SETUIDOK 0x0020 /* setuid files are ok */ +#define SFF_CREAT 0x0040 /* ok to create file if necessary */ +#define SFF_REGONLY 0x0080 /* regular files only */ +#define SFF_SAFEDIRPATH 0x0100 /* no writable directories allowed */ +#define SFF_NOHLINK 0x0200 /* file cannot have hard links */ +#define SFF_NOWLINK 0x0400 /* links only in non-writable dirs */ +#define SFF_NOGWFILES 0x0800 /* disallow world writable files */ +#define SFF_NOWWFILES 0x1000 /* disallow group writable files */ + +/* flags that are actually specific to safeopen/safefopen/dfopen */ +#define SFF_OPENASROOT 0x2000 /* open as root instead of real user */ +#define SFF_NOLOCK 0x4000 /* don't lock the file */ + +/* pseudo-flags */ +#define SFF_NOLINK (SFF_NOHLINK|SFF_NOSLINK) + +/* functions */ +extern int safefile __P((char *, UID_T, GID_T, char *, int, int, struct stat *)); +extern int safedirpath __P((char *, UID_T, GID_T, char *, int)); +extern int safeopen __P((char *, int, int, int)); +extern FILE *safefopen __P((char *, int, int, int)); +extern int dfopen __P((char *, int, int, int)); +extern bool filechanged __P((char *, int, struct stat *)); + + +/* +** Flags passed to mime8to7. +*/ + +#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 */ + + +/* +** 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 */ + + +/* +** Regular UNIX sockaddrs are too small to handle ISO addresses, so +** we are forced to declare a supertype here. +*/ + +# if NETINET || NETUNIX || NETISO || NETNS || NETX25 +union bigsockaddr +{ + struct sockaddr sa; /* general version */ +#if NETUNIX + struct sockaddr_un sunix; /* UNIX family */ +#endif +#if NETINET + struct sockaddr_in sin; /* INET family */ +#endif +#if NETISO + struct sockaddr_iso siso; /* ISO family */ +#endif +#if NETNS + struct sockaddr_ns sns; /* XNS family */ +#endif +#if NETX25 + struct sockaddr_x25 sx25; /* X.25 family */ +#endif +}; + +#define SOCKADDR union bigsockaddr + +EXTERN SOCKADDR RealHostAddr; /* address of host we are talking to */ + +extern char *hostnamebyanyaddr __P((SOCKADDR *)); +extern char *anynet_ntoa __P((SOCKADDR *)); +# if DAEMON +extern char *validate_connection __P((SOCKADDR *, char *, ENVELOPE *)); +# endif + +#endif + + +/* +** 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@CS.Berkeley.EDU 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 */ + +EXTERN int VendorCode; /* vendor-specific operation enhancements */ + +/* prototypes for vendor-specific hook routines */ +extern void vendor_set_uid __P((UID_T)); +extern void vendor_daemon_setup __P((ENVELOPE *)); + +/* +** DontBlameSendmail options +** +** Hopefully nobody uses these. +*/ +#define DBS_SAFE 0 +#define DBS_ASSUMESAFECHOWN 0x00000001 +#define DBS_GROUPWRITABLEDIRPATHSAFE 0x00000002 +#define DBS_GROUPWRITABLEFORWARDFILESAFE 0x00000004 +#define DBS_GROUPWRITABLEINCLUDEFILESAFE 0x00000008 +#define DBS_GROUPWRITABLEALIASFILE 0x00000010 +#define DBS_WORLDWRITABLEALIASFILE 0x00000020 +#define DBS_FORWARDFILEINUNSAFEDIRPATH 0x00000040 +#define DBS_INCLUDEFILEINUNSAFEDIRPATH 0x00000060 +#define DBS_MAPINUNSAFEDIRPATH 0x00000080 +#define DBS_LINKEDALIASFILEINWRITABLEDIR 0x00000100 +#define DBS_LINKEDCLASSFILEINWRITABLEDIR 0x00000200 +#define DBS_LINKEDFORWARDFILEINWRITABLEDIR 0x00000400 +#define DBS_LINKEDINCLUDEFILEINWRITABLEDIR 0x00000800 +#define DBS_LINKEDMAPINWRITABLEDIR 0x00001000 +#define DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR 0x00002000 +#define DBS_FILEDELIVERYTOHARDLINK 0x00004000 +#define DBS_FILEDELIVERYTOSYMLINK 0x00008000 +#define DBS_WRITEMAPTOHARDLINK 0x00010000 +#define DBS_WRITEMAPTOSYMLINK 0x00020000 +#define DBS_WRITESTATSTOHARDLINK 0x00040000 +#define DBS_WRITESTATSTOSYMLINK 0x00080000 +#define DBS_FORWARDFILEINGROUPWRITABLEDIRPATH 0x00100000 +#define DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH 0x00200000 +#define DBS_CLASSFILEINUNSAFEDIRPATH 0x00400000 +#define DBS_ERRORHEADERINUNSAFEDIRPATH 0x00800000 +#define DBS_HELPFILEINUNSAFEDIRPATH 0x01000000 +#define DBS_FORWARDFILEINUNSAFEDIRPATHSAFE 0x02000000 +#define DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE 0x04000000 +#define DBS_RUNPROGRAMINUNSAFEDIRPATH 0x08000000 +#define DBS_RUNWRITABLEPROGRAM 0x10000000 + +/* struct defining such things */ +struct dbsval +{ + char *dbs_name; /* name of DontBlameSendmail flag */ + long dbs_flag; /* numeric level */ +}; + +EXTERN long DontBlameSendmail; /* DontBlameSendmail option bits */ + +/* +** Terminal escape codes. +** +** To make debugging output clearer. +*/ + +struct termescape +{ + char *te_rv_on; /* turn reverse-video on */ + char *te_rv_off; /* turn reverse-video off */ +}; + +EXTERN struct termescape TermEscape; + + +/* +** Error return from inet_addr(3), in case not defined in /usr/include. +*/ + +#ifndef INADDR_NONE +# define INADDR_NONE 0xffffffff +#endif + /* +** Global variables. +*/ + +EXTERN bool FromFlag; /* if set, "From" person is explicit */ +EXTERN bool MeToo; /* send to the sender also */ +EXTERN bool IgnrDot; /* don't let dot end messages */ +EXTERN bool SaveFrom; /* save leading "From" lines */ +EXTERN bool GrabTo; /* if set, get recipients from msg */ +EXTERN bool SuprErrs; /* set if we are suppressing errors */ +EXTERN bool HoldErrs; /* only output errors to transcript */ +EXTERN bool NoConnect; /* don't connect to non-local mailers */ +EXTERN bool SuperSafe; /* be extra careful, even if expensive */ +EXTERN bool ForkQueueRuns; /* fork for each job when running the queue */ +EXTERN bool AutoRebuild; /* auto-rebuild the alias database as needed */ +EXTERN bool CheckAliases; /* parse addresses during newaliases */ +EXTERN bool NoAlias; /* suppress aliasing */ +EXTERN bool UseNameServer; /* using DNS -- interpret h_errno & MX RRs */ +EXTERN bool UseHesiod; /* using Hesiod -- interpret Hesiod errors */ +EXTERN bool SevenBitInput; /* force 7-bit data on input */ +EXTERN bool HasEightBits; /* has at least one eight bit input byte */ +EXTERN bool ConfigFileRead; /* configuration file has been read */ +EXTERN time_t SafeAlias; /* interval to wait until @:@ in alias file */ +EXTERN FILE *InChannel; /* input connection */ +EXTERN FILE *OutChannel; /* output connection */ +EXTERN char *RealUserName; /* real user name of caller */ +EXTERN uid_t RealUid; /* real uid of caller */ +EXTERN gid_t RealGid; /* real gid of caller */ +EXTERN uid_t DefUid; /* default uid to run as */ +EXTERN gid_t DefGid; /* default gid to run as */ +EXTERN char *DefUser; /* default user to run as (from DefUid) */ +EXTERN uid_t TrustedFileUid; /* uid of trusted owner of files and dirs */ +EXTERN MODE_T OldUmask; /* umask when sendmail starts up */ +EXTERN int Verbose; /* set if blow-by-blow desired */ +EXTERN int Errors; /* set if errors (local to single pass) */ +EXTERN int ExitStat; /* exit status code */ +EXTERN int LineNumber; /* line number in current input */ +EXTERN int LogLevel; /* level of logging to perform */ +EXTERN int FileMode; /* mode on files */ +EXTERN int QueueLA; /* load average starting forced queueing */ +EXTERN int RefuseLA; /* load average refusing connections are */ +EXTERN int CurrentLA; /* current load average */ +EXTERN long QueueFactor; /* slope of queue function */ +EXTERN time_t QueueIntvl; /* intervals between running the queue */ +EXTERN char *HelpFile; /* location of SMTP help file */ +EXTERN char *ErrMsgFile; /* file to prepend to all error messages */ +EXTERN char *StatFile; /* location of statistics summary */ +EXTERN char *QueueDir; /* location of queue directory */ +EXTERN char *FileName; /* name to print on error messages */ +EXTERN char *SmtpPhase; /* current phase in SMTP processing */ +EXTERN char *MyHostName; /* name of this host for SMTP messages */ +EXTERN char *RealHostName; /* name of host we are talking to */ +EXTERN char *CurHostName; /* current host we are dealing with */ +EXTERN jmp_buf TopFrame; /* branch-to-top-of-loop-on-error frame */ +EXTERN bool QuickAbort; /* .... but only if we want a quick abort */ +EXTERN bool OnlyOneError; /* .... or only want to give one SMTP reply */ +EXTERN bool LogUsrErrs; /* syslog user errors (e.g., SMTP RCPT cmd) */ +EXTERN bool SendMIMEErrors; /* send error messages in MIME format */ +EXTERN bool MatchGecos; /* look for user names in gecos field */ +EXTERN bool UseErrorsTo; /* use Errors-To: header (back compat) */ +EXTERN bool TryNullMXList; /* if we are the best MX, try host directly */ +EXTERN bool InChild; /* true if running in an SMTP subprocess */ +EXTERN bool DisConnected; /* running with OutChannel redirected to xf */ +EXTERN bool ColonOkInAddr; /* single colon legal in address */ +EXTERN bool HasWildcardMX; /* don't use MX records when canonifying */ +EXTERN char SpaceSub; /* substitution for */ +EXTERN int PrivacyFlags; /* privacy flags */ +EXTERN char *ConfFile; /* location of configuration file [conf.c] */ +EXTERN char *PidFile; /* location of proc id file [conf.c] */ +extern ADDRESS NullAddress; /* a null (template) address [main.c] */ +EXTERN long WkClassFact; /* multiplier for message class -> priority */ +EXTERN long WkRecipFact; /* multiplier for # of recipients -> priority */ +EXTERN long WkTimeFact; /* priority offset each time this job is run */ +EXTERN char *UdbSpec; /* user database source spec */ +EXTERN int MaxHopCount; /* max # of hops until bounce */ +EXTERN int ConfigLevel; /* config file level */ +EXTERN char *TimeZoneSpec; /* override time zone specification */ +EXTERN char *ForwardPath; /* path to search for .forward files */ +EXTERN long MinBlocksFree; /* min # of blocks free on queue fs */ +EXTERN char *FallBackMX; /* fall back MX host */ +EXTERN long MaxMessageSize; /* advertised max size we will accept */ +EXTERN time_t MinQueueAge; /* min delivery interval */ +EXTERN time_t DialDelay; /* delay between dial-on-demand tries */ +EXTERN char *SafeFileEnv; /* chroot location for file delivery */ +EXTERN char *HostsFile; /* path to /etc/hosts file */ +EXTERN char *HostStatDir; /* location of host status information */ +EXTERN int MaxQueueRun; /* maximum number of jobs in one queue run */ +EXTERN int MaxChildren; /* maximum number of daemonic children */ +EXTERN int CurChildren; /* current number of daemonic children */ +EXTERN char *SmtpGreeting; /* SMTP greeting message (old $e macro) */ +EXTERN char *UnixFromLine; /* UNIX From_ line (old $l macro) */ +EXTERN char *OperatorChars; /* operators (old $o macro) */ +EXTERN bool DontInitGroups; /* avoid initgroups() because of NIS cost */ +EXTERN int DefaultNotify; /* default DSN notification flags */ +EXTERN bool AllowBogusHELO; /* allow syntax errors on HELO command */ +EXTERN bool UserSubmission; /* initial (user) mail submission */ +EXTERN char *RunAsUserName; /* user to become for bulk of run */ +EXTERN uid_t RunAsUid; /* UID to become for bulk of run */ +EXTERN gid_t RunAsGid; /* GID to become for bulk of run */ +EXTERN int MaxRcptPerMsg; /* max recipients per SMTP message */ +EXTERN bool DoQueueRun; /* non-interrupt time queue run needed */ +EXTERN u_long ConnectOnlyTo; /* override connection address (for testing) */ +#if _FFR_DSN_RRT_OPTION +EXTERN bool RrtImpliesDsn; /* turn Return-Receipt-To: into DSN */ +#endif +EXTERN char *DeadLetterDrop; /* path to dead letter office */ +EXTERN bool DontProbeInterfaces; /* don't probe interfaces for names */ +EXTERN bool ChownAlwaysSafe; /* treat chown(2) as safe */ +EXTERN bool IgnoreHostStatus; /* ignore long term host status files */ +EXTERN bool SingleThreadDelivery; /* single thread hosts on delivery */ +EXTERN bool SingleLineFromHeader; /* force From: header to be one line */ +EXTERN bool DontLockReadFiles; /* don't read lock support files */ +EXTERN int ConnRateThrottle; /* throttle for SMTP connection rate */ +EXTERN int MaxAliasRecursion; /* maximum depth of alias recursion */ +EXTERN int MaxMacroRecursion; /* maximum depth of macro recursion */ +EXTERN int MaxRuleRecursion; /* maximum depth of ruleset recursion */ +EXTERN char *MustQuoteChars; /* quote these characters in phrases */ +EXTERN char *ServiceSwitchFile; /* backup service switch */ +EXTERN char *DefaultCharSet; /* default character set for MIME */ +EXTERN char *PostMasterCopy; /* address to get errs cc's */ +EXTERN int CheckpointInterval; /* queue file checkpoint interval */ +EXTERN bool DontPruneRoutes; /* don't prune source routes */ +EXTERN bool DontExpandCnames; /* do not $[...$] expand CNAMEs */ +EXTERN int MaxMciCache; /* maximum entries in MCI cache */ +EXTERN time_t ServiceCacheTime; /* time service switch was cached */ +EXTERN time_t ServiceCacheMaxAge; /* refresh interval for cache */ +EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */ +EXTERN time_t MciInfoTimeout; /* how long 'til we retry down hosts */ +EXTERN FILE *TrafficLogFile; /* file in which to log all traffic */ +EXTERN char *DoubleBounceAddr; /* where to send double bounces */ +EXTERN char **ExternalEnviron; /* input environment */ +EXTERN char *UserEnviron[MAXUSERENVIRON + 1]; + /* saved user environment */ +extern int errno; + +/* +** Queue Run Limitations +*/ +struct queue_char +{ + char *queue_match; /* string to match */ + struct queue_char *queue_next; +}; + +typedef struct queue_char QUEUE_CHAR; + +EXTERN QUEUE_CHAR *QueueLimitRecipient; /* limit queue runs to this recipient */ +EXTERN QUEUE_CHAR *QueueLimitSender; /* limit queue runs to this sender */ +EXTERN QUEUE_CHAR *QueueLimitId; /* limit queue runs to this id */ + +/* +** 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_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 */ + /* following are per message */ + time_t to_q_return[MAXTOCLASS]; /* queue return timeouts */ + time_t to_q_warning[MAXTOCLASS]; /* queue warning timeouts */ +} 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 */ + + +/* +** Trace information +*/ + +/* trace vector and macros for debugging flags */ +EXTERN u_char tTdvect[100]; +# define tTd(flag, level) (tTdvect[flag] >= level) +# define tTdlevel(flag) (tTdvect[flag]) + /* +** Miscellaneous information. +*/ + + +/* +** The "no queue id" queue id for sm_syslog +*/ + +#define NOQID "*~*" + + +/* +** Some in-line functions +*/ + +/* set exit status */ +#define setstat(s) { \ + if (ExitStat == EX_OK || ExitStat == EX_TEMPFAIL) \ + ExitStat = s; \ + } + +/* make a copy of a string */ +#define newstr(s) strcpy(xalloc(strlen(s) + 1), s) + +#define STRUCTCOPY(s, d) d = s + + + +/* +** Declarations of useful functions +*/ + +extern char *xalloc __P((int)); +extern char *sfgets __P((char *, int, FILE *, time_t, char *)); +extern char *queuename __P((ENVELOPE *, int)); +extern time_t curtime __P((void)); +extern bool transienterror __P((int)); +extern char *fgetfolded __P((char *, int, FILE *)); +extern char *username __P((void)); +extern char *pintvl __P((time_t, bool)); +extern bool shouldqueue __P((long, time_t)); +extern bool lockfile __P((int, char *, char *, int)); +extern char *hostsignature __P((MAILER *, char *, ENVELOPE *)); +extern void openxscript __P((ENVELOPE *)); +extern void closexscript __P((ENVELOPE *)); +extern char *shortenstring __P((const char *, int)); +extern bool usershellok __P((char *, char *)); +extern char *defcharset __P((ENVELOPE *)); +extern bool wordinclass __P((char *, int)); +extern char *denlstring __P((char *, bool, bool)); +extern void makelower __P((char *)); +extern bool rebuildaliases __P((MAP *, bool)); +extern void readaliases __P((MAP *, FILE *, bool, bool)); +extern void finis __P((void)); +extern void setsender __P((char *, ENVELOPE *, char **, int, bool)); +extern void xputs __P((const char *)); +extern void logsender __P((ENVELOPE *, char *)); +extern void smtprset __P((MAILER *, MCI *, ENVELOPE *)); +extern void smtpquit __P((MAILER *, MCI *, ENVELOPE *)); +extern void setuserenv __P((const char *, const char *)); +extern char *getextenv __P((const char *)); +extern void disconnect __P((int, ENVELOPE *)); +extern void putxline __P((char *, size_t, MCI *, int)); +extern void dumpfd __P((int, bool, bool)); +extern void makemailer __P((char *)); +extern void putfromline __P((MCI *, ENVELOPE *)); +extern void setoption __P((int, char *, bool, bool, ENVELOPE *)); +extern void setclass __P((int, char *)); +extern void inittimeouts __P((char *)); +extern void logdelivery __P((MAILER *, MCI *, const char *, ADDRESS *, time_t, ENVELOPE *)); +extern void giveresponse __P((int, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *)); +extern void buildfname __P((char *, char *, char *, int)); +extern void mci_setstat __P((MCI *, int, char *, char *)); +extern char *smtptodsn __P((int)); +extern int rscheck __P((char *, char *, char *, ENVELOPE *e)); +extern void mime7to8 __P((MCI *, HDR *, ENVELOPE *)); +extern int mime8to7 __P((MCI *, HDR *, ENVELOPE *, char **, int)); +extern void xfclose __P((FILE *, char *, char *)); +extern int switch_map_find __P((char *, char *[], short [])); +extern void shorten_hostname __P((char [])); +extern int waitfor __P((pid_t)); +extern void proc_list_add __P((pid_t)); +extern void proc_list_drop __P((pid_t)); +extern void proc_list_clear __P((void)); +extern void buffer_errors __P((void)); +extern void flush_errors __P((bool)); +extern void putline __P((char *, MCI *)); +extern bool xtextok __P((char *)); +extern char *xtextify __P((char *, char *)); +extern char *xuntextify __P((char *)); +extern void cleanstrcpy __P((char *, char *, int)); +extern int getmxrr __P((char *, char **, bool, int *)); +extern int strtorwset __P((char *, char **, int)); +extern void printav __P((char **)); +extern void printopenfds __P((bool)); +extern int endmailer __P((MCI *, ENVELOPE *, char **)); +extern void fixcrlf __P((char *, bool)); +extern int dofork __P((void)); +extern void initsys __P((ENVELOPE *)); +extern void collect __P((FILE *, bool, HDR **, ENVELOPE *)); +extern void stripquotes __P((char *)); +extern int include __P((char *, bool, ADDRESS *, ADDRESS **, int, ENVELOPE *)); +extern void unlockqueue __P((ENVELOPE *)); +extern void xunlink __P((char *)); +extern bool runqueue __P((bool, bool)); +extern int getla __P((void)); +extern void sendall __P((ENVELOPE *, int)); +extern void queueup __P((ENVELOPE *, bool)); +extern void checkfds __P((char *)); +extern int returntosender __P((char *, ADDRESS *, int, ENVELOPE *)); +extern void markstats __P((ENVELOPE *, ADDRESS *, bool)); +extern void poststats __P((char *)); +extern char *arpadate __P((char *)); +extern int mailfile __P((char *volatile, MAILER *volatile, ADDRESS *, volatile int, ENVELOPE *)); +extern void loseqfile __P((ENVELOPE *, char *)); +extern int prog_open __P((char **, int *, ENVELOPE *)); +extern bool getcanonname __P((char *, int, bool)); +extern bool path_is_dir __P((char *, bool)); +extern pid_t dowork __P((char *, bool, bool, ENVELOPE *)); +extern int drop_privileges __P((bool)); +extern void fill_fd __P((int, char *)); + +extern const char *errstring __P((int)); +extern sigfunc_t setsignal __P((int, sigfunc_t)); +extern int blocksignal __P((int)); +extern int releasesignal __P((int)); +extern struct hostent *sm_gethostbyname __P((char *)); +extern struct hostent *sm_gethostbyaddr __P((char *, int, int)); +extern struct passwd *sm_getpwnam __P((char *)); +extern struct passwd *sm_getpwuid __P((UID_T)); +extern struct passwd *finduser __P((char *, bool *)); + +#ifdef XDEBUG +extern void checkfdopen __P((int, char *)); +extern void checkfd012 __P((char *)); +#endif + +/* ellipsis is a different case though */ +extern void auth_warning __P((ENVELOPE *, const char *, ...)); +extern void syserr __P((const char *, ...)); +extern void usrerr __P((const char *, ...)); +extern void message __P((const char *, ...)); +extern void nmessage __P((const char *, ...)); +extern void setproctitle __P((const char *, ...)); +extern void sm_syslog __P((int, const char *, const char *, ...)); + +#if !HASSNPRINTF +extern int snprintf __P((char *, size_t, const char *, ...)); +extern int vsnprintf __P((char *, size_t, const char *, va_list)); +#endif +extern char *quad_to_string __P((QUAD_T)); diff --git a/contrib/sendmail/src/sendmail.hf b/contrib/sendmail/src/sendmail.hf new file mode 100644 index 0000000..12a3060 --- /dev/null +++ b/contrib/sendmail/src/sendmail.hf @@ -0,0 +1,119 @@ +cpyr +cpyr Copyright (c) 1998 Sendmail, Inc. 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 @(#)sendmail.hf 8.16 (Berkeley) 5/19/98 +cpyr +smtp Topics: +smtp HELO EHLO MAIL RCPT DATA +smtp RSET NOOP QUIT HELP VRFY +smtp EXPN VERB ETRN DSN +smtp For more info use "HELP ". +smtp To report bugs in the implementation send email to +smtp sendmail-bugs@sendmail.org. +smtp For local information send email to Postmaster at your site. +help HELP [ ] +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 ONEX One message transaction only [Allman] +ehlo CHUNKING Chunking [RFC1830] +ehlo BINARYMIME Binary MIME [RFC1830] +ehlo PIPELINING Command Pipelining [RFC1854] +ehlo DSN Delivery Status Notification [RFC1891] +ehlo ETRN Remote Message Queue Starting [RFC1985] +ehlo XUSR Initial (user) submission [Allman] +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). +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 (same as -bv). +-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 rules addr :run the indicated address through the named rules. +-bt Rules can be a comma separated list of rules. diff --git a/contrib/sendmail/src/snprintf.c b/contrib/sendmail/src/snprintf.c new file mode 100644 index 0000000..9e0b6f1 --- /dev/null +++ b/contrib/sendmail/src/snprintf.c @@ -0,0 +1,428 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)snprintf.c 8.11 (Berkeley) 6/4/98"; +#endif /* not lint */ + +#include "sendmail.h" + + /* +** SNPRINTF, VSNPRINT -- counted versions of printf +** +** These versions have been grabbed off the net. They have been +** cleaned up to compile properly and support for .precision and +** %lx has been added. +*/ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (sm_dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + **************************************************************/ + +/*static char _id[] = "$Id: snprintf.c,v 1.2 1995/10/09 11:19:47 roberto Exp $";*/ +void sm_dopr(); +char *DoprEnd; +int SnprfOverflow; + +#if !HASSNPRINTF + +/* VARARGS3 */ +int +# ifdef __STDC__ +snprintf(char *str, size_t count, const char *fmt, ...) +# else +snprintf(str, count, fmt, va_alist) + char *str; + size_t count; + const char *fmt; + va_dcl +#endif +{ + int len; + VA_LOCAL_DECL + + VA_START(fmt); + len = vsnprintf(str, count, fmt, ap); + VA_END; + return len; +} + + +# ifndef luna2 +int +vsnprintf(str, count, fmt, args) + char *str; + size_t count; + const char *fmt; + va_list args; +{ + str[0] = 0; + DoprEnd = str + count - 1; + SnprfOverflow = 0; + sm_dopr( str, fmt, args ); + if (count > 0) + DoprEnd[0] = 0; + if (SnprfOverflow && tTd(57, 2)) + printf("\nvsnprintf overflow, len = %ld, str = %s", + (long) count, shortenstring(str, MAXSHORTSTR)); + return strlen(str); +} + +# endif /* !luna2 */ +#endif /* !HASSNPRINTF */ + +/* + * sm_dopr(): poor man's version of doprintf + */ + +void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth)); +void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad)); +void dostr __P(( char * , int )); +char *output; +void dopr_outch __P(( int c )); +int SyslogErrno; + +void +sm_dopr( buffer, format, args ) + char *buffer; + const char *format; + va_list args; +{ + int ch; + long value; + int longflag = 0; + int pointflag = 0; + int maxwidth = 0; + char *strvalue; + int ljust; + int len; + int zpad; +# if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) + extern char *sys_errlist[]; + extern int sys_nerr; +# endif + + + output = buffer; + while( (ch = *format++) != '\0' ){ + switch( ch ){ + case '%': + ljust = len = zpad = maxwidth = 0; + longflag = pointflag = 0; + nextch: + ch = *format++; + switch( ch ){ + case 0: + dostr( "**end of format**" , 0); + return; + case '-': ljust = 1; goto nextch; + case '0': /* set zero padding if len not set */ + if(len==0 && !pointflag) zpad = '0'; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + if (pointflag) + maxwidth = maxwidth*10 + ch - '0'; + else + len = len*10 + ch - '0'; + goto nextch; + case '*': + if (pointflag) + maxwidth = va_arg( args, int ); + else + len = va_arg( args, int ); + goto nextch; + case '.': pointflag = 1; goto nextch; + case 'l': longflag = 1; goto nextch; + case 'u': case 'U': + /*fmtnum(value,base,dosign,ljust,len,zpad) */ + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 10,0, ljust, len, zpad ); break; + case 'o': case 'O': + /*fmtnum(value,base,dosign,ljust,len,zpad) */ + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 8,0, ljust, len, zpad ); break; + case 'd': case 'D': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 10,1, ljust, len, zpad ); break; + case 'x': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 16,0, ljust, len, zpad ); break; + case 'X': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value,-16,0, ljust, len, zpad ); break; + case 's': + strvalue = va_arg( args, char *); + if (maxwidth > 0 || !pointflag) { + if (pointflag && len > maxwidth) + len = maxwidth; /* Adjust padding */ + fmtstr( strvalue,ljust,len,zpad, maxwidth); + } + break; + case 'c': + ch = va_arg( args, int ); + dopr_outch( ch ); break; + case 'm': +#if HASSTRERROR + dostr(strerror(SyslogErrno), 0); +#else + if (SyslogErrno < 0 || SyslogErrno >= sys_nerr) + { + dostr("Error ", 0); + fmtnum(SyslogErrno, 10, 0, 0, 0, 0); + } + else + dostr((char *)sys_errlist[SyslogErrno], 0); +#endif + break; + + case '%': dopr_outch( ch ); continue; + default: + dostr( "???????" , 0); + } + break; + default: + dopr_outch( ch ); + break; + } + } + *output = 0; +} + +void +fmtstr( value, ljust, len, zpad, maxwidth ) + char *value; + int ljust, len, zpad, maxwidth; +{ + int padlen, strlen; /* amount to pad */ + + if( value == 0 ){ + value = ""; + } + for( strlen = 0; value[strlen]; ++ strlen ); /* strlen */ + if (strlen > maxwidth && maxwidth) + strlen = maxwidth; + padlen = len - strlen; + if( padlen < 0 ) padlen = 0; + if( ljust ) padlen = -padlen; + while( padlen > 0 ) { + dopr_outch( ' ' ); + --padlen; + } + dostr( value, maxwidth ); + while( padlen < 0 ) { + dopr_outch( ' ' ); + ++padlen; + } +} + +void +fmtnum( value, base, dosign, ljust, len, zpad ) + long value; + int base, dosign, ljust, len, zpad; +{ + int signvalue = 0; + unsigned long uvalue; + char convert[20]; + int place = 0; + int padlen = 0; /* amount to pad */ + int caps = 0; + + /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n", + value, base, dosign, ljust, len, zpad )); */ + uvalue = value; + if( dosign ){ + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } + } + if( base < 0 ){ + caps = 1; + base = -base; + } + do{ + convert[place++] = + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; + uvalue = (uvalue / (unsigned)base ); + }while(uvalue); + convert[place] = 0; + padlen = len - place; + if( padlen < 0 ) padlen = 0; + if( ljust ) padlen = -padlen; + /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n", + convert,place,signvalue,padlen)); */ + if( zpad && padlen > 0 ){ + if( signvalue ){ + dopr_outch( signvalue ); + --padlen; + signvalue = 0; + } + while( padlen > 0 ){ + dopr_outch( zpad ); + --padlen; + } + } + while( padlen > 0 ) { + dopr_outch( ' ' ); + --padlen; + } + if( signvalue ) dopr_outch( signvalue ); + while( place > 0 ) dopr_outch( convert[--place] ); + while( padlen < 0 ){ + dopr_outch( ' ' ); + ++padlen; + } +} + +void +dostr( str , cut) + char *str; + int cut; +{ + if (cut) { + while(*str && cut-- > 0) dopr_outch(*str++); + } else { + while(*str) dopr_outch(*str++); + } +} + +void +dopr_outch( c ) + int c; +{ +#if 0 + if( iscntrl(c) && c != '\n' && c != '\t' ){ + c = '@' + (c & 0x1F); + if( DoprEnd == 0 || output < DoprEnd ) + *output++ = '^'; + } +#endif + if( DoprEnd == 0 || output < DoprEnd ) + *output++ = c; + else + SnprfOverflow++; +} + + /* +** QUAD_TO_STRING -- Convert a quad type to a string. +** +** Convert a quad type to a string. This must be done +** separately as %lld/%qd are not supported by snprint() +** and adding support would slow down systems which only +** emulate the data type. +** +** Parameters: +** value -- number to convert to a string. +** +** Returns: +** pointer to a string. +*/ + +char * +quad_to_string(value) + QUAD_T value; +{ + char *fmtstr; + static char buf[64]; + + /* + ** Use sprintf() instead of snprintf() since snprintf() + ** does not support %qu or %llu. The buffer is large enough + ** to hold the string so there is no danger of buffer + ** overflow. + */ + +#if NEED_PERCENTQ + fmtstr = "%qu"; +#else + fmtstr = "%llu"; +#endif + sprintf(buf, fmtstr, value); + return buf; +} + /* +** SHORTENSTRING -- return short version of a string +** +** If the string is already short, just return it. If it is too +** long, return the head and tail of the string. +** +** Parameters: +** s -- the string to shorten. +** m -- the max length of the string. +** +** Returns: +** Either s or a short version of s. +*/ + +char * +shortenstring(s, m) + register const char *s; + int m; +{ + int l; + static char buf[MAXSHORTSTR + 1]; + + l = strlen(s); + if (l < m) + return (char *) s; + if (m > MAXSHORTSTR) + m = MAXSHORTSTR; + else if (m < 10) + { + if (m < 5) + { + strncpy(buf, s, m); + buf[m] = '\0'; + return buf; + } + strncpy(buf, s, m - 3); + strcpy(buf + m - 3, "..."); + return buf; + } + m = (m - 3) / 2; + strncpy(buf, s, m); + strcpy(buf + m, "..."); + strcpy(buf + m + 3, s + l - m); + return buf; +} diff --git a/contrib/sendmail/src/srvrsmtp.c b/contrib/sendmail/src/srvrsmtp.c new file mode 100644 index 0000000..fba103c --- /dev/null +++ b/contrib/sendmail/src/srvrsmtp.c @@ -0,0 +1,1532 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +# include "sendmail.h" + +#ifndef lint +#if SMTP +static char sccsid[] = "@(#)srvrsmtp.c 8.181 (Berkeley) 6/15/98 (with SMTP)"; +#else +static char sccsid[] = "@(#)srvrsmtp.c 8.181 (Berkeley) 6/15/98 (without SMTP)"; +#endif +#endif /* not lint */ + +# include + +# if SMTP + +/* +** SMTP -- run the SMTP protocol. +** +** Parameters: +** nullserver -- if non-NULL, rejection message for +** all SMTP commands. +** e -- the envelope. +** +** Returns: +** never. +** +** Side Effects: +** Reads commands from the input channel and processes +** them. +*/ + +struct cmd +{ + char *cmdname; /* command name */ + int cmdcode; /* internal code, see below */ +}; + +/* values for cmdcode */ +# 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 */ +/* non-standard commands */ +# define CMDONEX 16 /* onex -- sending one transaction only */ +# define CMDVERB 17 /* verb -- go into verbose mode */ +# define CMDXUSR 18 /* xusr -- initial (user) submission */ +/* 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 */ + +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 }, + { "onex", CMDONEX }, + { "xusr", CMDXUSR }, + /* remaining commands are here only to trap and log attempts to use them */ + { "showq", CMDDBGQSHOW }, + { "debug", CMDDBGDEBUG }, + { "wiz", CMDLOGBOGUS }, + + { NULL, CMDERROR } +}; + +bool OneXact = FALSE; /* one xaction only this run */ +char *CurSmtpClient; /* who's at the other end of channel */ + +static char *skipword __P((char *volatile, char *)); + + +#define MAXBADCOMMANDS 25 /* maximum number of bad commands */ +#define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */ +#define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */ +#define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */ +#define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */ + +void +smtp(nullserver, e) + char *nullserver; + register ENVELOPE *volatile e; +{ + register char *volatile p; + register struct cmd *c; + char *cmd; + auto ADDRESS *vrfyqueue; + ADDRESS *a; + volatile bool gotmail; /* mail command received */ + volatile bool gothello; /* helo command received */ + bool vrfy; /* set if this is a vrfy command */ + char *volatile protocol; /* sending protocol */ + char *volatile sendinghost; /* sending hostname */ + char *volatile peerhostname; /* name of SMTP peer or "localhost" */ + auto char *delimptr; + char *id; + volatile int nrcpts = 0; /* number of RCPT commands */ + bool doublequeue; + bool discard; + volatile int badcommands = 0; /* count of bad commands */ + volatile int nverifies = 0; /* count of VRFY/EXPN commands */ + volatile int n_etrn = 0; /* count of ETRN commands */ + volatile int n_noop = 0; /* count of NOOP/VERB/ONEX etc cmds */ + volatile int n_helo = 0; /* count of HELO/EHLO commands */ + bool ok; + volatile int lognullconnection = TRUE; + register char *q; + QUEUE_CHAR *new; + char inp[MAXLINE]; + char cmdbuf[MAXLINE]; + extern ENVELOPE BlankEnvelope; + extern void help __P((char *)); + extern void settime __P((ENVELOPE *)); + extern bool enoughdiskspace __P((long)); + extern int runinchild __P((char *, ENVELOPE *)); + extern void checksmtpattack __P((volatile int *, int, char *, ENVELOPE *)); + + if (fileno(OutChannel) != fileno(stdout)) + { + /* arrange for debugging output to go to remote host */ + (void) dup2(fileno(OutChannel), fileno(stdout)); + } + settime(e); + 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 */ + discard = bitset(EF_DISCARD, e->e_flags); + + setproctitle("server %s startup", CurSmtpClient); +#if DAEMON + if (LogLevel > 11) + { + /* log connection information */ + sm_syslog(LOG_INFO, NOQID, + "SMTP connect from %.100s (%.100s)", + CurSmtpClient, anynet_ntoa(&RealHostAddr)); + } +#endif + + /* output the first line, inserting "ESMTP" as second word */ + 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)]; + cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s"; + message(cmd, id - inp, inp, id); + + /* output remaining lines */ + while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) + { + *p++ = '\0'; + if (isascii(*id) && isspace(*id)) + id++; + message("220-%s", id); + } + if (id != NULL) + { + if (isascii(*id) && isspace(*id)) + id++; + message("220 %s", id); + } + + protocol = NULL; + sendinghost = macvalue('s', e); + gothello = FALSE; + gotmail = FALSE; + for (;;) + { + /* arrange for backout */ + (void) setjmp(TopFrame); + QuickAbort = FALSE; + HoldErrs = FALSE; + SuprErrs = FALSE; + LogUsrErrs = FALSE; + OnlyOneError = TRUE; + e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); + + /* setup for the read */ + e->e_to = NULL; + Errors = 0; + (void) fflush(stdout); + + /* read the input line */ + SmtpPhase = "server cmd read"; + setproctitle("server %s cmd read", CurSmtpClient); + p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, + SmtpPhase); + + /* handle errors */ + if (p == NULL) + { + /* end of file, just die */ + disconnect(1, e); + message("421 %s Lost input channel from %s", + MyHostName, CurSmtpClient); + if (LogLevel > (gotmail ? 1 : 19)) + sm_syslog(LOG_NOTICE, e->e_id, + "lost input channel from %.100s", + CurSmtpClient); + if (lognullconnection && LogLevel > 5) + sm_syslog(LOG_INFO, NULL, + "Null connection from %.100s", + CurSmtpClient); + + /* + ** If have not accepted mail (DATA), do not bounce + ** bad addresses back to sender. + */ + if (bitset(EF_CLRQUEUE, e->e_flags)) + e->e_sendqueue = NULL; + + if (InChild) + ExitStat = EX_QUIT; + finis(); + } + + /* clean up end of line */ + fixcrlf(inp, TRUE); + + /* echo command to transcript */ + if (e->e_xfp != NULL) + fprintf(e->e_xfp, "<<< %s\n", inp); + + if (LogLevel >= 15) + sm_syslog(LOG_INFO, e->e_id, + "<-- %s", + inp); + + if (e->e_id == NULL) + setproctitle("%s: %.80s", CurSmtpClient, inp); + else + setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, 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->cmdname != NULL; c++) + { + if (!strcasecmp(c->cmdname, cmdbuf)) + break; + } + + /* reset errors */ + errno = 0; + + /* + ** Process command. + ** + ** If we are running as a null server, return 550 + ** to everything. + */ + + if (nullserver != NULL) + { + switch (c->cmdcode) + { + case CMDQUIT: + case CMDHELO: + case CMDEHLO: + case CMDNOOP: + /* process normally */ + break; + + default: + if (++badcommands > MAXBADCOMMANDS) + sleep(1); + usrerr("550 %s", nullserver); + continue; + } + } + + /* non-null server */ + switch (c->cmdcode) + { + case CMDMAIL: + case CMDEXPN: + case CMDVRFY: + case CMDETRN: + lognullconnection = FALSE; + } + + switch (c->cmdcode) + { + case CMDHELO: /* hello -- introduce yourself */ + case CMDEHLO: /* extended hello */ + if (c->cmdcode == CMDEHLO) + { + protocol = "ESMTP"; + SmtpPhase = "server EHLO"; + } + else + { + protocol = "SMTP"; + SmtpPhase = "server HELO"; + } + + /* avoid denial-of-service */ + checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO", e); + + /* check for duplicate HELO/EHLO per RFC 1651 4.2 */ + if (gothello) + { + usrerr("503 %s Duplicate HELO/EHLO", + MyHostName); + break; + } + + /* 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"); + break; + } + + for (q = p; *q != '\0'; q++) + { + if (!isascii(*q)) + break; + if (isalnum(*q)) + continue; + if (isspace(*q)) + { + *q = '\0'; + break; + } + if (strchr("[].-_#", *q) == NULL) + break; + } + if (*q == '\0') + { + q = "pleased to meet you"; + sendinghost = newstr(p); + } + else if (!AllowBogusHELO) + { + usrerr("501 Invalid domain name"); + break; + } + else + { + q = "accepting invalid domain name"; + } + + gothello = TRUE; + + /* print HELO response message */ + if (c->cmdcode != CMDEHLO || nullserver != NULL) + { + message("250 %s Hello %s, %s", + MyHostName, CurSmtpClient, q); + break; + } + + message("250-%s Hello %s, %s", + MyHostName, CurSmtpClient, q); + + /* print EHLO features list */ + if (!bitset(PRIV_NOEXPN, PrivacyFlags)) + { + message("250-EXPN"); + if (!bitset(PRIV_NOVERB, PrivacyFlags)) + message("250-VERB"); + } +#if MIME8TO7 + message("250-8BITMIME"); +#endif + if (MaxMessageSize > 0) + message("250-SIZE %ld", MaxMessageSize); + else + message("250-SIZE"); +#if DSN + if (SendMIMEErrors) + message("250-DSN"); +#endif + message("250-ONEX"); + if (!bitset(PRIV_NOETRN, PrivacyFlags)) + message("250-ETRN"); + message("250-XUSR"); + message("250 HELP"); + break; + + case CMDMAIL: /* mail -- designate sender */ + SmtpPhase = "server MAIL"; + + /* check for validity of this command */ + if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) + { + usrerr("503 Polite people say HELO first"); + break; + } + if (gotmail) + { + usrerr("503 Sender already specified"); + break; + } + if (InChild) + { + errno = 0; + syserr("503 Nested MAIL command: MAIL %s", p); + finis(); + } + + /* make sure we know who the sending host is */ + if (sendinghost == NULL) + sendinghost = peerhostname; + + p = skipword(p, "from"); + if (p == NULL) + break; + + /* fork a subprocess to process this command */ + if (runinchild("SMTP-MAIL", e) > 0) + break; + if (Errors > 0) + goto undo_subproc_no_pm; + if (!gothello) + { + auth_warning(e, + "%s didn't use HELO protocol", + CurSmtpClient); + } +#ifdef PICKY_HELO_CHECK + if (strcasecmp(sendinghost, peerhostname) != 0 && + (strcasecmp(peerhostname, "localhost") != 0 || + strcasecmp(sendinghost, MyHostName) != 0)) + { + auth_warning(e, "Host %s claimed to be %s", + CurSmtpClient, sendinghost); + } +#endif + + if (protocol == NULL) + protocol = "SMTP"; + define('r', protocol, e); + define('s', sendinghost, e); + initsys(e); + if (Errors > 0) + goto undo_subproc_no_pm; + nrcpts = 0; + e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; + setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); + + /* child -- go do the processing */ + if (setjmp(TopFrame) > 0) + { + /* this failed -- undo work */ + undo_subproc_no_pm: + e->e_flags &= ~EF_PM_NOTIFY; + undo_subproc: + if (InChild) + { + QuickAbort = FALSE; + SuprErrs = TRUE; + e->e_flags &= ~EF_FATALERRS; + finis(); + } + break; + } + QuickAbort = TRUE; + + /* must parse sender first */ + delimptr = NULL; + setsender(p, e, &delimptr, ' ', FALSE); + if (delimptr != NULL && *delimptr != '\0') + *delimptr++ = '\0'; + if (Errors > 0) + goto undo_subproc_no_pm; + + /* do config file checking of the sender */ + if (rscheck("check_mail", p, NULL, e) != EX_OK || + Errors > 0) + goto undo_subproc_no_pm; + + /* 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); + } + + /* now parse ESMTP arguments */ + e->e_msgsize = 0; + p = delimptr; + while (p != NULL && *p != '\0') + { + char *kp; + char *vp = NULL; + extern void mail_esmtp_args __P((char *, char *, ENVELOPE *)); + + /* locate the beginning of the keyword */ + while (isascii(*p) && isspace(*p)) + p++; + if (*p == '\0') + break; + kp = p; + + /* skip to the value portion */ + while ((isascii(*p) && isalnum(*p)) || *p == '-') + p++; + if (*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)) + printf("MAIL: got arg %s=\"%s\"\n", kp, + vp == NULL ? "" : vp); + + mail_esmtp_args(kp, vp, e); + if (Errors > 0) + goto undo_subproc_no_pm; + } + if (Errors > 0) + goto undo_subproc_no_pm; + + if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) + { + usrerr("552 Message size exceeds fixed maximum message size (%ld)", + MaxMessageSize); + goto undo_subproc_no_pm; + } + + if (!enoughdiskspace(e->e_msgsize)) + { + usrerr("452 Insufficient disk space; try again later"); + goto undo_subproc_no_pm; + } + if (Errors > 0) + goto undo_subproc_no_pm; + message("250 Sender ok"); + gotmail = TRUE; + break; + + case CMDRCPT: /* rcpt -- designate recipient */ + if (!gotmail) + { + usrerr("503 Need MAIL before RCPT"); + break; + } + SmtpPhase = "server RCPT"; + if (setjmp(TopFrame) > 0) + { + e->e_flags &= ~EF_FATALERRS; + break; + } + QuickAbort = TRUE; + LogUsrErrs = TRUE; + + /* limit flooding of our machine */ + if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg) + { + usrerr("452 Too many recipients"); + break; + } + + if (e->e_sendmode != SM_DELIVER) + e->e_flags |= EF_VRFYONLY; + + p = skipword(p, "to"); + if (p == NULL) + break; + a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); + if (a == NULL || Errors > 0) + break; + if (delimptr != NULL && *delimptr != '\0') + *delimptr++ = '\0'; + + /* do config file checking of the recipient */ + if (rscheck("check_rcpt", p, NULL, e) != EX_OK || + Errors > 0) + break; + + /* now parse ESMTP arguments */ + p = delimptr; + while (p != NULL && *p != '\0') + { + char *kp; + char *vp = NULL; + extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); + + /* locate the beginning of the keyword */ + while (isascii(*p) && isspace(*p)) + p++; + if (*p == '\0') + break; + kp = p; + + /* skip to the value portion */ + while ((isascii(*p) && isalnum(*p)) || *p == '-') + p++; + if (*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)) + printf("RCPT: got arg %s=\"%s\"\n", kp, + vp == NULL ? "" : vp); + + rcpt_esmtp_args(a, kp, vp, e); + if (Errors > 0) + break; + } + if (Errors > 0) + break; + + /* save in recipient list after ESMTP mods */ + a = recipient(a, &e->e_sendqueue, 0, e); + if (Errors > 0) + break; + + /* no errors during parsing, but might be a duplicate */ + e->e_to = a->q_paddr; + if (!bitset(QBADADDR, a->q_flags)) + { + message("250 Recipient ok%s", + bitset(QQUEUEUP, a->q_flags) ? + " (will queue)" : ""); + nrcpts++; + } + else + { + /* punt -- should keep message in ADDRESS.... */ + usrerr("550 Addressee unknown"); + } + break; + + case CMDDATA: /* data -- text of mail */ + SmtpPhase = "server DATA"; + if (!gotmail) + { + usrerr("503 Need MAIL command"); + break; + } + else if (nrcpts <= 0) + { + usrerr("503 Need RCPT (recipient)"); + break; + } + + /* put back discard bit */ + if (discard) + e->e_flags |= EF_DISCARD; + + /* check to see if we need to re-expand aliases */ + /* also reset QBADADDR on already-diagnosted addrs */ + doublequeue = FALSE; + for (a = e->e_sendqueue; a != NULL; a = a->q_next) + { + if (bitset(QVERIFIED, a->q_flags) && + !bitset(EF_DISCARD, e->e_flags)) + { + /* need to re-expand aliases */ + doublequeue = TRUE; + } + if (bitset(QBADADDR, a->q_flags)) + { + /* make this "go away" */ + a->q_flags |= QDONTSEND; + a->q_flags &= ~QBADADDR; + } + } + + /* collect the text of the message */ + SmtpPhase = "collect"; + buffer_errors(); + collect(InChannel, TRUE, NULL, e); + if (Errors > 0) + { + flush_errors(TRUE); + buffer_errors(); + goto abortmessage; + } + + /* make sure we actually do delivery */ + e->e_flags &= ~EF_CLRQUEUE; + + /* from now on, we have to operate silently */ + buffer_errors(); + e->e_errormode = EM_MAIL; + + /* + ** Arrange to send to everyone. + ** If sending to multiple people, mail back + ** errors rather than reporting directly. + ** In any case, don't mail back errors for + ** anything that has happened up to + ** now (the other end will do this). + ** Truncate our transcript -- the mail has gotten + ** to us successfully, and if we have + ** to mail this back, it will be easier + ** on the reader. + ** Then send to everyone. + ** Finally give a reply code. If an error has + ** already been given, don't mail a + ** message back. + ** We goose error returns by clearing error bit. + */ + + SmtpPhase = "delivery"; + e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); + id = e->e_id; + + if (doublequeue) + { + /* make sure it is in the queue */ + queueup(e, FALSE); + } + else + { + /* send to all recipients */ + sendall(e, SM_DEFAULT); + } + e->e_to = NULL; + + /* issue success message */ + message("250 %s Message accepted for delivery", id); + + /* if we just queued, poke it */ + if (doublequeue && + e->e_sendmode != SM_QUEUE && + e->e_sendmode != SM_DEFER) + { + CurrentLA = getla(); + + if (!shouldqueue(e->e_msgpriority, e->e_ctime)) + { + extern pid_t dowork __P((char *, bool, bool, ENVELOPE *)); + + unlockqueue(e); + (void) dowork(id, TRUE, TRUE, e); + } + } + + abortmessage: + /* if in a child, pop back to our parent */ + if (InChild) + finis(); + + /* clean up a bit */ + gotmail = FALSE; + dropenvelope(e, TRUE); + CurEnv = e = newenvelope(e, CurEnv); + e->e_flags = BlankEnvelope.e_flags; + break; + + case CMDRSET: /* rset -- reset state */ + if (tTd(94, 100)) + message("451 Test failure"); + else + message("250 Reset state"); + + /* arrange to ignore any current send list */ + e->e_sendqueue = NULL; + e->e_flags |= EF_CLRQUEUE; + if (InChild) + finis(); + + /* clean up a bit */ + gotmail = FALSE; + SuprErrs = TRUE; + dropenvelope(e, TRUE); + CurEnv = e = newenvelope(e, CurEnv); + break; + + case CMDVRFY: /* vrfy -- verify address */ + case CMDEXPN: /* expn -- expand address */ + checksmtpattack(&nverifies, MAXVRFYCOMMANDS, + c->cmdcode == CMDVRFY ? "VRFY" : "EXPN", e); + vrfy = c->cmdcode == CMDVRFY; + if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, + PrivacyFlags)) + { + if (vrfy) + message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); + else + message("502 Sorry, we do not allow this operation"); + if (LogLevel > 5) + sm_syslog(LOG_INFO, e->e_id, + "%.100s: %s [rejected]", + CurSmtpClient, + shortenstring(inp, MAXSHORTSTR)); + break; + } + else if (!gothello && + bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, + PrivacyFlags)) + { + usrerr("503 I demand that you introduce yourself first"); + break; + } + if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) + break; + if (Errors > 0) + goto undo_subproc; + if (LogLevel > 5) + sm_syslog(LOG_INFO, e->e_id, + "%.100s: %s", + CurSmtpClient, + shortenstring(inp, MAXSHORTSTR)); + if (setjmp(TopFrame) > 0) + goto undo_subproc; + QuickAbort = TRUE; + vrfyqueue = NULL; + if (vrfy) + e->e_flags |= EF_VRFYONLY; + while (*p != '\0' && isascii(*p) && isspace(*p)) + p++; + if (*p == '\0') + { + usrerr("501 Argument required"); + } + else + { + (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); + } + if (Errors > 0) + goto undo_subproc; + if (vrfyqueue == NULL) + { + usrerr("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); + } + while (vrfyqueue != NULL) + { + extern void printvrfyaddr __P((ADDRESS *, bool, bool)); + + a = vrfyqueue; + while ((a = a->q_next) != NULL && + bitset(QDONTSEND|QBADADDR, a->q_flags)) + continue; + if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) + printvrfyaddr(vrfyqueue, a == NULL, vrfy); + vrfyqueue = vrfyqueue->q_next; + } + if (InChild) + finis(); + break; + + case CMDETRN: /* etrn -- force queue flush */ + if (bitset(PRIV_NOETRN, PrivacyFlags)) + { + message("502 Sorry, we do not allow this operation"); + if (LogLevel > 5) + sm_syslog(LOG_INFO, e->e_id, + "%.100s: %s [rejected]", + CurSmtpClient, + shortenstring(inp, MAXSHORTSTR)); + break; + } + + if (strlen(p) <= 0) + { + usrerr("500 Parameter required"); + break; + } + + /* crude way to avoid denial-of-service attacks */ + checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e); + + if (LogLevel > 5) + sm_syslog(LOG_INFO, e->e_id, + "%.100s: ETRN %s", + CurSmtpClient, + shortenstring(p, MAXSHORTSTR)); + + id = p; + if (*id == '@') + id++; + else + *--id = '@'; + + if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL) + { + syserr("500 ETRN out of memory"); + break; + } + new->queue_match = id; + new->queue_next = NULL; + QueueLimitRecipient = new; + ok = runqueue(TRUE, TRUE); + free(QueueLimitRecipient); + QueueLimitRecipient = NULL; + if (ok && Errors == 0) + message("250 Queuing for node %s started", p); + break; + + case CMDHELP: /* help -- give user info */ + help(p); + break; + + case CMDNOOP: /* noop -- do nothing */ + checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP", e); + message("250 OK"); + break; + + case CMDQUIT: /* quit -- leave mail */ + message("221 %s closing connection", MyHostName); + +doquit: + /* arrange to ignore any current send list */ + e->e_sendqueue = NULL; + + /* avoid future 050 messages */ + disconnect(1, e); + + if (InChild) + ExitStat = EX_QUIT; + if (lognullconnection && LogLevel > 5) + sm_syslog(LOG_INFO, NULL, + "Null connection from %.100s", + CurSmtpClient); + finis(); + + case CMDVERB: /* set verbose mode */ + if (bitset(PRIV_NOEXPN, PrivacyFlags) || + bitset(PRIV_NOVERB, PrivacyFlags)) + { + /* this would give out the same info */ + message("502 Verbose unavailable"); + break; + } + checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB", e); + Verbose = 1; + e->e_sendmode = SM_DELIVER; + message("250 Verbose mode"); + break; + + case CMDONEX: /* doing one transaction only */ + checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e); + OneXact = TRUE; + message("250 Only one transaction"); + break; + + case CMDXUSR: /* initial (user) submission */ + checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR", e); + UserSubmission = TRUE; + message("250 Initial submission"); + break; + +# if SMTPDEBUG + case CMDDBGQSHOW: /* show queues */ + printf("Send Queue="); + printaddr(e->e_sendqueue, TRUE); + break; + + case CMDDBGDEBUG: /* set debug mode */ + tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); + tTflag(p); + message("200 Debug set"); + break; + +# else /* not SMTPDEBUG */ + case CMDDBGQSHOW: /* show queues */ + case CMDDBGDEBUG: /* set debug mode */ +# endif /* SMTPDEBUG */ + case CMDLOGBOGUS: /* bogus command */ + if (LogLevel > 0) + sm_syslog(LOG_CRIT, e->e_id, + "\"%s\" command from %.100s (%.100s)", + c->cmdname, CurSmtpClient, + anynet_ntoa(&RealHostAddr)); + /* FALL THROUGH */ + + case CMDERROR: /* unknown command */ + if (++badcommands > MAXBADCOMMANDS) + { + message("421 %s Too many bad commands; closing connection", + MyHostName); + goto doquit; + } + + usrerr("500 Command unrecognized: \"%s\"", + shortenstring(inp, MAXSHORTSTR)); + break; + + default: + errno = 0; + syserr("500 smtp: unknown code %d", c->cmdcode); + break; + } + } +} + /* +** 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. +** cname -- command name for logging. +** e -- the current envelope. +** +** Returns: +** none. +** +** Side Effects: +** Slows down if we seem to be under attack. +*/ + +void +checksmtpattack(pcounter, maxcount, cname, e) + volatile int *pcounter; + int maxcount; + char *cname; + ENVELOPE *e; +{ + if (++(*pcounter) >= maxcount) + { + if (*pcounter == maxcount && LogLevel > 5) + { + sm_syslog(LOG_INFO, e->e_id, + "%.100s: %.40s attack?", + CurSmtpClient, cname); + } + sleep(*pcounter / maxcount); + } +} + /* +** 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 */ + while (isascii(*p) && isspace(*p)) + 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 Syntax error in parameters scanning \"%s\"", + shortenstring(firstp, MAXSHORTSTR)); + return (NULL); + } + *p++ = '\0'; + while (isascii(*p) && isspace(*p)) + p++; + + if (*p == '\0') + goto syntax; + + /* see if the input word matches desired word */ + if (strcasecmp(q, w)) + goto syntax; + + return (p); +} + /* +** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line +** +** Parameters: +** kp -- the parameter key. +** vp -- the value of that parameter. +** e -- the envelope. +** +** Returns: +** none. +*/ + +void +mail_esmtp_args(kp, vp, e) + char *kp; + char *vp; + ENVELOPE *e; +{ + if (strcasecmp(kp, "size") == 0) + { + if (vp == NULL) + { + usrerr("501 SIZE requires a value"); + /* NOTREACHED */ + } +# if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) + e->e_msgsize = strtoul(vp, (char **) NULL, 10); +# else + e->e_msgsize = strtol(vp, (char **) NULL, 10); +# endif + } + else if (strcasecmp(kp, "body") == 0) + { + if (vp == NULL) + { + usrerr("501 BODY requires a value"); + /* NOTREACHED */ + } + else if (strcasecmp(vp, "8bitmime") == 0) + { + SevenBitInput = FALSE; + } + else if (strcasecmp(vp, "7bit") == 0) + { + SevenBitInput = TRUE; + } + else + { + usrerr("501 Unknown BODY type %s", + vp); + /* NOTREACHED */ + } + e->e_bodytype = newstr(vp); + } + else if (strcasecmp(kp, "envid") == 0) + { + if (vp == NULL) + { + usrerr("501 ENVID requires a value"); + /* NOTREACHED */ + } + if (!xtextok(vp)) + { + usrerr("501 Syntax error in ENVID parameter value"); + /* NOTREACHED */ + } + if (e->e_envid != NULL) + { + usrerr("501 Duplicate ENVID parameter"); + /* NOTREACHED */ + } + e->e_envid = newstr(vp); + } + else if (strcasecmp(kp, "ret") == 0) + { + if (vp == NULL) + { + usrerr("501 RET requires a value"); + /* NOTREACHED */ + } + if (bitset(EF_RET_PARAM, e->e_flags)) + { + usrerr("501 Duplicate RET parameter"); + /* NOTREACHED */ + } + e->e_flags |= EF_RET_PARAM; + if (strcasecmp(vp, "hdrs") == 0) + e->e_flags |= EF_NO_BODY_RETN; + else if (strcasecmp(vp, "full") != 0) + { + usrerr("501 Bad argument \"%s\" to RET", vp); + /* NOTREACHED */ + } + } + else + { + usrerr("501 %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 (strcasecmp(kp, "notify") == 0) + { + char *p; + + if (vp == NULL) + { + usrerr("501 NOTIFY requires a value"); + /* NOTREACHED */ + } + a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); + a->q_flags |= QHASNOTIFY; + if (strcasecmp(vp, "never") == 0) + return; + for (p = vp; p != NULL; vp = p) + { + p = strchr(p, ','); + if (p != NULL) + *p++ = '\0'; + if (strcasecmp(vp, "success") == 0) + a->q_flags |= QPINGONSUCCESS; + else if (strcasecmp(vp, "failure") == 0) + a->q_flags |= QPINGONFAILURE; + else if (strcasecmp(vp, "delay") == 0) + a->q_flags |= QPINGONDELAY; + else + { + usrerr("501 Bad argument \"%s\" to NOTIFY", + vp); + /* NOTREACHED */ + } + } + } + else if (strcasecmp(kp, "orcpt") == 0) + { + if (vp == NULL) + { + usrerr("501 ORCPT requires a value"); + /* NOTREACHED */ + } + if (strchr(vp, ';') == NULL || !xtextok(vp)) + { + usrerr("501 Syntax error in ORCPT parameter value"); + /* NOTREACHED */ + } + if (a->q_orcpt != NULL) + { + usrerr("501 Duplicate ORCPT parameter"); + /* NOTREACHED */ + } + a->q_orcpt = newstr(vp); + } + else + { + usrerr("501 %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. +*/ + +void +printvrfyaddr(a, last, vrfy) + register ADDRESS *a; + bool last; + bool vrfy; +{ + char fmtbuf[20]; + + if (vrfy && a->q_mailer != NULL && + !bitnset(M_VRFY250, a->q_mailer->m_flags)) + strcpy(fmtbuf, "252"); + else + strcpy(fmtbuf, "250"); + fmtbuf[3] = last ? ' ' : '-'; + + if (a->q_fullname == NULL) + { + if (strchr(a->q_user, '@') == NULL) + strcpy(&fmtbuf[4], "<%s@%s>"); + else + strcpy(&fmtbuf[4], "<%s>"); + message(fmtbuf, a->q_user, MyHostName); + } + else + { + if (strchr(a->q_user, '@') == NULL) + strcpy(&fmtbuf[4], "%s <%s@%s>"); + else + strcpy(&fmtbuf[4], "%s <%s>"); + message(fmtbuf, a->q_fullname, a->q_user, MyHostName); + } +} + /* +** RUNINCHILD -- return twice -- once in the child, then in the parent again +** +** Parameters: +** label -- a string used in error messages +** +** Returns: +** zero in the child +** one in the parent +** +** Side Effects: +** none. +*/ + +int +runinchild(label, e) + char *label; + register ENVELOPE *e; +{ + pid_t childpid; + + if (!OneXact) + { + /* + ** Disable child process reaping, in case ETRN has preceeded + ** MAIL command, and then fork. + */ + + (void) blocksignal(SIGCHLD); + + childpid = dofork(); + if (childpid < 0) + { + syserr("451 %s: cannot fork", label); + (void) releasesignal(SIGCHLD); + return (1); + } + if (childpid > 0) + { + auto int st; + + /* parent -- wait for child to complete */ + setproctitle("server %s child wait", CurSmtpClient); + st = waitfor(childpid); + if (st == -1) + syserr("451 %s: lost child", label); + else if (!WIFEXITED(st)) + syserr("451 %s: died on signal %d", + label, st & 0177); + + /* if we exited on a QUIT command, complete the process */ + if (WEXITSTATUS(st) == EX_QUIT) + { + disconnect(1, e); + finis(); + } + + /* restore the child signal */ + (void) releasesignal(SIGCHLD); + + return (1); + } + else + { + /* child */ + InChild = TRUE; + QuickAbort = FALSE; + clearenvelope(e, FALSE); + (void) setsignal(SIGCHLD, SIG_DFL); + (void) releasesignal(SIGCHLD); + } + } + + /* open alias database */ + initmaps(FALSE, e); + + return (0); +} + +# endif /* SMTP */ + /* +** HELP -- implement the HELP command. +** +** Parameters: +** topic -- the topic we want help for. +** +** Returns: +** none. +** +** Side Effects: +** outputs the help file to message output. +*/ + +void +help(topic) + char *topic; +{ + register FILE *hf; + int len; + bool noinfo; + int sff = SFF_OPENASROOT|SFF_REGONLY; + char buf[MAXLINE]; + extern char Version[]; + + if (DontLockReadFiles) + sff |= SFF_NOLOCK; + if (!bitset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail)) + sff |= SFF_SAFEDIRPATH; + + if (HelpFile == NULL || + (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL) + { + /* no help */ + errno = 0; + message("502 Sendmail %s -- HELP not implemented", Version); + return; + } + + if (topic == NULL || *topic == '\0') + { + topic = "smtp"; + message("214-This is Sendmail version %s", Version); + noinfo = FALSE; + } + else + { + makelower(topic); + noinfo = TRUE; + } + + len = strlen(topic); + + while (fgets(buf, sizeof buf, hf) != NULL) + { + if (strncmp(buf, topic, len) == 0) + { + register char *p; + + p = strchr(buf, '\t'); + if (p == NULL) + p = buf; + else + p++; + fixcrlf(p, TRUE); + message("214-%s", p); + noinfo = FALSE; + } + } + + if (noinfo) + message("504 HELP topic \"%.10s\" unknown", topic); + else + message("214 End of HELP info"); + (void) fclose(hf); +} diff --git a/contrib/sendmail/src/stab.c b/contrib/sendmail/src/stab.c new file mode 100644 index 0000000..1ffc7cb --- /dev/null +++ b/contrib/sendmail/src/stab.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)stab.c 8.19 (Berkeley) 5/19/98"; +#endif /* not lint */ + +# include "sendmail.h" + +/* +** 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 + +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; + extern char lower __P((char)); + + if (tTd(36, 5)) + printf("STAB: %s %d ", name, type); + + /* + ** Compute the hashing function + */ + + hfunc = type; + for (p = name; *p != '\0'; p++) + hfunc = ((hfunc << 1) ^ (lower(*p) & 0377)) % STABSIZE; + + if (tTd(36, 9)) + printf("(hfunc=%d) ", hfunc); + + ps = &SymTab[hfunc]; + if (type == ST_MACRO || type == ST_RULESET) + { + while ((s = *ps) != NULL && + (s->s_type != type || strcmp(name, s->s_name))) + ps = &s->s_next; + } + else + { + while ((s = *ps) != NULL && + (s->s_type != type || 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) + printf("not found\n"); + else + { + long *lp = (long *) s->s_class; + + printf("type %d val %lx %lx %lx %lx\n", + s->s_type, lp[0], lp[1], lp[2], lp[3]); + } + } + return (s); + } + + /* + ** Make a new entry and link it in. + */ + + if (tTd(36, 5)) + printf("entered\n"); + + /* determine size of new entry */ +#if _FFR_MEMORY_MISER + 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; + + 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_SERVICE: + len = sizeof s->s_service; + break; + + case ST_HEADER: + len = sizeof s->s_header; + break; + + default: + 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; +#else + len = sizeof *s; +#endif + + /* make new entry */ + s = (STAB *) xalloc(len); + bzero((char *) s, len); + s->s_name = newstr(name); + s->s_type = type; + s->s_len = len; + + /* link it in */ + *ps = s; + + return (s); +} + /* +** STABAPPLY -- apply function to all stab entries +** +** Parameters: +** func -- the function to apply. It will be given one +** parameter (the stab entry). +** 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)) + printf("stabapply: trying %d/%s\n", + s->s_type, s->s_name); + func(s, arg); + } + } +} diff --git a/contrib/sendmail/src/stats.c b/contrib/sendmail/src/stats.c new file mode 100644 index 0000000..767a55e --- /dev/null +++ b/contrib/sendmail/src/stats.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)stats.c 8.22 (Berkeley) 5/19/98"; +#endif /* not lint */ + +# include "sendmail.h" +# include "mailstats.h" + +struct statistics Stat; + +bool GotStats = FALSE; /* set when we have stats to merge */ + +#define ONE_K 1000 /* one thousand (twenty-four?) */ +#define KBYTES(x) (((x) + (ONE_K - 1)) / ONE_K) + /* +** MARKSTATS -- mark statistics +*/ + +void +markstats(e, to, reject) + register ENVELOPE *e; + register ADDRESS *to; + bool reject; +{ + if (reject == TRUE) + { + 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]++; + } + } + else 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); + } + GotStats = TRUE; +} + /* +** 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; +{ + register int fd; + int sff = SFF_REGONLY|SFF_OPENASROOT; + struct statistics stat; + extern off_t lseek(); + + if (sfile == NULL || !GotStats) + return; + + (void) time(&Stat.stat_itime); + Stat.stat_size = sizeof Stat; + Stat.stat_magic = STAT_MAGIC; + Stat.stat_version = STAT_VERSION; + + if (!bitset(DBS_WRITESTATSTOSYMLINK, DontBlameSendmail)) + sff |= SFF_NOSLINK; + if (!bitset(DBS_WRITESTATSTOHARDLINK, DontBlameSendmail)) + sff |= SFF_NOHLINK; + + fd = safeopen(sfile, O_RDWR, 0644, sff); + if (fd < 0) + { + if (LogLevel > 12) + sm_syslog(LOG_INFO, NOQID, "poststats: %s: %s", + sfile, errstring(errno)); + errno = 0; + return; + } + if (read(fd, (char *) &stat, sizeof stat) == sizeof stat && + stat.stat_size == sizeof stat && + stat.stat_magic == Stat.stat_magic && + stat.stat_version == Stat.stat_version) + { + /* merge current statistics into statfile */ + register int i; + + for (i = 0; i < MAXMAILERS; i++) + { + stat.stat_nf[i] += Stat.stat_nf[i]; + stat.stat_bf[i] += Stat.stat_bf[i]; + stat.stat_nt[i] += Stat.stat_nt[i]; + stat.stat_bt[i] += Stat.stat_bt[i]; + stat.stat_nr[i] += Stat.stat_nr[i]; + stat.stat_nd[i] += Stat.stat_nd[i]; + } + } + else + bcopy((char *) &Stat, (char *) &stat, sizeof stat); + + /* write out results */ + (void) lseek(fd, (off_t) 0, 0); + (void) write(fd, (char *) &stat, sizeof stat); + (void) close(fd); + + /* clear the structure to avoid future disappointment */ + bzero(&Stat, sizeof stat); + GotStats = FALSE; +} diff --git a/contrib/sendmail/src/sysexits.c b/contrib/sendmail/src/sysexits.c new file mode 100644 index 0000000..10d7c85 --- /dev/null +++ b/contrib/sendmail/src/sysexits.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)sysexits.c 8.13 (Berkeley) 5/24/98"; +#endif /* not lint */ + +#include "sendmail.h" + +/* +** SYSEXITS.C -- error messages corresponding to sysexits.h +** +** If the first character of the string is a colon, interpolate +** the current errno after the rest of the string. +*/ + +char *SysExMsg[] = +{ + /* 64 USAGE */ " 500 Bad usage", + /* 65 DATAERR */ " 501 Data format error", + /* 66 NOINPUT */ ":550 Cannot open input", + /* 67 NOUSER */ " 550 User unknown", + /* 68 NOHOST */ " 550 Host unknown", + /* 69 UNAVAILABLE */ " 554 Service unavailable", + /* 70 SOFTWARE */ ":554 Internal error", + /* 71 OSERR */ ":451 Operating system error", + /* 72 OSFILE */ ":554 System file missing", + /* 73 CANTCREAT */ ":550 Can't create output", + /* 74 IOERR */ ":451 I/O error", + /* 75 TEMPFAIL */ " 250 Deferred", + /* 76 PROTOCOL */ " 554 Remote protocol error", + /* 77 NOPERM */ ":550 Insufficient permission", + /* 78 CONFIG */ " 554 Local configuration error", +}; + +int N_SysEx = sizeof(SysExMsg) / sizeof(SysExMsg[0]); + /* +** 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; + + /* 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 */ + return EX_OK; + } + break; + + case 2: /* Mailbox Status */ + switch (code3) + { + case 0: /* Other or Undefined mailbox status */ + case 1: /* Mailbox disabled, not acccepting 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_CONFIG; +} diff --git a/contrib/sendmail/src/trace.c b/contrib/sendmail/src/trace.c new file mode 100644 index 0000000..5ff9795 --- /dev/null +++ b/contrib/sendmail/src/trace.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)trace.c 8.12 (Berkeley) 5/19/98"; +#endif /* not lint */ + +# include "sendmail.h" + +/* +** 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. +*/ + +u_char *tTvect; +int tTsize; +static char *DefFlags; + +void +tTsetup(vect, size, defflags) + u_char *vect; + int size; + char *defflags; +{ + tTvect = vect; + tTsize = size; + DefFlags = defflags; +} + /* +** TtFLAG -- process an external trace flag description. +** +** Parameters: +** s -- the trace flag. +** +** Returns: +** none. +** +** Side Effects: +** sets/clears trace flags. +*/ + +void +tTflag(s) + register char *s; +{ + unsigned int first, last; + register unsigned int i; + + if (*s == '\0') + s = DefFlags; + + for (;;) + { + /* find first flag to set */ + i = 0; + while (isascii(*s) && isdigit(*s)) + i = i * 10 + (*s++ - '0'); + first = i; + + /* find last flag to set */ + if (*s == '-') + { + i = 0; + while (isascii(*++s) && isdigit(*s)) + i = i * 10 + (*s - '0'); + } + 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++] = i; + + /* more arguments? */ + if (*s++ == '\0') + return; + } +} diff --git a/contrib/sendmail/src/udb.c b/contrib/sendmail/src/udb.c new file mode 100644 index 0000000..e7f42af --- /dev/null +++ b/contrib/sendmail/src/udb.c @@ -0,0 +1,1264 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#include "sendmail.h" + +#ifndef lint +#if USERDB +static char sccsid [] = "@(#)udb.c 8.66 (Berkeley) 6/18/98 (with USERDB)"; +#else +static char sccsid [] = "@(#)udb.c 8.66 (Berkeley) 6/18/98 (without USERDB)"; +#endif +#endif + +#if USERDB + +#include + +#ifdef NEWDB +# include +# ifndef DB_VERSION_MAJOR +# define DB_VERSION_MAJOR 1 +# endif +#else +# define DBT struct _data_base_thang_ +DBT +{ + void *data; /* pointer to data */ + size_t size; /* length of data */ +}; +#endif + +/* +** 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 */ + char *udb_default; /* default host for outgoing mail */ + union + { + /* type UE_REMOTE -- do remote call for lookup */ + struct + { + struct sockaddr_in _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 + + /* 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 + +#ifdef NEWDB + /* type UE_FETCH -- lookup in local database */ + struct + { + char *_udb_dbname; /* pathname of database */ + DB *_udb_dbp; /* open database ptr */ + } udb_lookup; +#define udb_dbname udb_u.udb_lookup._udb_dbname +#define udb_dbp udb_u.udb_lookup._udb_dbp +#endif + } 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 option +{ + char *name; + char *val; +}; + +#ifdef HESIOD +extern int hes_udb_get __P((DBT *, DBT *)); +#endif +extern int _udbx_init __P((ENVELOPE *)); + /* +** 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. +*/ + +int UdbPort = 1616; +int UdbTimeout = 10; + +struct udbent UdbEnts[MAXUDBENT + 1]; +int UdbSock = -1; +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 keybuf[MAXKEY]; + + bzero(&key, sizeof key); + bzero(&info, sizeof info); + + if (tTd(28, 1)) + printf("udbexpand(%s)\n", a->q_paddr); + + /* make certain we are supposed to send to this address */ + if (bitset(QDONTSEND|QVERIFIED, a->q_flags)) + 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; + + /* short circuit name begins with '\\' since it can't possibly match */ + if (a->q_user[0] == '\\') + return EX_OK; + + /* if name is too long, assume it won't match */ + if (strlen(a->q_user) > (SIZE_T) sizeof keybuf - 12) + return EX_OK; + + /* if name begins with a colon, it indicates our metadata */ + if (a->q_user[0] == ':') + return EX_OK; + + /* build actual database key */ + (void) strcpy(keybuf, a->q_user); + (void) strcat(keybuf, ":maildrop"); + keylen = strlen(keybuf); + + breakout = FALSE; + for (up = UdbEnts; !breakout; up++) + { + char *user; + int usersize; + int userleft; + char userbuf[MEMCHUNKSIZE]; +#if defined(HESIOD) && defined(HES_GETMAILHOST) + char pobuf[MAXNAME]; +#endif +#if defined(NEWDB) && DB_VERSION_MAJOR > 1 + DBC *dbc = NULL; +#endif + + 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) + { +#ifdef NEWDB + case UDB_DBFETCH: + key.data = keybuf; + key.size = keylen; + if (tTd(28, 80)) + printf("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 + i = 0; + if (dbc == NULL && + (errno = (*up->udb_dbp->cursor)(up->udb_dbp, + NULL, &dbc)) != 0) + i = -1; + if (i != 0 || dbc == NULL || + (errno = dbc->c_get(dbc, &key, + &info, DB_SET)) != 0) + i = 1; +#endif + if (i > 0 || info.size <= 0) + { + if (tTd(28, 2)) + printf("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 + break; + } + if (tTd(28, 80)) + printf("udbexpand: match %.*s: %.*s\n", + (int) key.size, (char *) key.data, + (int) info.size, (char *) info.data); + + a->q_flags &= ~QSELFREF; + while (i == 0 && key.size == keylen && + bcmp(key.data, keybuf, keylen) == 0) + { + char *p; + + if (bitset(EF_VRFYONLY, e->e_flags)) + { + a->q_flags |= QVERIFIED; +#if DB_VERSION_MAJOR > 1 + if (dbc != NULL) + { + (void) dbc->c_close(dbc); + dbc = NULL; + } +#endif + return EX_OK; + } + + breakout = TRUE; + if (info.size >= userleft - 1) + { + char *nuser; + int size = MEMCHUNKSIZE; + + if (info.size > MEMCHUNKSIZE) + size = info.size; + nuser = xalloc(usersize + size); + + bcopy(user, nuser, usersize); + if (user != userbuf) + free(user); + user = nuser; + usersize += size; + userleft += size; + } + p = &user[strlen(user)]; + if (p != user) + { + *p++ = ','; + userleft--; + } + bcopy(info.data, p, 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 + i = 0; + if ((errno = dbc->c_get(dbc, &key, + &info, DB_NEXT)) != 0) + i = 1; +#endif + } + +#if DB_VERSION_MAJOR > 1 + if (dbc != NULL) + { + (void) dbc->c_close(dbc); + dbc = NULL; + } +#endif + + /* 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)) + { + printf("udbexpand: QDONTSEND "); + printaddr(a, FALSE); + } + a->q_flags |= QDONTSEND; + } + 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. + */ + + bzero(&key, sizeof key); + bzero(&info, sizeof info); + (void) strcpy(keybuf, a->q_user); + (void) strcat(keybuf, ":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 + i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL, + &key, &info, 0); +#endif + if (i != 0 || info.size <= 0) + break; + a->q_owner = xalloc(info.size + 1); + bcopy(info.data, a->q_owner, info.size); + a->q_owner[info.size] = '\0'; + + /* announce delivery; NORECEIPT bit set later */ + if (e->e_xfp != NULL) + { + fprintf(e->e_xfp, + "Message delivered to mailing list %s\n", + a->q_paddr); + } + e->e_flags |= EF_SENDRECEIPT; + a->q_flags |= QDELIVERED|QEXPANDED; + break; +#endif + +#ifdef HESIOD + case UDB_HESIOD: + key.data = keybuf; + key.size = keylen; + if (tTd(28, 80)) + printf("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 + + if (tTd(28, 2)) + printf("udbexpand: no match on %s (%d)\n", + (char *) keybuf, (int) keylen); +#if HES_GETMAILHOST + if (tTd(28, 8)) + printf(" ... 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)) + printf("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)) + printf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n", + a->q_user, + hp->po_name, + hp->po_host); + break; + } + info.data = pobuf; + snprintf(pobuf, sizeof pobuf, "%s@%s", + hp->po_name, hp->po_host); + info.size = strlen(info.data); +#else + break; +#endif + } + if (tTd(28, 80)) + printf("udbexpand: match %.*s: %.*s\n", + (int) key.size, (char *) key.data, + (int) info.size, (char *) info.data); + a->q_flags &= ~QSELFREF; + + if (bitset(EF_VRFYONLY, e->e_flags)) + { + a->q_flags |= QVERIFIED; + return EX_OK; + } + + breakout = TRUE; + if (info.size >= usersize) + user = xalloc(info.size + 1); + bcopy(info.data, user, 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)) + { + printf("udbexpand: QDONTSEND "); + printaddr(a, FALSE); + } + a->q_flags |= QDONTSEND; + } + + /* + ** If this address has a -request address, reflect + ** it into the envelope. + */ + + (void) strcpy(keybuf, a->q_user); + (void) strcat(keybuf, ":mailsender"); + keylen = strlen(keybuf); + key.data = keybuf; + key.size = keylen; + i = hes_udb_get(&key, &info); + if (i != 0 || info.size <= 0) + break; + a->q_owner = xalloc(info.size + 1); + bcopy(info.data, a->q_owner, 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)) + return EX_OK; + i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; + if (i >= usersize) + { + usersize = i + 1; + user = xalloc(usersize); + } + (void) snprintf(user, usersize, "%s@%s", + 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)) + { + printf("udbexpand: QDONTSEND "); + printaddr(a, FALSE); + } + a->q_flags |= QDONTSEND; + } + breakout = TRUE; + break; + + case UDB_EOLIST: + breakout = TRUE; + break; + + default: + /* unknown entry type */ + break; + } + if (user != userbuf) + free(user); + } + return EX_OK; +} + /* +** UDBSENDER -- return canonical external name of sender, given local name +** +** Parameters: +** sender -- the name of the sender on the local machine. +** +** Returns: +** The external name for this sender, if derivable from the +** database. +** NULL -- if nothing is changed from the database. +** +** Side Effects: +** none. +*/ + +char * +udbsender(sender) + char *sender; +{ + extern char *udbmatch __P((char *, char *)); + + return udbmatch(sender, "mailname"); +} + + +char * +udbmatch(user, field) + char *user; + char *field; +{ + register char *p; + register struct udbent *up; + int i; + int keylen; + DBT key, info; + char keybuf[MAXKEY]; + + if (tTd(28, 1)) + printf("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) strcpy(keybuf, user); + (void) strcat(keybuf, ":"); + (void) strcat(keybuf, field); + keylen = strlen(keybuf); + + for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) + { + /* + ** Select action based on entry type. + */ + + switch (up->udb_type) + { +#ifdef NEWDB + case UDB_DBFETCH: + bzero(&key, sizeof key); + bzero(&info, 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 + i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL, + &key, &info, 0); +#endif + if (i != 0 || info.size <= 0) + { + if (tTd(28, 2)) + printf("udbmatch: no match on %s (%d) via db\n", + keybuf, keylen); + continue; + } + + p = xalloc(info.size + 1); + bcopy(info.data, p, info.size); + p[info.size] = '\0'; + if (tTd(28, 1)) + printf("udbmatch ==> %s\n", p); + return p; +#endif + +#ifdef 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)) + printf("udbmatch: no match on %s (%d) via hesiod\n", + keybuf, keylen); + continue; + } + + p = xalloc(info.size + 1); + bcopy(info.data, p, info.size); + p[info.size] = '\0'; + if (tTd(28, 1)) + printf("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) strcpy(keybuf, user); + (void) strcat(keybuf, ":maildrop"); + keylen = strlen(keybuf); + + for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) + { + switch (up->udb_type) + { +#ifdef NEWDB + case UDB_DBFETCH: + /* get the default case for this database */ + if (up->udb_default == NULL) + { + bzero(&key, sizeof key); + bzero(&info, sizeof info); + 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 + i = errno = (*up->udb_dbp->get)(up->udb_dbp, + NULL, &key, + &info, 0); +#endif + if (i != 0 || info.size <= 0) + { + /* no default case */ + up->udb_default = ""; + continue; + } + + /* save the default case */ + up->udb_default = xalloc(info.size + 1); + bcopy(info.data, up->udb_default, info.size); + up->udb_default[info.size] = '\0'; + } + else if (up->udb_default[0] == '\0') + continue; + + /* we have a default case -- verify user:maildrop */ + bzero(&key, sizeof key); + bzero(&info, sizeof info); + key.data = keybuf; + key.size = keylen; +#if DB_VERSION_MAJOR < 2 + i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); +#else + i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL, + &key, &info, 0); +#endif + if (i != 0 || info.size <= 0) + { + /* nope -- no aliasing for this user */ + continue; + } + + /* they exist -- build the actual address */ + p = xalloc(strlen(user) + strlen(up->udb_default) + 2); + (void) strcpy(p, user); + (void) strcat(p, "@"); + (void) strcat(p, up->udb_default); + if (tTd(28, 1)) + printf("udbmatch ==> %s\n", p); + return p; +#endif + +#ifdef HESIOD + case UDB_HESIOD: + /* get the default case for this database */ + if (up->udb_default == NULL) + { + key.data = ":default:mailname"; + key.size = strlen(key.data); + i = hes_udb_get(&key, &info); + + if (i != 0 || info.size <= 0) + { + /* no default case */ + up->udb_default = ""; + continue; + } + + /* save the default case */ + up->udb_default = xalloc(info.size + 1); + bcopy(info.data, up->udb_default, 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 */ + p = xalloc(strlen(user) + strlen(up->udb_default) + 2); + (void) strcpy(p, user); + (void) strcat(p, "@"); + (void) strcat(p, up->udb_default); + if (tTd(28, 1)) + printf("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 keybuf[MAXNAME + 1]; + + if (tTd(28, 20) || tTd(38, 20)) + printf("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; + bcopy(name, keybuf, keysize); + keybuf[keysize] = '\0'; + makelower(keybuf); + key = keybuf; + } + val = udbmatch(key, map->map_file); + if (val == NULL) + return NULL; + if (bitset(MF_MATCHONLY, map->map_mflags)) + return map_rewrite(map, name, strlen(name), NULL); + else + return map_rewrite(map, val, strlen(val), av); +} + /* +** _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 + +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 + + p = UdbSpec; + up = UdbEnts; + while (p != NULL) + { + char *spec; + int l; +# if 0 + auto int rcode; + int nmx; + int i; + register struct hostent *h; + char *mxhosts[MAXMXHOSTS + 1]; +# endif + struct option opts[MAXUDBOPTS + 1]; + extern int _udb_parsespec __P((char *, struct option [], int)); + + 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 -- send a datagram to the udb server + ** on host "hostname" asking for the + ** home mail server for this user. + ** *hostname -- similar to +hostname, except that the + ** hostname is searched as an MX record; + ** resulting hosts are searched as for + ** +mxhostname. If no MX host is found, + ** this is the same as +hostname. + ** @hostname -- forward email to the indicated host. + ** This should be the last in the list, + ** since it always matches the input. + ** /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) + { +#if 0 + case '+': /* search remote database */ + case '*': /* search remote database (expand MX) */ + if (*spec == '*') + { +#if NAMED_BIND + nmx = getmxrr(spec + 1, mxhosts, FALSE, &rcode); +#else + mxhosts[0] = spec + 1; + nmx = 1; + rcode = 0; +#endif + if (tTd(28, 16)) + { + int i; + + printf("getmxrr(%s): %d", spec + 1, nmx); + for (i = 0; i <= nmx; i++) + printf(" %s", mxhosts[i]); + printf("\n"); + } + } + else + { + nmx = 1; + mxhosts[0] = spec + 1; + } + + for (i = 0; i < nmx; i++) + { + h = sm_gethostbyname(mxhosts[i]); + if (h == NULL) + continue; + up->udb_type = UDB_REMOTE; + up->udb_addr.sin_family = h->h_addrtype; + bcopy(h->h_addr_list[0], + (char *) &up->udb_addr.sin_addr, + INADDRSZ); + up->udb_addr.sin_port = UdbPort; + up->udb_timeout = UdbTimeout; + ents++; + up++; + } + + /* set up a datagram socket */ + if (UdbSock < 0) + { + UdbSock = socket(AF_INET, SOCK_DGRAM, 0); + (void) fcntl(UdbSock, F_SETFD, 1); + } + break; +#endif + + case '@': /* forward to remote host */ + up->udb_type = UDB_FORWARD; + up->udb_fwdhost = spec + 1; + ents++; + up++; + break; + +#ifdef HESIOD + case 'h': /* use hesiod */ + case 'H': + if (strcasecmp(spec, "hesiod") != 0) + goto badspec; + up->udb_type = UDB_HESIOD; + ents++; + up++; + break; +#endif /* HESIOD */ + +#ifdef 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 = xalloc(l + 4); + strcpy(up->udb_dbname, spec); + strcat(up->udb_dbname, ".db"); + } + errno = 0; +#if DB_VERSION_MAJOR < 2 + up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY, + 0644, DB_BTREE, NULL); +#else + up->udb_dbp = NULL; + errno = db_open(up->udb_dbname, DB_BTREE, DB_RDONLY, + 0644, NULL, NULL, &up->udb_dbp); +#endif + if (up->udb_dbp == NULL) + { + if (tTd(28, 1)) + { + int saveerrno = errno; + +#if DB_VERSION_MAJOR < 2 + printf("dbopen(%s): %s\n", +#else + printf("db_open(%s): %s\n", +#endif + up->udb_dbname, + errstring(errno)); + errno = saveerrno; + } + if (errno != ENOENT && errno != EACCES) + { + if (LogLevel > 2) + sm_syslog(LOG_ERR, e->e_id, +#if DB_VERSION_MAJOR < 2 + "dbopen(%s): %s", +#else + "db_open(%s): %s", +#endif + up->udb_dbname, + errstring(errno)); + up->udb_type = UDB_EOLIST; + if (up->udb_dbname != spec) + free(up->udb_dbname); + goto tempfail; + } + if (up->udb_dbname != spec) + free(up->udb_dbname); + break; + } + up->udb_type = UDB_DBFETCH; + ents++; + up++; + break; +#endif + + default: +badspec: + 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) + { +#if DAEMON + case UDB_REMOTE: + printf("REMOTE: addr %s, timeo %d\n", + anynet_ntoa((SOCKADDR *) &up->udb_addr), + up->udb_timeout); + break; +#endif + + case UDB_DBFETCH: +#ifdef NEWDB + printf("FETCH: file %s\n", + up->udb_dbname); +#else + printf("FETCH\n"); +#endif + break; + + case UDB_FORWARD: + printf("FORWARD: host %s\n", + up->udb_fwdhost); + break; + + case UDB_HESIOD: + printf("HESIOD\n"); + break; + + default: + printf("UNKNOWN\n"); + break; + } + } + } + + UdbInitialized = TRUE; + errno = 0; + return EX_OK; + + /* + ** On temporary failure, back out anything we've already done + */ + + tempfail: +#ifdef 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 + errno = (*up->udb_dbp->close)(up->udb_dbp, 0); +#endif + } + } +#endif + return EX_TEMPFAIL; +} + +int +_udb_parsespec(udbspec, opt, maxopts) + char *udbspec; + struct 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].name = spec; + opt[optnum].val = NULL; + p = strchr(spec, '='); + if (p != NULL) + opt[optnum].val = ++p; + } + return optnum; +} + +#ifdef HESIOD + +int +hes_udb_get(key, info) + DBT *key; + DBT *info; +{ + char *name, *type; + char **hp; + char kbuf[MAXKEY + 1]; + + if (strlen(key->data) >= (SIZE_T) sizeof kbuf) + return 0; + strcpy(kbuf, key->data); + name = kbuf; + type = strrchr(name, ':'); + if (type == NULL) + return 1; + *type++ = '\0'; + if (strchr(name, '@') != NULL) + return 1; + + if (tTd(28, 1)) + printf("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 + 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 + 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)) + printf("hes_udb_get => %s\n", *hp); + + return 0; +} +#endif /* HESIOD */ + +#else /* not 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/useful.h b/contrib/sendmail/src/useful.h new file mode 100644 index 0000000..2a283ab --- /dev/null +++ b/contrib/sendmail/src/useful.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + * + * @(#)useful.h 8.12 (Berkeley) 5/19/98 + */ + +# include + +/* support for bool type */ +typedef int bool; +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +# ifndef NULL +# define NULL 0 +# endif /* NULL */ + +/* bit hacking */ +# define bitset(bit, word) (((word) & (bit)) != 0) + +/* some simple functions */ +# ifndef max +# define max(a, b) ((a) > (b) ? (a) : (b)) +# define min(a, b) ((a) < (b) ? (a) : (b)) +# endif + +/* assertions */ +# ifndef NASSERT +# define ASSERT(expr, msg, parm)\ + if (!(expr))\ + {\ + fprintf(stderr, "assertion botch: %s:%d: ", __FILE__, __LINE__);\ + fprintf(stderr, msg, parm);\ + } +# else /* NASSERT */ +# define ASSERT(expr, msg, parm) +# endif /* NASSERT */ + +/* sccs id's */ +# ifndef lint +# ifdef __STDC__ +# define SCCSID(arg) static char SccsId[] = #arg; +# else +# define SCCSID(arg) static char SccsId[] = "arg"; +# endif +# else +# define SCCSID(arg) +# endif diff --git a/contrib/sendmail/src/usersmtp.c b/contrib/sendmail/src/usersmtp.c new file mode 100644 index 0000000..23e4d02 --- /dev/null +++ b/contrib/sendmail/src/usersmtp.c @@ -0,0 +1,1166 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +# include "sendmail.h" + +#ifndef lint +#if SMTP +static char sccsid[] = "@(#)usersmtp.c 8.104 (Berkeley) 6/30/98 (with SMTP)"; +#else +static char sccsid[] = "@(#)usersmtp.c 8.104 (Berkeley) 6/30/98 (without SMTP)"; +#endif +#endif /* not lint */ + +# include +# include + +# if SMTP + +/* +** USERSMTP -- run SMTP protocol from the user end. +** +** This protocol is described in RFC821. +*/ + +#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ +#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ +#define SMTPCLOSING 421 /* "Service Shutting Down" */ + +char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ +char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ +char SmtpError[MAXLINE] = ""; /* save failure error messages */ +bool SmtpNeedIntro; /* need "while talking" in transcript */ + +extern void smtpmessage __P((char *f, MAILER *m, MCI *mci, ...)); +extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)())); + /* +** SMTPINIT -- initialize SMTP. +** +** Opens the connection and sends the initial protocol. +** +** Parameters: +** m -- mailer to create connection to. +** pvp -- pointer to parameter vector to pass to +** the mailer. +** +** Returns: +** none. +** +** Side Effects: +** creates connection and sends initial protocol. +*/ + +void +smtpinit(m, mci, e) + MAILER *m; + register MCI *mci; + ENVELOPE *e; +{ + register int r; + register char *p; + extern void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *)); + extern void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *)); + + if (tTd(18, 1)) + { + printf("smtpinit "); + mci_dump(mci, FALSE); + } + + /* + ** Open the connection to the mailer. + */ + + SmtpError[0] = '\0'; + CurHostName = mci->mci_host; /* XXX UGLY XXX */ + if (CurHostName == NULL) + CurHostName = MyHostName; + SmtpNeedIntro = TRUE; + switch (mci->mci_state) + { + case MCIS_ACTIVE: + /* need to clear old information */ + smtprset(m, mci, e); + /* fall through */ + + case MCIS_OPEN: + return; + + case MCIS_ERROR: + case MCIS_SSD: + /* shouldn't happen */ + smtpquit(m, mci, e); + /* fall through */ + + case MCIS_CLOSED: + syserr("451 smtpinit: state CLOSED"); + return; + + case MCIS_OPENING: + break; + } + + mci->mci_state = MCIS_OPENING; + + /* + ** Get the greeting message. + ** This should appear spontaneously. Give it five minutes to + ** happen. + */ + + SmtpPhase = mci->mci_phase = "client greeting"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); + 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. + */ + + if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags)) + mci->mci_flags |= MCIF_ESMTP; + +tryhelo: + if (bitnset(M_LMTP, m->m_flags)) + { + smtpmessage("LHLO %s", m, mci, MyHostName); + SmtpPhase = mci->mci_phase = "client LHLO"; + } + else if (bitset(MCIF_ESMTP, mci->mci_flags)) + { + smtpmessage("EHLO %s", m, mci, MyHostName); + SmtpPhase = mci->mci_phase = "client EHLO"; + } + else + { + smtpmessage("HELO %s", m, mci, MyHostName); + SmtpPhase = mci->mci_phase = "client HELO"; + } + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_helo, helo_options); + 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) && + strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) + { + syserr("553 %s config error: mail loops back to me (MX problem?)", + CurHostName); + mci_setstat(mci, EX_CONFIG, NULL, NULL); + mci->mci_errno = 0; + smtpquit(m, mci, e); + return; + } + + /* + ** If this is expected to be another sendmail, send some internal + ** commands. + */ + + if (bitnset(M_INTERNAL, m->m_flags)) + { + /* tell it to be verbose */ + smtpmessage("VERB", m, mci); + r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); + 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: + if (mci->mci_errno == 0) + mci->mci_errno = errno; + mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); + if (mci->mci_state != MCIS_CLOSED) + smtpquit(m, mci, e); + return; + + tempfail2: + if (mci->mci_errno == 0) + mci->mci_errno = errno; + /* XXX should use code from other end iff ENHANCEDSTATUSCODES */ + mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer); + if (mci->mci_state != MCIS_CLOSED) + smtpquit(m, mci, e); + return; + + unavailable: + mci->mci_errno = errno; + mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer); + smtpquit(m, mci, e); + return; +} + /* +** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol +** +** Parameters: +** line -- the response line. +** firstline -- set if this is the first line of the reply. +** m -- the mailer. +** mci -- the mailer connection info. +** e -- the envelope. +** +** Returns: +** none. +*/ + +void +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; + if (strstr(line, "8BIT-OK") != NULL) + mci->mci_flags |= MCIF_8BITOK; +} + /* +** 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. +** +** Returns: +** none. +*/ + +void +helo_options(line, firstline, m, mci, e) + char *line; + bool firstline; + MAILER *m; + register MCI *mci; + ENVELOPE *e; +{ + register char *p; + + if (firstline) + return; + + if (strlen(line) < (SIZE_T) 5) + return; + line += 4; + p = strchr(line, ' '); + if (p != NULL) + *p++ = '\0'; + if (strcasecmp(line, "size") == 0) + { + mci->mci_flags |= MCIF_SIZE; + if (p != NULL) + mci->mci_maxsize = atol(p); + } + else if (strcasecmp(line, "8bitmime") == 0) + { + mci->mci_flags |= MCIF_8BITMIME; + mci->mci_flags &= ~MCIF_7BIT; + } + else if (strcasecmp(line, "expn") == 0) + mci->mci_flags |= MCIF_EXPN; + else if (strcasecmp(line, "dsn") == 0) + mci->mci_flags |= MCIF_DSN; +} + /* +** 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; + int l; + char *bufp; + char *bodytype; + char buf[MAXNAME + 1]; + char optbuf[MAXLINE]; + + if (tTd(18, 2)) + printf("smtpmailfrom: CurHost=%s\n", CurHostName); + + /* set up appropriate options to include */ + if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) + snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize); + else + strcpy(optbuf, ""); + l = sizeof optbuf - strlen(optbuf) - 1; + + 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 && strlen(bodytype) + 7 < l) + { + strcat(optbuf, " BODY="); + strcat(optbuf, bodytype); + l -= strlen(optbuf); + } + } + else if (bitnset(M_8BITS, m->m_flags) || + !bitset(EF_HAS8BIT, e->e_flags) || + bitset(MCIF_8BITOK, mci->mci_flags)) + { + /* 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 + else if (!bitset(MM_PASS8BIT, MimeMode)) + { + /* cannot just send a 8-bit version */ + extern char MsgBuf[]; + + usrerr("%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 && strlen(e->e_envid) < (SIZE_T) (l - 7)) + { + strcat(optbuf, " ENVID="); + strcat(optbuf, e->e_envid); + l -= strlen(optbuf); + } + + /* RET= parameter */ + if (bitset(EF_RET_PARAM, e->e_flags) && l >= 9) + { + strcat(optbuf, " RET="); + if (bitset(EF_NO_BODY_RETN, e->e_flags)) + strcat(optbuf, "HDRS"); + else + strcat(optbuf, "FULL"); + l -= 9; + } + } + + /* + ** Send the MAIL command. + ** Designates the sender. + */ + + mci->mci_state = MCIS_ACTIVE; + + if (bitset(EF_RESPONSE, e->e_flags) && + !bitnset(M_NO_NULL_FROM, m->m_flags)) + (void) strcpy(buf, ""); + 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"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_mail, NULL); + if (r < 0) + { + /* communications failure */ + mci->mci_errno = errno; + mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); + smtpquit(m, mci, e); + return EX_TEMPFAIL; + } + else if (r == 421) + { + /* service shutting down */ + mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer); + smtpquit(m, mci, e); + return EX_TEMPFAIL; + } + else if (REPLYTYPE(r) == 4) + { + mci_setstat(mci, EX_NOTSTICKY, 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, "5.5.2", SmtpReplyBuffer); + return EX_DATAERR; + } + else if (r == 553) + { + /* mailbox name not allowed */ + mci_setstat(mci, EX_NOTSTICKY, "5.1.3", SmtpReplyBuffer); + return EX_DATAERR; + } + else if (r == 552) + { + /* exceeded storage allocation */ + mci_setstat(mci, EX_NOTSTICKY, "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, "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, "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) + ADDRESS *to; + register MAILER *m; + MCI *mci; + ENVELOPE *e; +{ + register int r; + int l; + char optbuf[MAXLINE]; + + strcpy(optbuf, ""); + l = sizeof optbuf - 1; + if (bitset(MCIF_DSN, mci->mci_flags)) + { + /* NOTIFY= parameter */ + if (bitset(QHASNOTIFY, to->q_flags) && + bitset(QPRIMARY, to->q_flags) && + !bitnset(M_LOCALMAILER, m->m_flags)) + { + bool firstone = TRUE; + + strcat(optbuf, " NOTIFY="); + if (bitset(QPINGONSUCCESS, to->q_flags)) + { + strcat(optbuf, "SUCCESS"); + firstone = FALSE; + } + if (bitset(QPINGONFAILURE, to->q_flags)) + { + if (!firstone) + strcat(optbuf, ","); + strcat(optbuf, "FAILURE"); + firstone = FALSE; + } + if (bitset(QPINGONDELAY, to->q_flags)) + { + if (!firstone) + strcat(optbuf, ","); + strcat(optbuf, "DELAY"); + firstone = FALSE; + } + if (firstone) + strcat(optbuf, "NEVER"); + l -= strlen(optbuf); + } + + /* ORCPT= parameter */ + if (to->q_orcpt != NULL && strlen(to->q_orcpt) + 7 < l) + { + strcat(optbuf, " ORCPT="); + strcat(optbuf, to->q_orcpt); + l -= strlen(optbuf); + } + } + + smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf); + + SmtpPhase = mci->mci_phase = "client RCPT"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); + to->q_rstatus = newstr(SmtpReplyBuffer); + to->q_status = smtptodsn(r); + to->q_statmta = mci->mci_host; + if (r < 0 || REPLYTYPE(r) == 4) + return EX_TEMPFAIL; + else if (REPLYTYPE(r) == 2) + return EX_OK; + else if (r == 550) + { + to->q_status = "5.1.1"; + return EX_NOUSER; + } + else if (r == 551) + { + to->q_status = "5.1.6"; + return EX_NOUSER; + } + else if (r == 553) + { + to->q_status = "5.1.3"; + 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, "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. +** +** Side Effects: +** none. +*/ + +static jmp_buf CtxDataTimeout; +static void datatimeout __P((void)); + +int +smtpdata(m, mci, e) + MAILER *m; + register MCI *mci; + register ENVELOPE *e; +{ + register int r; + register EVENT *ev; + int rstat; + int xstat; + time_t timeout; + + /* + ** Send the data. + ** First send the command and check that it is ok. + ** Then send the data. + ** 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); + SmtpPhase = mci->mci_phase = "client DATA 354"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + r = reply(m, mci, e, TimeOuts.to_datainit, NULL); + if (r < 0 || REPLYTYPE(r) == 4) + { + smtpquit(m, mci, e); + return EX_TEMPFAIL; + } + else if (REPLYTYPE(r) == 5) + { + smtprset(m, mci, e); + return EX_UNAVAILABLE; + } + else if (r != 354) + { + 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, "5.5.1", SmtpReplyBuffer); + return (EX_PROTOCOL); + } + + /* + ** 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 (setjmp(CtxDataTimeout) != 0) + { + mci->mci_errno = errno; + mci->mci_state = MCIS_ERROR; + mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); + syserr("451 timeout writing message to %s", CurHostName); + smtpquit(m, mci, e); + return EX_TEMPFAIL; + } + + timeout = e->e_msgsize / 16; + if (timeout < (time_t) 600) + timeout = (time_t) 600; + timeout += e->e_nrcpts * 300; + ev = setevent(timeout, datatimeout, 0); + + /* + ** Output the actual message. + */ + + (*e->e_puthdr)(mci, e->e_header, e); + (*e->e_putbody)(mci, e, NULL); + + /* + ** Cleanup after sending message. + */ + + clrevent(ev); + + if (ferror(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 */ + fprintf(mci->mci_out, ".%s", m->m_eol); + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid()); + if (Verbose) + nmessage(">>> ."); + + /* check for the results of the transaction */ + SmtpPhase = mci->mci_phase = "client DATA status"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); + if (bitnset(M_LMTP, m->m_flags)) + return EX_OK; + r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); + if (r < 0) + { + smtpquit(m, mci, e); + return EX_TEMPFAIL; + } + 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 (REPLYCLASS(r) != 5) + rstat = xstat = EX_PROTOCOL; + else if (REPLYTYPE(r) == 2) + rstat = xstat = EX_OK; + else if (REPLYTYPE(r) == 5) + rstat = EX_UNAVAILABLE; + else + rstat = EX_PROTOCOL; + mci_setstat(mci, xstat, smtptodsn(r), SmtpReplyBuffer); + if (e->e_statmsg != NULL) + free(e->e_statmsg); + e->e_statmsg = newstr(&SmtpReplyBuffer[4]); + 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; +} + + +static void +datatimeout() +{ + longjmp(CtxDataTimeout, 1); +} + /* +** 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 stat; + + /* check for the results of the transaction */ + r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); + if (r < 0) + { + smtpquit(m, mci, e); + return EX_TEMPFAIL; + } + if (e->e_statmsg != NULL) + free(e->e_statmsg); + e->e_statmsg = newstr(&SmtpReplyBuffer[4]); + if (REPLYTYPE(r) == 4) + stat = EX_TEMPFAIL; + else if (REPLYCLASS(r) != 5) + stat = EX_PROTOCOL; + else if (REPLYTYPE(r) == 2) + stat = EX_OK; + else if (REPLYTYPE(r) == 5) + stat = EX_UNAVAILABLE; + else + stat = EX_PROTOCOL; + mci_setstat(mci, stat, smtptodsn(r), SmtpReplyBuffer); + if (LogLevel > 1 && stat == EX_PROTOCOL) + { + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP DATA-3 protocol error: %s", + CurHostName, + shortenstring(SmtpReplyBuffer, 403)); + } + return stat; +} + /* +** 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; + + /* + ** 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) + { + SmtpPhase = "client QUIT"; + smtpmessage("QUIT", m, mci); + (void) reply(m, mci, e, TimeOuts.to_quit, NULL); + SuprErrs = oldSuprErrs; + if (mci->mci_state == MCIS_CLOSED) + return; + } + + /* now actually close the connection and pick up the zombie */ + (void) endmailer(mci, e, NULL); + + SuprErrs = oldSuprErrs; +} + /* +** SMTPRSET -- send a RSET (reset) command +*/ + +void +smtprset(m, mci, e) + register MAILER *m; + register MCI *mci; + ENVELOPE *e; +{ + int r; + + SmtpPhase = "client RSET"; + smtpmessage("RSET", m, mci); + r = reply(m, mci, e, TimeOuts.to_rset, NULL); + if (r < 0) + mci->mci_state = MCIS_ERROR; + else if (REPLYTYPE(r) == 2) + { + mci->mci_state = MCIS_OPEN; + return; + } + smtpquit(m, mci, e); +} + /* +** SMTPPROBE -- check the connection state +*/ + +int +smtpprobe(mci) + register MCI *mci; +{ + int r; + MAILER *m = mci->mci_mailer; + extern ENVELOPE BlankEnvelope; + ENVELOPE *e = &BlankEnvelope; + + SmtpPhase = "client probe"; + smtpmessage("RSET", m, mci); + r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); + if (r < 0 || 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. +** +** Returns: +** reply code it reads. +** +** Side Effects: +** flushes the mail file. +*/ + +int +reply(m, mci, e, timeout, pfunc) + MAILER *m; + MCI *mci; + ENVELOPE *e; + time_t timeout; + void (*pfunc)(); +{ + register char *bufp; + register int r; + bool firstline = TRUE; + char junkbuf[MAXLINE]; + + if (mci->mci_out != NULL) + (void) fflush(mci->mci_out); + + if (tTd(18, 1)) + printf("reply\n"); + + /* + ** Read the input line, being careful not to hang. + */ + + bufp = SmtpReplyBuffer; + for (;;) + { + register char *p; + extern time_t curtime __P((void)); + + /* actually do the read */ + if (e->e_xfp != NULL) + (void) fflush(e->e_xfp); /* for debugging */ + + /* if we are in the process of closing just give the code */ + if (mci->mci_state == MCIS_CLOSED) + return (SMTPCLOSING); + + if (mci->mci_out != NULL) + fflush(mci->mci_out); + + /* get the line from the other side */ + p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); + mci->mci_lastuse = curtime(); + + if (p == NULL) + { + bool oldholderrs; + extern char MsgBuf[]; + + /* if the remote end closed early, fake an error */ + if (errno == 0) +# ifdef ECONNRESET + errno = ECONNRESET; +# else /* ECONNRESET */ + errno = EPIPE; +# endif /* ECONNRESET */ + + mci->mci_errno = errno; + oldholderrs = HoldErrs; + HoldErrs = TRUE; + usrerr("451 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)) + pause(); + mci->mci_state = MCIS_ERROR; + smtpquit(m, mci, e); +#if XDEBUG + { + char wbuf[MAXLINE]; + char *p = wbuf; + int wbufleft = sizeof wbuf; + + if (e->e_to != NULL) + { + int plen; + + snprintf(p, wbufleft, "%s... ", + shortenstring(e->e_to, MAXSHORTSTR)); + plen = strlen(p); + p += plen; + wbufleft -= plen; + } + snprintf(p, wbufleft, "reply(%.100s) during %s", + CurHostName, SmtpPhase); + checkfd012(wbuf); + } +#endif + HoldErrs = oldholderrs; + 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 */ + fprintf(CurEnv->e_xfp, + "... while talking to %s:\n", + CurHostName); + SmtpNeedIntro = FALSE; + } + if (SmtpMsgBuffer[0] != '\0') + fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); + SmtpMsgBuffer[0] = '\0'; + + /* now log the message as from the other side */ + fprintf(e->e_xfp, "<<< %s\n", bufp); + } + + /* display the input for verbose mode */ + if (Verbose) + nmessage("050 %s", bufp); + + /* ignore improperly formated input */ + if (!(isascii(bufp[0]) && isdigit(bufp[0])) || + !(isascii(bufp[1]) && isdigit(bufp[1])) || + !(isascii(bufp[2]) && isdigit(bufp[2])) || + !(bufp[3] == ' ' || bufp[3] == '-' || bufp[3] == '\0')) + continue; + + /* 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' && + (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0')) + snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer); + + /* reply code 421 is "Service Shutting Down" */ + if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) + { + /* 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 +smtpmessage(f, m, mci, va_alist) + char *f; + MAILER *m; + MCI *mci; + va_dcl +#endif +{ + VA_LOCAL_DECL + + VA_START(mci); + (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap); + VA_END; + + if (tTd(18, 1) || Verbose) + nmessage(">>> %s", SmtpMsgBuffer); + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d >>> %s\n", + (int) getpid(), SmtpMsgBuffer); + if (mci->mci_out != NULL) + { + fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, + m == NULL ? "\r\n" : m->m_eol); + } + else if (tTd(18, 1)) + { + printf("smtpmessage: NULL mci_out\n"); + } +} + +# endif /* SMTP */ diff --git a/contrib/sendmail/src/util.c b/contrib/sendmail/src/util.c new file mode 100644 index 0000000..ba35dcd --- /dev/null +++ b/contrib/sendmail/src/util.c @@ -0,0 +1,2094 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)util.c 8.159 (Berkeley) 7/1/98"; +#endif /* not lint */ + +# include "sendmail.h" +# include + /* +** STRIPQUOTES -- Strip quotes & quote bits from a string. +** +** Runs through a string and strips off unquoted quote +** characters and quote bits. This is done in place. +** +** Parameters: +** s -- the string to strip. +** +** Returns: +** none. +** +** Side Effects: +** none. +** +** Called By: +** deliver +*/ + +void +stripquotes(s) + char *s; +{ + register char *p; + register char *q; + register char c; + + if (s == NULL) + return; + + p = q = s; + do + { + c = *p++; + if (c == '\\') + c = *p++; + else if (c == '"') + continue; + *q++ = c; + } while (c != '\0'); +} + /* +** ADDQUOTES -- Adds quotes & quote bits to a string. +** +** Runs through a string and adds characters and quote bits. +** +** Parameters: +** s -- the string to modify. +** +** Returns: +** pointer to quoted string. +** +** Side Effects: +** none. +** +*/ + +char * +addquotes(s) + char *s; +{ + 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 = xalloc(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; +} + /* +** 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 -- if the string is RFC822 compliant. +** FALSE -- if the string is not RFC822 compliant. +** +** Side Effects: +** none. +** +*/ + +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 '(' */ + if (quoted || commentlev != 0) + return FALSE; + else + return TRUE; +} + /* +** XALLOC -- Allocate memory and bitch wildly on failure. +** +** THIS IS A CLUDGE. This should be made to give a proper +** error -- but after all, what can we do? +** +** Parameters: +** sz -- size of area to allocate. +** +** Returns: +** pointer to data region. +** +** Side Effects: +** Memory is allocated. +*/ + +char * +xalloc(sz) + register int sz; +{ + register char *p; + + /* some systems can't handle size zero mallocs */ + if (sz <= 0) + sz = 1; + + p = malloc((unsigned) sz); + if (p == NULL) + { + syserr("!Out of memory!!"); + /* exit(EX_UNAVAILABLE); */ + } + return (p); +} + /* +** COPYPLIST -- copy list of pointers. +** +** This routine is the equivalent of newstr 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. +** +** Returns: +** a copy of 'list'. +** +** Side Effects: +** none. +*/ + +char ** +copyplist(list, copycont) + char **list; + bool copycont; +{ + register char **vp; + register char **newvp; + + for (vp = list; *vp != NULL; vp++) + continue; + + vp++; + + newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); + bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); + + if (copycont) + { + for (vp = newvp; *vp != NULL; vp++) + *vp = newstr(*vp); + } + + return (newvp); +} + /* +** COPYQUEUE -- copy address queue. +** +** This routine is the equivalent of newstr for address queues +** addresses marked with QDONTSEND aren't copied +** +** Parameters: +** addr -- list of address structures to copy. +** +** Returns: +** a copy of 'addr'. +** +** Side Effects: +** none. +*/ + +ADDRESS * +copyqueue(addr) + ADDRESS *addr; +{ + register ADDRESS *newaddr; + ADDRESS *ret; + register ADDRESS **tail = &ret; + + while (addr != NULL) + { + if (!bitset(QDONTSEND, addr->q_flags)) + { + newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS)); + STRUCTCOPY(*addr, *newaddr); + *tail = newaddr; + tail = &newaddr->q_next; + } + addr = addr->q_next; + } + *tail = NULL; + + return ret; +} + /* +** PRINTAV -- print argument vector. +** +** Parameters: +** av -- argument vector. +** +** Returns: +** none. +** +** Side Effects: +** prints av. +*/ + +void +printav(av) + register char **av; +{ + while (*av != NULL) + { + if (tTd(0, 44)) + printf("\n\t%08lx=", (u_long) *av); + else + (void) putchar(' '); + xputs(*av++); + } + (void) putchar('\n'); +} + /* +** LOWER -- turn letter into lower case. +** +** Parameters: +** c -- character to turn into lower case. +** +** Returns: +** c, in lower case. +** +** Side Effects: +** none. +*/ + +char +lower(c) + register char c; +{ + return((isascii(c) && isupper(c)) ? tolower(c) : c); +} + /* +** XPUTS -- put string doing control escapes. +** +** Parameters: +** s -- string to put. +** +** Returns: +** none. +** +** Side Effects: +** output to stdout +*/ + +void +xputs(s) + register const char *s; +{ + register int c; + register struct metamac *mp; + bool shiftout = FALSE; + extern struct metamac MetaMacros[]; + + if (s == NULL) + { + printf("%s%s", TermEscape.te_rv_on, TermEscape.te_rv_off); + return; + } + while ((c = (*s++ & 0377)) != '\0') + { + if (shiftout) + { + printf("%s", TermEscape.te_rv_off); + shiftout = FALSE; + } + if (!isascii(c)) + { + if (c == MATCHREPL) + { + printf("%s$", TermEscape.te_rv_on); + shiftout = TRUE; + if (*s == '\0') + continue; + c = *s++ & 0377; + goto printchar; + } + if (c == MACROEXPAND || c == MACRODEXPAND) + { + printf("%s$", TermEscape.te_rv_on); + if (c == MACRODEXPAND) + putchar('&'); + shiftout = TRUE; + if (*s == '\0') + continue; + if (strchr("=~&?", *s) != NULL) + putchar(*s++); + if (bitset(0200, *s)) + printf("{%s}", macname(*s++ & 0377)); + else + printf("%c", *s++); + continue; + } + for (mp = MetaMacros; mp->metaname != '\0'; mp++) + { + if ((mp->metaval & 0377) == c) + { + printf("%s$%c", + TermEscape.te_rv_on, + mp->metaname); + shiftout = TRUE; + break; + } + } + if (c == MATCHCLASS || c == MATCHNCLASS) + { + if (bitset(0200, *s)) + printf("{%s}", macname(*s++ & 0377)); + else if (*s != '\0') + printf("%c", *s++); + } + if (mp->metaname != '\0') + continue; + + /* unrecognized meta character */ + printf("%sM-", TermEscape.te_rv_on); + shiftout = TRUE; + c &= 0177; + } + printchar: + if (isprint(c)) + { + putchar(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) + { + printf("%s", TermEscape.te_rv_on); + shiftout = TRUE; + } + if (isprint(c)) + { + (void) putchar('\\'); + (void) putchar(c); + } + else + { + (void) putchar('^'); + (void) putchar(c ^ 0100); + } + } + if (shiftout) + printf("%s", TermEscape.te_rv_off); + (void) fflush(stdout); +} + /* +** 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. +** +** Called By: +** parse +*/ + +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); +} + /* +** BUILDFNAME -- build full name from gecos style entry. +** +** This routine interprets the strange entry that would appear +** in the GECOS field of the password file. +** +** Parameters: +** p -- name to build. +** login -- the login name of this user (for &). +** buf -- place to put the result. +** buflen -- length of buf. +** +** Returns: +** none. +** +** Side Effects: +** none. +*/ + +void +buildfname(gecos, login, buf, buflen) + register char *gecos; + char *login; + char *buf; + int buflen; +{ + register char *p; + register char *bp = buf; + + if (*gecos == '*') + gecos++; + + /* copy gecos, interpolating & to be full name */ + for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) + { + if (bp >= &buf[buflen - 1]) + { + /* buffer overflow -- just use login name */ + snprintf(buf, buflen, "%s", login); + return; + } + if (*p == '&') + { + /* interpolate full name */ + snprintf(bp, buflen - (bp - buf), "%s", login); + *bp = toupper(*bp); + bp += strlen(bp); + } + else + *bp++ = *p; + } + *bp = '\0'; +} + /* +** 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: +** none +** +** Side Effects: +** output of l to fp. +*/ + +void +putline(l, mci) + register char *l; + register MCI *mci; +{ + 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. +** +** Returns: +** none +** +** Side Effects: +** output of l to fp. +*/ + +void +putxline(l, len, mci, pxflags) + register char *l; + size_t len; + register MCI *mci; + int pxflags; +{ + register char *p, *end; + int slop = 0; + size_t eol_len = strlen(mci->mci_mailer->m_eol); + + /* strip out 0200 bits -- these can look like TELNET protocol */ + if (bitset(MCIF_7BIT, mci->mci_flags) || + bitset(PXLF_STRIP8BIT, pxflags)) + { + register char svchar; + + for (p = l; (svchar = *p) != '\0'; ++p) + if (bitset(0200, svchar)) + *p = svchar &~ 0200; + } + + end = l + len; + do + { + /* find the end of the line */ + p = memchr(l, '\n', end - l); + if (p == NULL) + p = end; + + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d >>> ", (int) getpid()); + + /* check for line overflow */ + while (mci->mci_mailer->m_linelimit > 0 && + (p - l + slop) > mci->mci_mailer->m_linelimit) + { + char *l_base = l; + register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; + + if (l[0] == '.' && slop == 0 && + bitnset(M_XDOT, mci->mci_mailer->m_flags)) + { + (void) putc('.', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; + if (TrafficLogFile != NULL) + (void) putc('.', TrafficLogFile); + } + else if (l[0] == 'F' && slop == 0 && + bitset(PXLF_MAPFROM, pxflags) && + strncmp(l, "From ", 5) == 0 && + bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) + { + (void) putc('>', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; + if (TrafficLogFile != NULL) + (void) putc('>', TrafficLogFile); + } + while (l < q) + { + (void) putc(*l++, mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; + } + (void) putc('!', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; + fputs(mci->mci_mailer->m_eol, mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen += eol_len; + (void) putc(' ', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; + if (TrafficLogFile != NULL) + { + for (l = l_base; l < q; l++) + (void) putc(*l, TrafficLogFile); + fprintf(TrafficLogFile, "!\n%05d >>> ", + (int) getpid()); + } + slop = 1; + } + + /* output last part */ + if (l[0] == '.' && slop == 0 && + bitnset(M_XDOT, mci->mci_mailer->m_flags)) + { + (void) putc('.', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; + if (TrafficLogFile != NULL) + (void) putc('.', TrafficLogFile); + } + else if (l[0] == 'F' && slop == 0 && + bitset(PXLF_MAPFROM, pxflags) && + strncmp(l, "From ", 5) == 0 && + bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) + { + (void) putc('>', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; + if (TrafficLogFile != NULL) + (void) putc('>', TrafficLogFile); + } + for ( ; l < p; ++l) + { + if (TrafficLogFile != NULL) + (void) putc(*l, TrafficLogFile); + (void) putc(*l, mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; + } + if (TrafficLogFile != NULL) + (void) putc('\n', TrafficLogFile); + fputs(mci->mci_mailer->m_eol, mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen += eol_len; + if (l < end && *l == '\n') + { + if (*++l != ' ' && *l != '\t' && *l != '\0' && + bitset(PXLF_HEADER, pxflags)) + { + (void) putc(' ', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; + if (TrafficLogFile != NULL) + (void) putc(' ', TrafficLogFile); + } + } + } while (l < end); +} + /* +** XUNLINK -- unlink a file, doing logging as appropriate. +** +** Parameters: +** f -- name of file to unlink. +** +** Returns: +** none. +** +** Side Effects: +** f is unlinked. +*/ + +void +xunlink(f) + char *f; +{ + register int i; + + if (LogLevel > 98) + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "unlink %s", + f); + + i = unlink(f); + if (i < 0 && LogLevel > 97) + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "%s: unlink-fail %d", + f, errno); +} + /* +** XFCLOSE -- close a file, doing logging as appropriate. +** +** Parameters: +** fp -- file pointer for the file to close +** a, b -- miscellaneous crud to print for debugging +** +** Returns: +** none. +** +** Side Effects: +** fp is closed. +*/ + +void +xfclose(fp, a, b) + FILE *fp; + char *a, *b; +{ + if (tTd(53, 99)) + printf("xfclose(%lx) %s %s\n", (u_long) fp, a, b); +#if XDEBUG + if (fileno(fp) == 1) + syserr("xfclose(%s %s): fd = 1", a, b); +#endif + if (fclose(fp) < 0 && tTd(53, 99)) + printf("xfclose FAILURE: %s\n", errstring(errno)); +} + /* +** 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 will also leave +** buf containing a null string. +** buf otherwise. +** +** Side Effects: +** none. +*/ + +static jmp_buf CtxReadTimeout; +static void readtimeout __P((time_t)); + +char * +sfgets(buf, siz, fp, timeout, during) + char *buf; + int siz; + FILE *fp; + time_t timeout; + char *during; +{ + register EVENT *ev = NULL; + register char *p; + + if (fp == NULL) + { + buf[0] = '\0'; + return NULL; + } + + /* set the timeout */ + if (timeout != 0) + { + if (setjmp(CtxReadTimeout) != 0) + { + if (LogLevel > 1) + sm_syslog(LOG_NOTICE, CurEnv->e_id, + "timeout waiting for input from %.100s during %s", + CurHostName ? CurHostName : "local", + during); + errno = 0; + buf[0] = '\0'; +#if XDEBUG + checkfd012(during); +#endif + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n", + (int) getpid()); + return (NULL); + } + ev = setevent(timeout, readtimeout, 0); + } + + /* try to read */ + p = NULL; + while (!feof(fp) && !ferror(fp)) + { + errno = 0; + p = fgets(buf, siz, fp); + if (p != NULL || errno != EINTR) + break; + clearerr(fp); + } + + /* clear the event if it has not sprung */ + clrevent(ev); + + /* clean up the books and exit */ + LineNumber++; + if (p == NULL) + { + buf[0] = '\0'; + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid()); + return (NULL); + } + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), 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); +} + +/* ARGSUSED */ +static void +readtimeout(timeout) + time_t timeout; +{ + longjmp(CtxReadTimeout, 1); +} + /* +** FGETFOLDED -- like fgets, but know about folded lines. +** +** Parameters: +** buf -- place to put result. +** n -- bytes available. +** f -- file to read from. +** +** Returns: +** input line(s) on success, NULL on error or EOF. +** This will normally be buf -- unless the line is too +** long, when it will be xalloc()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, n, f) + char *buf; + register int n; + FILE *f; +{ + register char *p = buf; + char *bp = buf; + register int i; + + n--; + while ((i = getc(f)) != EOF) + { + if (i == '\r') + { + i = getc(f); + if (i != '\n') + { + if (i != EOF) + (void) ungetc(i, f); + i = '\r'; + } + } + if (--n <= 0) + { + /* allocate new space */ + char *nbp; + int nn; + + nn = (p - bp); + if (nn < MEMCHUNKSIZE) + nn *= 2; + else + nn += MEMCHUNKSIZE; + nbp = xalloc(nn); + bcopy(bp, nbp, p - bp); + p = &nbp[p - bp]; + if (bp != buf) + free(bp); + bp = nbp; + n = nn - (p - bp); + } + *p++ = i; + if (i == '\n') + { + LineNumber++; + i = getc(f); + if (i != EOF) + (void) ungetc(i, f); + 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. +** +** Side Effects: +** none. +*/ + +time_t +curtime() +{ + auto time_t t; + + (void) time(&t); + return (t); +} + /* +** ATOBOOL -- convert a string representation to boolean. +** +** Defaults to "TRUE" +** +** Parameters: +** s -- string to convert. Takes "tTyY" as true, +** others as false. +** +** Returns: +** A boolean representation of the string. +** +** Side Effects: +** none. +*/ + +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. +** +** Side Effects: +** none. +*/ + +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 +** +** Side Effects: +** none. +*/ + +bool +bitintersect(a, b) + BITMAP a; + BITMAP 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. +** +** Side Effects: +** none. +*/ + +bool +bitzerop(map) + BITMAP 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: +** a -- possible substring. +** b -- possible superstring. +** +** Returns: +** TRUE if a is contained in b. +** FALSE otherwise. +*/ + +bool +strcontainedin(a, b) + register char *a; + register char *b; +{ + int la; + int lb; + int c; + + la = strlen(a); + lb = strlen(b); + c = *a; + if (isascii(c) && isupper(c)) + c = tolower(c); + for (; lb-- >= la; b++) + { + if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) + continue; + if (strncasecmp(a, b, la) == 0) + return TRUE; + } + 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 +} + /* +** 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 BITMAP baseline; + extern int DtableSize; + + if (DtableSize > 256) + maxfd = 256; + 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. +*/ + +#include + +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, send output to syslog instead of stdout. +*/ + +void +dumpfd(fd, printclosed, logit) + int fd; + bool printclosed; + bool logit; +{ + register char *p; + char *hp; +#ifdef S_IFSOCK + SOCKADDR sa; +#endif + auto SOCKADDR_LEN_T slen; + int i; +#if STAT64 > 0 + struct stat64 st; +#else + struct stat st; +#endif + char buf[200]; + + p = buf; + snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); + p += strlen(p); + + if ( +#if STAT64 > 0 + fstat64(fd, &st) +#else + fstat(fd, &st) +#endif + < 0) + { + if (errno != EBADF) + { + snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)", + errstring(errno)); + goto printit; + } + else if (printclosed) + { + snprintf(p, SPACELEFT(buf, p), "CLOSED"); + goto printit; + } + return; + } + + i = fcntl(fd, F_GETFL, NULL); + if (i != -1) + { + snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i); + p += strlen(p); + } + + snprintf(p, SPACELEFT(buf, p), "mode=%o: ", st.st_mode); + p += strlen(p); + switch (st.st_mode & S_IFMT) + { +#ifdef S_IFSOCK + case S_IFSOCK: + snprintf(p, SPACELEFT(buf, p), "SOCK "); + p += strlen(p); + slen = sizeof sa; + if (getsockname(fd, &sa.sa, &slen) < 0) + snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); + else + { + hp = hostnamebyanyaddr(&sa); + if (sa.sa.sa_family == AF_INET) + snprintf(p, SPACELEFT(buf, p), "%s/%d", + hp, ntohs(sa.sin.sin_port)); + else + snprintf(p, SPACELEFT(buf, p), "%s", hp); + } + p += strlen(p); + snprintf(p, SPACELEFT(buf, p), "->"); + p += strlen(p); + slen = sizeof sa; + if (getpeername(fd, &sa.sa, &slen) < 0) + snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); + else + { + hp = hostnamebyanyaddr(&sa); + if (sa.sa.sa_family == AF_INET) + snprintf(p, SPACELEFT(buf, p), "%s/%d", + hp, ntohs(sa.sin.sin_port)); + else + snprintf(p, SPACELEFT(buf, p), "%s", hp); + } + break; +#endif + + case S_IFCHR: + snprintf(p, SPACELEFT(buf, p), "CHR: "); + p += strlen(p); + goto defprint; + + case S_IFBLK: + snprintf(p, SPACELEFT(buf, p), "BLK: "); + p += strlen(p); + goto defprint; + +#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) + case S_IFIFO: + snprintf(p, SPACELEFT(buf, p), "FIFO: "); + p += strlen(p); + goto defprint; +#endif + +#ifdef S_IFDIR + case S_IFDIR: + snprintf(p, SPACELEFT(buf, p), "DIR: "); + p += strlen(p); + goto defprint; +#endif + +#ifdef S_IFLNK + case S_IFLNK: + snprintf(p, SPACELEFT(buf, p), "LNK: "); + p += strlen(p); + goto defprint; +#endif + + default: +defprint: + if (sizeof st.st_ino > sizeof (long)) + snprintf(p, SPACELEFT(buf, p), + "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ", + major(st.st_dev), minor(st.st_dev), + quad_to_string(st.st_ino), + st.st_nlink, st.st_uid, st.st_gid); + else + snprintf(p, SPACELEFT(buf, p), + "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ", + major(st.st_dev), minor(st.st_dev), + (unsigned long) st.st_ino, + st.st_nlink, st.st_uid, st.st_gid); + if (sizeof st.st_size > sizeof (long)) + snprintf(p, SPACELEFT(buf, p), "size=%s", + quad_to_string(st.st_size)); + else + snprintf(p, SPACELEFT(buf, p), "size=%lu", + (unsigned long) st.st_size); + break; + } + +printit: + if (logit) + sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL, + "%.800s", buf); + else + printf("%s\n", buf); +} + /* +** SHORTEN_HOSTNAME -- strip local domain information off of hostname. +** +** Parameters: +** host -- the host to shorten (stripped in place). +** +** Returns: +** none. +*/ + +void +shorten_hostname(host) + char host[]; +{ + register char *p; + char *mydom; + int i; + bool canon = FALSE; + + /* strip off final dot */ + p = &host[strlen(host) - 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; + + /* yes, we have a domain -- see if it looks like us */ + mydom = macvalue('m', CurEnv); + if (mydom == NULL) + mydom = ""; + i = strlen(++p); + if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 && + (mydom[i] == '.' || mydom[i] == '\0')) + *--p = '\0'; +} + /* +** 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. +*/ + +int +prog_open(argv, pfd, e) + char **argv; + int *pfd; + ENVELOPE *e; +{ + int pid; + int i; + int saveerrno; + int fdv[2]; + char *p, *q; + char buf[MAXLINE + 1]; + 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]); + close(fdv[0]); + close(fdv[1]); + return -1; + } + if (pid > 0) + { + /* parent */ + close(fdv[1]); + *pfd = fdv[0]; + return pid; + } + + /* child -- close stdin */ + close(0); + + /* stdout goes back to parent */ + close(fdv[0]); + if (dup2(fdv[1], 1) < 0) + { + syserr("%s: cannot dup2 for stdout", argv[0]); + _exit(EX_OSERR); + } + close(fdv[1]); + + /* stderr goes to transcript if available */ + if (e->e_xfp != NULL) + { + if (dup2(fileno(e->e_xfp), 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) + close(fileno(e->e_lockfp)); + + /* run as default user */ + endpwent(); + if (setgid(DefGid) < 0 && geteuid() == 0) + syserr("prog_open: setgid(%ld) failed", (long) DefGid); + if (setuid(DefUid) < 0 && geteuid() == 0) + syserr("prog_open: setuid(%ld) failed", (long) DefUid); + + /* 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("/"); + } + + /* arrange for all the files to be closed */ + for (i = 3; i < DtableSize; i++) + { + register int j; + + if ((j = fcntl(i, F_GETFD, 0)) != -1) + (void) fcntl(i, F_SETFD, j | 1); + } + + /* now exec the process */ + execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); + + /* woops! failed */ + saveerrno = errno; + syserr("%s: cannot exec", argv[0]); + if (transienterror(saveerrno)) + _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; + char delim; + char buf[]; + int buflen; +{ + char *p; + char *begin, *end; + int i; + char delimbuf[4]; + + if (delim == '\0') + strcpy(delimbuf, "\n\t "); + else + { + delimbuf[0] = delim; + delimbuf[1] = '\0'; + } + + p = line; + if (*p == '\0') + return NULL; /* line empty */ + if (*p == delim && col == 0) + return NULL; /* first column empty */ + + begin = line; + + if (col == 0 && 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 (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; + strncpy(buf, begin, i); + buf[i] = '\0'; + 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); + + 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 */ + if (bp != NULL) + free(bp); + bp = xalloc(l); + bl = l; + } + strcpy(bp, s); + for (p = bp; (p = strchr(p, '\n')) != NULL; ) + *p++ = ' '; + + if (logattacks) + { + sm_syslog(LOG_NOTICE, CurEnv->e_id, + "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", + RealHostName == NULL ? "[UNKNOWN]" : RealHostName, + shortenstring(bp, MAXSHORTSTR)); + } + + return bp; +} + /* +** 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 +*/ + +int +path_is_dir(pathname, createflag) + char *pathname; + bool createflag; +{ + struct stat statbuf; + +#if HASLSTAT + if (lstat(pathname, &statbuf) < 0) +#else + if (stat(pathname, &statbuf) < 0) +#endif + { + 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. +** +** Returns: +** none +*/ + +static pid_t *ProcListVec = NULL; +static int ProcListSize = 0; + +#define NO_PID ((pid_t) 0) +#ifndef PROC_LIST_SEG +# define PROC_LIST_SEG 32 /* number of pids to alloc at a time */ +#endif + +void +proc_list_add(pid) + pid_t pid; +{ + int i; + extern void proc_list_probe __P((void)); + + for (i = 0; i < ProcListSize; i++) + { + if (ProcListVec[i] == 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] == NO_PID) + break; + } + } + if (i >= ProcListSize) + { + /* grow process list */ + pid_t *npv; + + npv = (pid_t *) xalloc(sizeof (pid_t) * (ProcListSize + PROC_LIST_SEG)); + if (ProcListSize > 0) + { + bcopy(ProcListVec, npv, ProcListSize * sizeof (pid_t)); + free(ProcListVec); + } + for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) + npv[i] = NO_PID; + i = ProcListSize; + ProcListSize += PROC_LIST_SEG; + ProcListVec = npv; + } + ProcListVec[i] = pid; + CurChildren++; +} + /* +** PROC_LIST_DROP -- drop pid from process list +** +** Parameters: +** pid -- pid to drop +** +** Returns: +** none. +*/ + +void +proc_list_drop(pid) + pid_t pid; +{ + int i; + + for (i = 0; i < ProcListSize; i++) + { + if (ProcListVec[i] == pid) + { + ProcListVec[i] = NO_PID; + break; + } + } + if (CurChildren > 0) + CurChildren--; +} + /* +** PROC_LIST_CLEAR -- clear the process list +** +** Parameters: +** none. +** +** Returns: +** none. +*/ + +void +proc_list_clear() +{ + int i; + + for (i = 0; i < ProcListSize; i++) + ProcListVec[i] = NO_PID; + CurChildren = 0; +} + /* +** PROC_LIST_PROBE -- probe processes in the list to see if they still exist +** +** Parameters: +** none +** +** Returns: +** none +*/ + +void +proc_list_probe() +{ + int i; + + for (i = 0; i < ProcListSize; i++) + { + if (ProcListVec[i] == NO_PID) + continue; + if (kill(ProcListVec[i], 0) < 0) + { + if (LogLevel > 3) + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "proc_list_probe: lost pid %d", + ProcListVec[i]); + ProcListVec[i] = NO_PID; + CurChildren--; + } + } + if (CurChildren < 0) + CurChildren = 0; +} + /* +** SM_STRCASECMP -- 8-bit clean version of strcasecmp +** +** Thank you, vendors, for making this all necessary. +*/ + +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +/* + * This array is designed for mapping upper and lower case letter + * together for a case independent comparison. The mappings are + * based upon ascii character sequences. + */ +static const u_char charmap[] = { + 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, + 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, + 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, + 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, + 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, + 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, + 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, +}; + +int +sm_strcasecmp(s1, s2) + const char *s1, *s2; +{ + register const u_char *cm = charmap, + *us1 = (const u_char *)s1, + *us2 = (const u_char *)s2; + + while (cm[*us1] == cm[*us2++]) + if (*us1++ == '\0') + return (0); + return (cm[*us1] - cm[*--us2]); +} + +int +sm_strncasecmp(s1, s2, n) + const char *s1, *s2; + register size_t n; +{ + if (n != 0) { + register const u_char *cm = charmap, + *us1 = (const u_char *)s1, + *us2 = (const u_char *)s2; + + do { + if (cm[*us1] != cm[*us2++]) + return (cm[*us1] - cm[*--us2]); + if (*us1++ == '\0') + break; + } while (--n != 0); + } + return (0); +} diff --git a/contrib/sendmail/src/version.c b/contrib/sendmail/src/version.c new file mode 100644 index 0000000..bf0b391 --- /dev/null +++ b/contrib/sendmail/src/version.c @@ -0,0 +1,17 @@ +/* + * Copyright (c) 1998 Sendmail, Inc. 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)version.c 8.9.1.1 (Berkeley) 7/2/98"; +#endif /* not lint */ + +char Version[] = "8.9.1"; -- cgit v1.1