Index: vendor/sendmail/dist-old/usr.sbin/sendmail/FAQ =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/FAQ (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/FAQ (revision 26986) @@ -1,9 +1,11 @@ The FAQ is no longer maintained with the sendmail release. It is posted regularly to comp.mail.sendmail, comp.mail.misc, comp.mail.smail, comp.answers, and news.answers, and can be obtained via anonymous FTP from ftp://rtfm.mit.edu/pub/usenet/news.answers/mail/sendmail-faq. If you do not have access to anonymous FTP, you can retrieve it by sending email to mail-server@rtfm.mit.edu with the command "send usenet/news.answers/mail/sendmail-faq" in the message. - --Eric Allman 8/17/96 +An HTML version is also available at http://www.sendmail.org/faq. + + --Eric Allman 14 June 1997 Index: vendor/sendmail/dist-old/usr.sbin/sendmail/KNOWNBUGS =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/KNOWNBUGS (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/KNOWNBUGS (revision 26986) @@ -1,109 +1,107 @@ K N O W N B U G S I N S E N D M A I L - (for 8.8) + (for 8.8.6) The following are bugs or deficiencies in sendmail that I am aware of but which have not been fixed in the current release. You probably -want to get the most up to date version of this from FTP.CS.Berkeley.EDU -in /ucb/sendmail/KNOWNBUGS. For descriptions of bugs that have been +want to get the most up to date version of this from ftp.sendmail.org +in /pub/sendmail/KNOWNBUGS. For descriptions of bugs that have been fixed, see the file RELEASE_NOTES (in the root directory of the sendmail distribution). This list is not guaranteed to be complete. * Null bytes are not handled properly in headers. Sendmail should handle full binary data. As it stands, it handles all values in the body, but only 0x01-0x80 and 0xA0-0xFF in the header. Notably missing is 0x00, which would require a major restructuring of the code -- for example, almost no C library support could be used to handle strings. * Duplicate error messages. Sometimes identical, duplicate error messages can be generated. As near as I can tell, this is rare and relatively innocuous. * $c (hop count) macro improperly set. The $c macro is supposed to contain the current hop count, for use when calling a mailer. This macro is initialized too early, and is always zero (or the value of the -c command line flag, if any). This macro will probably be removed entirely in a future release; I don't believe there are any mailers left that require it. * If you EXPN a list or user that has a program mailer, the output of EXPN will include ``@local.host.name''. You can't actually mail to this address. It's not clear what the right behaviour is in this circumstance. -* MX records that point at non-existent hosts work strangly. - - Consider the DNS records: - - hostH MX 1 hostA - MX 2 hostB - hostA A 128.32.8.9 - - (note that there is no A record for hostB). If hostA is down, - an attempt to send to hostH gives "host unknown" -- that is, it - reflects out the status on the last host it tries, which in this - case is hostB, which is unknown. It probably ought to eliminate - hostB early in processing. - * \231 considered harmful. Header addresses that have the \231 character (and possibly others in the range \201 - \237) behave in odd and usually unexpected ways. * accept() problem on SVR4. Apparently, the sendmail daemon loop (doing accept()s on the network) can get into a wierd state on SVR4; it starts logging ``SYSERR: getrequests: accept: Protocol Error''. The workaround is to kill and restart the sendmail daemon. We don't have an SVR4 system at Berkeley that carries more than token mail load, so I can't validate this. It is likely to be a glitch in the sockets emulation, since "Protocol Error" is not possible error code with Berkeley TCP/IP. I've also had someone report the message ``sendmail: accept: SIOCGPGRP failed errno 22'' on an SVR4 system. This message is not in the sendmail source code, so I assume it is also a bug in the sockets emulation. (Errno 22 is EINVAL "Invalid Argument" on all the systems I have available, including Solaris 2.x.) Apparently, this problem is due to linking -lc before -lsocket; if you are having this problem, check your Makefile. +* accept() problem on Linux. + + Apparently, the accept() in sendmail daemon loop can return ETIMEDOUT + and cause sendmail to sleep for 5 seconds during which time no new + connections will be accepted. An error is reported to syslog: + + Jun 9 17:14:12 hostname sendmail[207]: NOQUEUE: SYSERR(root): + getrequests: accept: Connection timed out + + "Connection timed out" is not documented as a valid return from + accept(2) and this is believed to be a bug in the Linux kernel. + * Excessive mailing list nesting can run out of file descriptors. If you have a mailing list that includes lots of other mailing lists, each of which has a separate owner, you can run out of file descriptors. Each mailing list with a separate owner uses one open file descriptor (prior to 8.6.6 it was three open file descriptors per list). This is particularly egregious if you have your connection cache set to be large. * Connection caching breaks if you pass the port number as an argument. If you have a definition such as: Mport, P=[IPC], F=kmDFMuX, S=11/31, R=21, M=2100000, T=DNS/RFC822/SMTP, A=IPC [127.0.0.1] $h (i.e., where $h is the port number instead of the host name) the connection caching code will break because it won't notice that two messages addressed to different ports should use different connections. * ESMTP SIZE underestimates the size of a message Sendmail makes no allowance for headers that it adds, nor does it account for the SMTP on-the-wire \r\n expansion. It probably doesn't allow for 8->7 bit MIME conversions either. -(Version 8.23, last updated 10/15/96) +(Version 8.25, last updated 6/13/97) Index: vendor/sendmail/dist-old/usr.sbin/sendmail/READ_ME =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/READ_ME (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/READ_ME (revision 26986) @@ -1,298 +1,298 @@ /*- - * @(#)READ_ME 8.29 (Berkeley) 9/24/96 + * @(#)READ_ME 8.30 (Berkeley) 5/8/97 */ SENDMAIL RELEASE 8 This directory has the latest sendmail software from Berkeley. See doc/changes/changes.me for a summary of changes since 5.67. Report any bugs to sendmail-bugs@sendmail.ORG There is a web site at http://WWW.Sendmail.ORG -- see that site for the latest updates. ****************************************************************** ** DO NOT USE MAKE to compile sendmail. Instead, cd src and ** ** use the "makesendmail" shell script. On many environments ** ** this will do everything for you, no fuss, no muss. See ** ** src/READ_ME for more details of compilation. See cf/README ** ** for details about building a runtime configuration file. ** ****************************************************************** +--------------+ | MANUAL PAGES | +--------------+ The sendmail manual pages use contemporary Berkeley troff macros. If your system does not process these manual pages, you can pick up the new macros in a BSD Net/2 FTP site (e.g. on FTP.UU.NET, the files /systems/unix/bsd-sources/share/tmac/me/strip.sed and /systems/unix/bsd-sources/share/tmac/*). The strip.sed file is only used in installation. After installation, edit tmac.doc and tmac.andoc to reflect the installation path of the tmac files. Those files contain pointers to /usr/share/tmac/, and those pointers are not changed by the `make install` process. There's also a bug in those files -- make the following patch: *** tmac.an~ Tue Jul 12 14:29:09 1994 --- tmac.an Fri Jul 15 13:17:54 1994 *************** *** 50,55 **** .de TH .rn TH xX .so /usr/share/lib/tmac/tmac.an.old ! .TH \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 .rm xX .. --- 50,55 ---- .de TH .rn TH xX .so /usr/share/lib/tmac/tmac.an.old ! .TH "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" .rm xX .. Rename the existing tmac.an to be tmac.an.old, and rename tmac.andoc to be tmac.an. tmac.an will choose between tmac.an.old, your old macros, or tmac.doc, which are the new macros, so that both the new man pages and the existing man pages will be translated properly. I'm also told that the groff distribution from MIT has a tmac.doc macro set that is compatible with these macros. +-----------------------+ | RELATED DOCUMENTATION | +-----------------------+ There are other files you should read. Rooted in this directory are: CHANGES-R5-R8 Describes changes between Release 5 and Release 8 of sendmail. There are some things that may behave somewhat differently. For example, the rules governing when :include: files will be read have been tightened up for security reasons. FAQ Answers to Frequently Asked Questions. KNOWNBUGS Known bugs in the current release. I try to keep this up to date -- get the latest version from FTP.CS.Berkeley.EDU in /ucb/sendmail/KNOWNBUGS. RELEASE_NOTES A detailed description of the changes in each version. This is quite long, but informative. src/READ_ME Details on compiling and installing sendmail. cf/README Details on configuring sendmail. doc/op/op.me The sendmail Installation & Operations Guide. Be warned: if you are running this off on SunOS or some other system with an old version of -me, you need to add the following macro to the macros: .de sm \s-1\\$1\\s0\\$2 .. This sets a word in a smaller pointsize. +--------------+ | RELATED RFCS | +--------------+ There are several related RFCs that you may wish to read -- they are available via anonymous FTP to several sites, including nic.ddn.mil (directory rfc), ftp.nisc.sri.com (rfc), nis.nsf.net (RFC), nisc.jvnc.net (rfc), venera.isi.edu (in-notes), and wuarchive.wustl.edu (info/rfc). They can also be retrieved via electronic mail by sending email to one of: mail-server@nisc.sri.com Put "send rfcNNN" in message body nis-info@nis.nsf.net Put "send RFCnnn.TXT-1" in message body sendrfc@jvnc.net Put "RFCnnn" as Subject: line Important RFCs for electronic mail are: RFC821 SMTP protocol RFC822 Mail header format RFC974 MX routing RFC976 UUCP mail format RFC1123 Host requirements (modifies 821, 822, and 974) RFC1413 Identification server RFC1869 SMTP Service Extensions (ESMTP spec) RFC1652 SMTP Service Extension for 8bit-MIMEtransport RFC1870 SMTP Service Extension for Message Size Declaration RFC1521 MIME: Multipurpose Internet Mail Extensions RFC1344 Implications of MIME for Internet Mail Gateways RFC1428 Transition of Internet Mail from Just-Send-8 to 8-bit SMTP/MIME RFC1891 SMTP Service Extension for Delivery Status Notifications RFC1892 Multipart/Report Content Type for the Reporting of Mail System Administrative Messages RFC1893 Enhanced Mail System Status Codes RFC1894 An Extensible Message Format for Delivery Status Notifications RFC1985 SMTP Service Extension for Remote Message Queue Starting Other standards that may be of interest (but which are less directly relevant to sendmail) are: RFC987 Mapping between RFC822 and X.400 RFC1049 Content-Type header field (extension to RFC822) Warning to AIX users: this version of sendmail does not implement MB, MR, or MG DNS resource records, as defined (as experiments) in RFC1035. +-------------------+ | DATABASE ROUTINES | +-------------------+ IF YOU WANT TO RUN THE NEW BERKELEY DB SOFTWARE: **** DO NOT **** use the version that was on the Net2 tape -- it has a number of nefarious bugs that were bad enough when I got them; you shouldn't have to go through the same thing. Instead, get a new version via public -FTP from ftp.CS.Berkeley.EDU, file ucb/4bsd/db.tar.Z. This software -is highly recommended; it gets rid of several stupid limits, it's much -faster, and the interface is nicer to animals and plants. You will +FTP from ftp.sleepycat.com, file db/packages/db.1.85.tar.gz. This +software is highly recommended; it gets rid of several stupid limits, it's +much faster, and the interface is nicer to animals and plants. You will also probably find that you have to add -I/where/you/put/db/include to the sendmail makefile to get db.h to work properly. Be sure you remove ndbm.h and ndbm.o from the db distribution. These will cause problems with sendmail because sendmail already understands about NEWDB and NDBM coexisting. +--------------------+ | HOST NAME SERVICES | +--------------------+ If you are using NIS or /etc/hosts, it is critical that you list the long (fully qualified) name somewhere (preferably first) in the /etc/hosts file used to build the NIS database. For example, the line should read 128.32.149.68 mastodon.CS.Berkeley.EDU mastodon **** NOT **** 128.32.149.68 mastodon If you do not include the long name, sendmail will complain loudly about ``unable to qualify my own domain name (mastodon) -- using short name'' and conclude that your canonical name is the short version and use that in messages. The name "mastodon" doesn't mean much outside of Berkeley, and so this creates incorrect and unreplyable messages. +-------------+ | USE WITH MH | +-------------+ This version of sendmail notices and reports certain kinds of SMTP protocol violations that were ignored by older versions. If you are running MH you may wish to install the patch in contrib/mh.patch that will prevent these warning reports. This patch also works with the old version of sendmail, so it's safe to go ahead and install it. +----------------+ | USE WITH IDENT | +----------------+ Sendmail 8 supports the IDENT protocol, as defined by RFC 1413. No ident server is included with this distribution. I have found copies available on: ftp.lysator.liu.se /pub/ident/servers romulus.ucs.uoknor.edu /networking/ident/servers ftp.cyf-kr.edu.pl /agh/uciagh/network/ident If you want to run an IDENT server, I suggest getting a copy from one of those sites. Versions are available for several different systems, including Apollo, BSD, NeXT, AIX, TOPS20, and VMS. +-----------+ | MAKEFILES | +-----------+ The Makefiles in this release use the new Berkeley "make" that is available in BSD Net/2 and 4.4BSD. If you are using this version of make, you may notice one or two places where the Makefile includes "../../Makefile.inc". This file is not included with the sendmail distribution because it's not part of sendmail. However, it is, in toto: # @(#)Makefile.inc 8.1 (Berkeley) 6/6/93 BINDIR?= /usr/sbin The other directories should all have Makefile.dist files that work on the old make, albeit without all the niceties included. You can also get a new Berkeley make from the Net2 release (available on many public FTP archives). This version should also interpret old Makefiles, so you could drop it in as your default make. For more details, see src/READ_ME. +---------------------+ | DIRECTORY STRUCTURE | +---------------------+ The structure of this directory tree is: cf Source for Berkeley configuration files. These are different than what you've seen before. They are a fairly dramatic rewrite, requiring the new sendmail (since they use new features). contrib Some contributed tools to help with sendmail. THESE ARE NOT SUPPORTED by Berkeley -- contact the original authors if you have problems. (This directory is not on the 4.4BSD tape.) doc Documentation. If you are getting source, read op.me -- it's long, but worth it. mail.local The source for the local delivery agent used for 4.4BSD. THIS IS NOT PART OF SENDMAIL! and may not compile everywhere, since it depends on some 4.4-isms. Warning: it does mailbox locking differently than other systems. mailstats Statistics printing program. It has the pathname of sendmail.st compiled in, so if you've changed that, beware. This isn't all that useful. makemap A program that creates the keyed maps used by the $( ... $) construct in sendmail. It is primitive but effective. It takes a very simple input format, so you will probably expect to preprocess must human-convenient formats using sed scripts before this program will like them. But it should be functionally complete. praliases A program to print the DBM or NEWDB version of the aliases file. rmail Source for rmail(8). This is used as a delivery agent for for UUCP, and could presumably be used by other non-socket oriented mailers. Older versions of rmail are probably deficient. RMAIL IS NOT PART OF SENDMAIL!!! The 4.4BSD source is included for you to look at or try to port to your system. I know it doesn't compile on {SunOS, HP-UX, OSF/1, other} (pick one). smrsh The "sendmail restricted shell", which can be used as a replacement for /bin/sh in the prog mailer to provide increased security control. NOT PART OF SENDMAIL! src Source for the sendmail program itself. test Some test scripts (currently only for compilation aids). Index: vendor/sendmail/dist-old/usr.sbin/sendmail/RELEASE_NOTES =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/RELEASE_NOTES (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/RELEASE_NOTES (revision 26986) @@ -1,5127 +1,5428 @@ SENDMAIL RELEASE NOTES - @(#)RELEASE_NOTES 8.8.5.3 (Berkeley) 1/21/97 + @(#)RELEASE_NOTES 8.8.6.11 (Berkeley) 6/14/97 This listing shows the version of the sendmail binary, the version of the sendmail configuration files, the date of release, and a summary of the changes in that release. +8.8.6/8.8.6 97/06/14 + ************************************************************* + * The extensive assistance of Gregory Neil Shapiro of WPI * + * in preparing this release is gratefully appreciated. * + * Sun Microsystems has also provided resources toward * + * continued sendmail development. * + ************************************************************* + SECURITY: A few systems allow an open with the O_EXCL|O_CREAT open + mode bits set to create a file that is a symbolic link that + points nowhere. This makes it possible to create a root + owned file in an arbitrary directory by inserting the symlink + into a writable directory after the initial lstat(2) check + determined that the file did not exist. The only verified + example of a system having these odd semantics for O_EXCL + and symbolic links was HP-UX prior to version 9.07. Most + systems do not have the problem, since a exclusive create + of a file disallows symbolic links. Systems that have been + verified to NOT have the problem include AIX 3.x, *BSD, + DEC OSF/1, HP-UX 9.07 and higher, Linux, SunOS, Solaris, + and Ultrix. This is a potential exposure on systems that + have this bug and which do not have a MAILER-DAEMON alias + pointing at a legitimate account, since this will cause old + mail to be dropped in /var/tmp/dead.letter. + SECURITY: Problems can occur on poorly managed systems, specifically, + if maps or alias files are in world writable directories. + If your system has alias maps in writable directories, it + is potentially possible for an attacker to replace the .db + (or .dir and .pag) files by symbolic links pointing at + another database; this can be used either to expose + information (e.g., by pointing an alias file at /etc/spwd.db + and probing for accounts), or as a denial-of-service attack + (by trashing the password database). The fix disallows + symbolic links entirely when rebuilding alias files or on + maps that are in writable directories, and always warns on + writable directories; 8.9 will probably consider writable + directories to be fatal errors. This does not represent an + exposure on systems that have alias files in unwritable + system directories. + SECURITY: disallow .forward or :include: files that are links (hard + or soft) if the parent directory (or any directory in the + path) is writable by anyone other than the owner. This is + similar to the previous case for user files. This change + should not affect most systems, but is necessary to prevent + an attacker who can write the directory from pointing such + files at other files that are readable only by the owner. + SECURITY: Tighten safechown rules: many systems will say that they + have a safe (restricted to root) chown even on files that + are mounted from another system that allows owners to give + away files. The new rules are very strict, trusting file + ownership only in those few cases where the system has + been verified to be at least as paranoid as necessary. + However, it is possible to relax the rules to partially + trust the ownership if the directory path is not world or + group writable. This might allow someone who has a legitimate + :include: file (referenced directly from /etc/aliases) to + become another non-root user if the :include: file is in a + non-writable directory on an NFS-mounted filesystem where + the local system says that giveaway is denied but it is + actually permitted. I believe this to be a very small set + of cases. If in doubt, do not point :include: aliases at + NFS-mounted filesystems. + SECURITY: When setting a numeric group id using the RunAsUser option + (e.g., "O RunAsUser=10:20", the group id would not be set. + Implicit group ids (e.g., "O RunAsUser=mailnull") or alpha + group ids (e.g., "O RunAsUser=mailuser:mailgrp") worked fine. + The user id was still set properly. Problem noted by Uli + Pralle of the Technical University of Berlin. + Save the initial gid set for use when checking for if the + PrivacyOptions=restrictmailq option is set. Problem reported + by Wolfgang Ley of DFN-CERT. + Make 55x reply codes to the SMTP DATA-"." be non-sticky (i.e., a + failure on one message won't affect future messages to the + same host). + IP source route printing had an "off by one" error that would + affect any options that came after the route option. Patch + from Theo de Raadt. + The "Message is too large" error didn't successfully bounce the error + back to the sender. Problem reported by Stephen More of + PSI; patch from Gregory Neil Shapiro of WPI. + Change SMTP status code 553 to map into Extended code 5.1.0 (instead + of 5.1.3); it apparently gets used in multiple ways. + Suggested by John Myers of Portola Communications. + Fix possible extra null byte generated during collection if errors + occur at the beginning of the stream. Patch contributed by + Andrey A. Chernov and Gregory Neil Shapiro. + Code changes to avoid possible reentrant call of malloc/free within + a signal handler. Problem noted by John Beck of Sun + Microsystems. + Move map initialization to be earlier so that check_relay ruleset + will have the latest version of the map data. Problem noted + by Paul Forgey of Metainfo; patch from Gregory Neil Shapiro. + If there are fatal errors during the collection phase (e.g., message + too large) don't send the bogus message. + Avoid "cannot open xfAAA00000" messages when sending to aliases that + have errors and have owner- aliases. Problem noted by Michael + Barber of MTU; fix from Gregory Neil Shapiro of WPI. + Avoid null pointer dereference on illegal Boundary= parameters in + multipart/mixed Content-Type: header. Problem noted by + Richard Muirden of RMIT University. + Always print error messages during newaliases (-bi) even if the + ErrorMode is not set to "print". Fix from Gregory Neil + Shapiro. + Test mode could core dump if you did a /map lookup in an optional map + that could not be opened. Based on a fix from John Beck of + Sun Microsystems. + If DNS is misconfigured so that the last MX record tried points to + a host that does not have an A record, but other MX records + pointed to something reasonable, don't bounce the message + with a "host unknown" error. Note that this should really + be fixed in the zone file for the domain. Problem noted by + Joe Rhett of Navigist, Inc. + If a map fails (e.g., DNS times out) on all recipient addresses, mark + the message as having been tried; otherwise the next queue + run will not realize that this is a second attempt and will + retry immediately. Problem noted by Bryan Costales of + Mercury Mail. + If the clock is set backwards, and a MinQueueAge is set, no jobs + will be run until the later setting of the clock is reached. + "Problem" (I use the term loosely) noted by Eric Hagberg of + Morgan Stanley. + If the load average rises above the cutoff threshold (above which + sendmail will not process the queue at all) during a queue + run, abort the queue run immediately. Problem noted by + Bryan Costales of Mercury Mail. + The variable queue processing algorithm (based on the message size, + number of recipients, message precedence, and job age) was + non-functional -- either the entire queue was processed or + none of the queue was processed. The updated algorithm + does no queue run if a single recipient zero size job will + not be run. + If there is a fatal ("panic") message that will cause sendmail to + die immediately, never hold the error message for future + printing. + Force ErrorMode=print in -bt mode so that all errors are printed + regardless of the setting of the ErrorMode option in the + configuration file. Patch from Gregory Neil Shapiro. + New compile flag HASSTRERROR says that this OS has the strerror(3) + routine available in one of the libraries. Use it in conf.h. + The -m (match only) flag now works on host class maps. + If class hash or btree maps are rebuilt, sendmail will now detect + this and reopen the map. Previously, they could give + erroneous results during a single message processing + (but would recover when the next message was received). + Don't delete zero length queue files when doing queue runs until the + files are at least ten minutes old. This avoids a potential + race condition: the creator creates the qf file, getting back + a file descriptor. The queue runner locks it and deletes it + because it is zero length. The creator then writes the + descriptor that is now for a disconnected file, and the + job goes away. Based on a suggestion by Bryan Costales. + When determining the "validated" host name ($_ macro), do a forward + (A) DNS lookup on the result of the PTR lookup and compare + results. If they differ or if the PTR lookup fails, tag the + address as "may be forged". + Log null connections (i.e., hosts that connect but do not do any + substantive activity on the connection before disconnecting; + "substantive" is defined to be MAIL, EXPN, VRFY, or ETRN. + Always permit "writes" to /dev/null regardless of the link count. + This is safe because /dev/null is special cased, and no open + or write is ever actually attempted. Patch from Villy Kruse + of TwinCom. + If a message cannot be sent because of a 552 (exceeded storage + allocation) response to the MAIL FROM:<>, and a SIZE= parameter + was given, don't return the body in the bounce, since there + is a very good chance that the message will double-bounce. + Fix possible line truncation if a quoted-printable had an =00 escape + in the body. Problem noted by Charles Karney of the Princeton + Plasma Physics Laboratory. + Notify flags (e.g., -NSUCCESS) were lost on user+detail addresses. + Problem noted by Kari Hurtta of the Finnish Meteorological + Institute. + The MaxDaemonChildren option wasn't applying to queue runs as + documented. Note that this increases the potential denial + of service problems with this option: an attacker can + connect many times, and thereby lock out queue runs as well + as incoming connections. If you use this option, you should + run the "sendmail -bd" and "sendmail -q30m" jobs separately + to avoid this attack. Failure to limit noted by Matthew + Dillon of BEST Internet Communications. + Always give a message in newaliases if alias files cannot be + opened instead of failing silently. Suggested by Gregory + Neil Shapiro. This change makes the code match the O'Reilly + book (2nd edition). + Some older versions of the resolver could return with h_errno == -1 + if no name server could be reached, causing mail to bounce + instead of queueing. Treat this like TRY_AGAIN. Fix from + John Beck of SunSoft. + If a :include: file is owned by a user that does not have an entry + in the passwd file, sendmail could dereference a null pointer. + Problem noted by Satish Mynam of Sun Microsystems. + Take precautions to make sure that the SMTP protocol cannot get out + of sync if (for example) an alias file cannot be opened. + Fix a possible race condition that can cause a SIGALRM to come in + immediately after a SIGHUP, causing the new sendmail to die. + Avoid possible hang on SVr3 systems when doing child reaping. Patch + from Villy Kruse of TwinCom. + Ignore improperly formatted SMTP reply codes. Previously these were + partially processed, which could cause confusing error + returns. + Fix possible bogus pointer dereference when doing ldapx map lookups + on some architectures. + Portability: + A/UX: from Jim Jagielski of NASA/GSFC. + glibc: SOCK_STREAM was changed from a #define to an enum, + thus breaking #ifdef SOCK_STREAM. Only option seems + to be to assume SOCK_STREAM if __GNU_LIBRARY__ is + defined. Problem reported by A Sun of the University + of Washington. + Solaris: use SIOCGIFNUM to get the number of interfaces on + the system rather than guessing at compile time. + Patch contributed by John Beck of Sun Microsystems. + Intel Paragon: from Wendy Lin of Purdue University. + GNU Hurd: from Miles Bader of the GNU project. + RISC/os 4.50 from Harlan Stenn of PFCS Corporation. + ISC Unix: wait never returns if SIGCLD signals are blocked. + Unfortunately releasing them opens a race condition, + but there appears to be no fix for this. Patch from + Gregory Neil Shapiro. + BIND 8.1 for IPv6 compatibility from John Kennedy. + Solaris: a bug in strcasecmp caused characters with the + high order bit set to apparently randomly match + letters -- for example, $| (0233) matches "i" and "I". + Problem noted by John Gregson of the University of + Cambridge. + IRIX 6.x: make Makefile.IRIX.6.2 apply to all 6.x. From + Kari Hurtta. + IRIX 6.x: Create Makefiles for systems that claim to be + IRIX64 but are 6.2 or higher (so use the regular + IRIX Makefile). + IRIX 6.x: Fix load average computation on 64 bit kernels. + Problem noted by Eric Hagberg of Morgan Stanley. + CONFIG: Some canonification was still done for UUCP-like addresses + even if FEATURE(nocanonify) was set. Problem pointed out by + Brian Candler. + CONFIG: In some cases UUCP mailers wouldn't properly recognize all + local names as local. Problem noted by Jeff Polk of BSDI; + fix provided by Gregory Neil Shapiro. + CONFIG: The "local:user" syntax entries in mailertables and other + "mailer:user" syntax locations returned an incorrect value + for the $h macro. Problem noted by Gregory Neil Shapiro. + CONFIG: Retain "+detail" information when forwarding mail to a + MAIL_HUB, LUSER_RELAY, or LOCAL_RELAY. Patch from Philip + Guenther of Gustavus Adolphus College. + CONFIG: Make sure user+detail works for FEATURE(virtusertable); + rules are the same as for aliasing. Based on a patch from + Gregory Neil Shapiro. + CONFIG: Break up parsing rules into several pieces; this should + have no functional change in this release, but makes it + possible to have better anti-spam rulesets in the future. + CONFIG: Disallow double dots in host names to avoid having the + HostStatusDirectory store status under the wrong name. + In some cases this can be used as a denial-of-service attack. + Problem noted by Ron Jarrell of Virginia Tech, patch from + Gregory Neil Shapiro. + CONFIG: Don't use F=m (multiple recipients per invocation) for + MAILER(procmail), but do pass F=Pn9 (include Return-Path:, + don't include From_, and convert to 8-bit). Suggestions + from Kimmo Suominen and Roderick Schertler. + CONFIG: Domains under $=M (specified with MASQUERADE_DOMAIN) where + being masqueraded as though FEATURE(masquerade_entire_domain) + was specified, even when it wasn't. + MAIL.LOCAL: Solaris 2.6 has snprintf. From John Beck of SunSoft. + MAIL.LOCAL: SECURITY: check to make sure that an attacker doesn't + "slip in" a symbolic link between the lstat(2) call and the + exclusive open. This is only a problem on System V derived + systems that allow an exclusive create on files that are + symbolic links pointing nowhere. + MAIL.LOCAL: If the final mailbox close() failed, the user id was + not reset back to root, which on some systems would cause + later mailboxes to fail. Also, any partial message would + not be truncated, which could result in repeated deliveries. + Problem noted by Bruce Evans via Peter Wemm (FreeBSD + developers). + MAKEMAP: Handle cases where O_EXLOCK is #defined to be 0. A similar + change to the sendmail map code was made in 8.8.3. Problem + noted by Gregory Neil Shapiro. + MAKEMAP: Give warnings on file problems such as map files that are + symbolic links; although makemap is not setuid root, it is + often run as root and hence has the potential for the same + sorts of problems as alias rebuilds. + MAKEMAP: Change compilation so that it will link properly on + NEXTSTEP. + CONTRIB: etrn.pl: search for Cw as well as Fw lines in sendmail.cf. + Accept an optional list of arguments following the server + name for the ETRN arguments to use (instead of $=w). Other + miscellaneous bug fixes. From Christian von Roques via + John Beck of Sun Microsystems. + CONTRIB: Add passwd-to-alias.pl, contributed by Kari Hurtta. This + Perl script converts GECOS information in the /etc/passwd + file into aliases, allowing for faster access to full name + lookups; it is also clever about adding aliases (to root) + for system accounts. + NEW FILES: + src/safefile.c + cf/ostype/gnuhurd.m4 + cf/ostype/irix6.m4 + contrib/passwd-to-alias.pl + test/t_exclopen.c + src/Makefiles/Makefile.IRIX64.6.1 + src/Makefiles/Makefile.IRIX64.6.x + RENAMED FILES: + src/Makefiles/Makefile.IRIX.6.2 => Makefile.IRIX.6.x + src/Makefiles/Makefile.IRIX64 => Makefile.IRIX64.6.0 + 8.8.5/8.8.5 97/01/21 SECURITY: Clear out group list during startup. Without this, sendmail will continue to run with the group permissions of the caller, even if RunAsUser is specified. SECURITY: Make purgestat (-bH) be root-only. This is not in response to any known attack, but it's best to be conservative. Suggested by Peter Wemm of DIALix. SECURITY: Fix buffer overrun problem in MIME code that has possible security implications. Patch from Alex Garthwaite of the University of Pennsylvania. Use of a -f flag with a phrase attached (e.g., "-f 'Full Name '") would truncate the address after "Full". Although the -f syntax is incorrect (since it is in the envelope, it shouldn't have comments and full names), the failure mode was unnecessarily awful. Fix a possible null pointer dereference when converting 8-bit data to a 7-bit format. Problem noted by Jim Hutchins of Sandia National Labs and David James of British Telecom. Clear out stale state that affected F=9 on SMTP mailers in queue runs. Although this really shouldn't be used (F=9 is for final delivery only, and using it on an SMTP mailer makes it possible for a message to be converted from 8->7->8->7 bits several times), it shouldn't have failed with a syserr. Problem noted by Eric Hagberg of Morgan Stanley. _Really_ fix the multiple :maildrop code in the user database module. Patch from Roy Mongiovi of Georgia Tech. Let F lines in the configuration file actually read root-only files if the configuration file is safe. Based on a patch from Keith Reynolds of SCO. ETRN followed by QUIT would hold the connection open until the queue run completed. Problem noted by Truck Lewis of TDK Semiconductor Corp. It turns out that despite the documentation, the TCP wrappers library does _not_ log rejected connections. Do the logging ourselves. Problem noted by Fletcher Mattox of the University of Texas at Austin. If sendmail finds a qf file in its queue directory that is an unknown version (e.g., when backing out to an old version), the error is reported on every queue run. Change it to only give the error once (and rename the qf => Qf). Patch from William A. Gianopoulos of Raytheon Company. Start a new session when doing background delivery; currently it ignored signals but didn't start a new signal, that caused some problems if a background process tried to send mail under certain circumstances. Problem noted by Eric Hagberg of Morgan Stanley; fix from Kari Hurtta. Simplify test for skipping a queue run to just check if the current load average is >= the queueing load average. Previously the check factored in some other parameters that caused it to essentially never skip the queue run. Patch from Bryan Costales. If the SMTP server is running in "nullserver" mode (that is, it is rejecting all commands), start sleeping after MAXBADCOMMAND (25) commands; this helps prevent a bad guy from putting you into a tight loop as a denial-of-service attack. Based on an e-mail conversation with Brad Knowles of AOL. Slow down when too many "light weight" commands have been issued; this helps prevent a class of denial-of-service attacks. The current values and defaults are: MAXNOOPCOMMANDS 20 NOOP, VERB, ONEX, XUSR MAXHELOCOMMANDS 3 HELO, EHLO MAXVRFYCOMMANDS 6 VRFY, EXPN MAXETRNCOMMANDS 8 ETRN These will probably be configurable in a future release. On systems that have uid_t typedefed to be an unsigned short, programs that had the F=S flag and no U= equate would be invoked with the real uid set to 65535 rather than being left unchanged. In some cases, NOTIFY=NEVER was not being honored. Problem noted by Steve Hubert of the University of Washington, Seattle. Mail that was Quoted-Printable encoded and had a soft line break on the last line (i.e., an incomplete continuation) had the last line dropped. Since this appears to be illegal it isn't clear what to do with it, but flushing the last line seems to be a better "fail soft" approach. Based on a patch from Eric Hagberg. If AllowBogusHELO and PrivacyOptions=needmailhelo are both set, a bogus HELO command still causes the "Polite people say HELO first" error message. Problem pointed out by Chris Thomas of UCLA; patch from John Beck of SunSoft. Handle "sendmail -bp -qSfoobar" properly if restrictqrun is set in PrivacyFlags. The -q shouldn't turn this command off. Problem noted by Murray Kucherawy of Pacific Bell Internet; based on a patch from Gregory Neil Shapiro of WPI. Don't consider SMTP reply codes 452 or 552 (exceeded storage allocation) in a DATA transaction to be sticky; these can occur because a message is too large, and smaller messages should still go through. Problem noted by Matt Dillon of Best Internet Communications. In some cases bounces were saved in /var/tmp/dead.letter even if they had been successfully delivered to the envelope sender. Problem noted Eric Hagberg of Morgan Stanley; solution from Gregory Neil Shapiro of WPI. Give better diagnostics on long alias lines. Based on code contributed by Patrick Gosling of the University of Cambridge. Increase the number of virtual interfaces that will be probed for - alternate names. Problem noted by Gregory Neil Shapiro of - WPI. + alternate names. Problem noted by Amy Rich of Shore.Net. PORTABILITY: UXP/DS V20L10 for Fujitsu DS/90: Makefile patches from Toshiaki Nomura of Fujitsu Limited. SunOS with LDAP support: compile problems with struct timeval. Patch from Nick Cuccia of TCSI Corporation. SCO: from Keith Reynolds of SCO. Solaris: kstat load average computation wasn't being used. Fixes from Michael Ju. Tokarev of Telecom Service, JSC (Moscow). OpenBSD: from Jason Downs of teeny.org. Altos System V: from Tim Rice. Solaris 2.5: from Alan Perry of SunSoft. Solaris 2.6: from John Beck of SunSoft. Harris Nighthawk PowerUX (mh6000 box): from Bob Miorelli of Pratt & Whitney . CONFIG: It seems that I hadn't gotten the Received: line syntax _just_right_ yet. Tweak it again. I'll omit the names of the "contributors" (quantity two) in this one case. As of now, NO MORE DISCUSSION about the syntax of the Received: line. CONFIG: Although FEATURE(nullclient) uses EXPOSED_USER (class $=E), it never inserts that class into the output file. Fix it so it will honor EXPOSED_USER but will _not_ include root automatically in this class. Problem noted by Ronan KERYELL of Centre de Recherche en Informatique de l'École Nationale Supérieure des Mines de Paris (CRI-ENSMP). CONFIG: Clean up handling of "local:" syntax in relay specifications such as LUSER_RELAY. This change permits the following syntaxes: ``local:'' will send to the same user on the local machine (e.g., in a mailertable entry for "host", ``local:'' will cause an address addressed to user@host to go to user on the local machone). ``local:user'' will send to the named user on the local machine. ``local:user@host'' is equivalent to ``local:user'' (the host is ignored). In all cases, the original user@host is passed in $@ (i.e., the - detail information). Inspired by a report from Michael Fuhr - of Dimensional Communications, L.L.C. + detail information). Inspired by a report from Michael Fuhr. CONFIG: Strip quotes from the first word of an "error:" host indication. This lets you set (for example) the LUSER_RELAY to be ``error:\"5.1.1\" Your Message Here''. Note the use of the \" so that the resulting string is properly quoted. Problem noted by Gregory Neil Shapiro of WPI. OP.ME: documentation was inconsistent about whether sendmail did a NOOP or a RSET to probe the connection (it does a RSET). Inconsistency noted by Deeran Peethamparam. OP.ME: insert additional blank pages so it will print properly on a duplex printer. From Matthew Black of Cal State University, Long Beach. 8.8.4/8.8.4 96/12/02 SECURITY: under some circumstances, an attacker could get additional permissions by hard linking to files that were group writable by the attacker. The solution is to disallow any files that have hard links -- this will affect .forward, :include:, and output files. Problem noted by Terry Kyriacopoulos of Interlog Internet Services. As a workaround, set UnsafeGroupWrites -- always a good idea. SECURITY: the TryNullMXList (w) option should not be safe -- if it is, it is possible to do a denial-of-service attack on MX hosts that rely on the use of the null MX list. There is no danger if you have this option turned off (the default). Problem noted by Dan Bernstein. Also, make the DontInitGroups unsafe. I know of no specific attack against this, although a denial-of-service attack is probably possible, but in theory you should not be able to safely tweak anything that affects the permissions that are used when mail is delivered. Purgestat could go into an infinite loop if one of the host status directories somehow became empty. Problem noted by Roy Mongiovi of Georgia Tech. Processes got "lost" when counting children due to a race condition. This caused "proc_list_probe: lost pid" messages to be logged. Problem noted by several people. On systems with System V SIGCLD child signal semantics (notably AIX and HP-UX), mail transactions would print the message "451 SMTP-MAIL: lost child: No child processes". Problem noted by several people. Miscellaneous compiler warnings on picky compilers (or when setting gcc to high warning levels). From Tom Moore of NCR Corp. SMTP protocol errors, and most errors on MAIL FROM: lines should not be persistent between runs, since they are based on the message rather than the host. Problem noted by Matt Dillon of Best Internet Communications. The F=7 flag was ignored on SMTP mailers. Problem noted by Tom Moore of NCR (a.k.a., AT&T Global Information Solutions). Avoid the possibility of having a child daemon run to completion (including closing the SMTP socket) before the parent has had a chance to close the socket; this can cause the parent to hang for a long time waiting for the socket to drain. Patch from Don Lewis of TDK Semiconductor. If the fork() failed in a queue run, the queue runners would not be rescheduled (so queue runs would stop). Patch from Don Lewis. Some error conditions in ETRN could cause output without an SMTP status code. Problem noted by Don Lewis. Multiple :maildrop addresses in the user database didn't work properly. Patch from Roy Mongiovi of Georgia Tech. Add ".db" automatically onto any user database spec that does not already have it; this is for consistency with makemap, the K line, and the documentation. Inconsistency pointed out by Roy Mongiovi. Allow sendmail to be properly called in nohup mode. Patch from Kyle Jones of UUNET. Change ETRN to ignore but still update host status files; previously it would ignore them and not save the updated status, which caused stale information to be maintained. Based on a patch from Christopher Davis of Kapor Enterprises Inc. Also, have ETRN ignore the MinQueueAge option. Patch long term host status to recover more gracefully from an empty host status file condition. Patch from NAKAMURA Motonori of Kyoto University. Several patches to signal handling code to fix potential race conditions from Don Lewis. Make it possible to compile with -DDAEMON=0 (previously it had some compile errors). This turns DAEMON, QUEUE, and SMTP into 0/1 compilation flags. Note that DAEMON is an obsolete compile flag; use NETINET instead. Solution based on a patch from Bryan Costales. PORTABILITY FIXES: AIX4: getpwnam() and getpwuid() do a sequential scan of the /etc/security/passwd file when called as root. This is very slow on some systems. To speed it up, use the (undocumented) _getpw{nam,uid}_shadow() routines. Patch from Chris Thomas of UCLA/OAC Systems Group. SCO 5.x: include -lprot in the Makefile. Patch from Bill Glicker of Burrelle's Information Service. NEWS-OS 4.x: need a definition for MODE_T to compile. Patch from Makoto MATSUSHITA of Osaka University. SunOS 4.0.3: compile problems. Patches from Andrew Cole of Leeds University and SASABE Tetsuro of the University of Tokyo. DG/UX 5.4.4.11 from Brian J. Murrell of InterLinx Support Services, Inc. Domain/OS from Don (Truck) Lewis of TDK Semiconductor Corp. I believe this to have only been a problem if you compiled with -DUSE_VENDOR_CF_PATH -- another reason to stick with /etc/sendmail.cf as your One True Path. Digital UNIX (OSF/1 on Alpha) load average computation from Martin Laubach of the Technischen Universität Wien. CONFIG: change default Received: line to be multiple lines rather than one long one. By popular demand. MAIL.LOCAL: warnings weren't being logged on some systems. Patch from Jerome Berkman of U.C. Berkeley. MAKEMAP: be sure to zero hinfo to avoid cruft that can cause runs to take a very long time. Problem noted by Yoshiro YONEYA of NTT Software Corporation. CONTRIB: add etrn.pl, contributed by John Beck. NEW FILES: contrib/etrn.pl 8.8.3/8.8.3 96/11/17 SECURITY: it was possible to get a root shell by lying to sendmail about argv[0] and then sending it a signal. Problem noted by Leshka Zakharoff on the best-of-security list. Log sendmail binary version number in "Warning: .cf version level (%d) exceeds program functionality (%d) message" -- this should make it clearer to people that they are running the wrong binary. Fix a problem that occurs when you open an SMTP connection and then do one or more ETRN commands followed by a MAIL command; at the end of the DATA phase sendmail would incorrectly report "451 SMTP-MAIL: lost child: No child processes". Problem noted by Eric Bishop of Virginia Tech. When doing text-based host canonification (typically /etc/hosts lookup), a null host name would match any /etc/hosts entry with space at the end of the line. Problem noted by Steve Hubert of the University of Washington, Seattle. 7 to 8 bit BASE64 MIME conversions could duplicate bits of text. Problem reported by Tom Smith of Digital Equipment Corp. Increase the size of the DNS answer buffer -- the standard UDP packet size PACKETSZ (512) is not sufficient for some nameserver answers containing very many resource records. The resolver may also switch to TCP and retry if it detects UDP packet overflow. Also, allow for the fact 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. Patch from Eric Wassenaar. Improvements to MaxDaemonChildren code. If you think you have too many children, probe the ones you have to verify that they are still around. Suggested by Jared Mauch of CICnet, Inc. Also, do this probe before growing the vector of children pids; this previously caused the vector to grow indefinitely due to a race condition. Problem reported by Kyle Jones of UUNET. On some architectures, (from the Berkeley DB library) defines O_EXLOCK to zero; this fools the map compilation code into thinking that it can avoid race conditions by locking on open. Change it to check for O_EXLOCK non-zero. Problem noted by Leif Erlingsson of Data Lege. Always call res_init() on startup (if compiled in, of course) to allow the sendmail.cf file to tweak resolver flags; without it, flag tweaks in ResolverOptions are ignored. Patch from Andrew Sun of Merrill Lynch. Improvements to host status printing code. Suggested by Steve Hubert of the University of Washington, Seattle. Change MinQueueAge option processing to do the check for the job age when reading the queue file, rather than at the end; this avoids parsing the addresses, which can do DNS lookups. Problem noted by John Beck of InReference, Inc. When MIME was being 7->8 bit decoded, "From " lines weren't being properly escaped. Problem noted by Peter Nilsson of the University of Linkoping. In some cases, sendmail would retain root permissions during queue runs even if RunAsUser was set. Problem noted by Mark Thomas of Mark G. Thomas Consulting. If the F=l flag was set on an SMTP mailer to indicate that it is actually local delivery, and NOTIFY=SUCCESS is specified in the envelope, and the receiving SMTP server speaks DSN, then the DSN would be both generated locally and propogated to the other end. The U= mailer field didn't correctly extract the group id if the user id was numeric. Problem noted by Kenneth Herron of MCI Telecommunications Communications. If a message exceeded the fixed maximum size on input, the body of the message was included in the bounce. Note that this did not occur if it exceeded the maximum _output_ size. Problem reported by Kyle Jones of UUNET. PORTABILITY FIXES: AIX4: 4.1 does't have a working setreuid(2); change the AIX4 defines to use seteuid(2) instead, which works on 4.1 as well as 4.2. Problem noted by Håkan Lindholm of interAF, Sweden. AIX4: use tzname[] vector to determine time zone name. Patch from NAKAMURA Motonori of Kyoto University. MkLinux: add Makefile.Linux.ppc and OSTYPE(mklinux) support. Contributed by Paul DuBois . Solaris: kstat(3k) support for retrieving the load average. This adds the LA_KSTAT definition for LA_TYPE. The outline of the implementation was contributed by Michael Tokarev of Telecom Service, JSC, Moscow. HP-UX 10.0 gripes about the (perfectly legal!) forward declaration of struct rusage at the top of conf.h; change it to only be included if you are using gcc, which is apparently the only compiler that requires it in the first place. Problem noted by Jeff Earickson of Colby College. IRIX: don't default to using gcc. IRIX is a civilized operating system that comes with a decent compiler by default. Problem noted by Barry Bouwsma and Kari Hurtta. CONFIG: specify F=9 as default in FEATURE(local_procmail) for consistency with other local mailers. Inconsistency pointed out by Teddy Hogeborn . CONFIG: if the "limited best mx" feature is used (to reduce DNS overhead) as part of the bestmx_is_local feature, the domain part was dropped from the name. Patch from Steve Hubert of the University of Washington, Seattle. CONFIG: catch addresses of the form "user@.dom.ain"; these could end up being translated to the null host name, which would return any entry in /etc/hosts that had a space at the end of the line. Problem noted by Steve Hubert of the University of Washington, Seattle. CONFIG: add OSTYPE(aix4). From Michael Sofka of Rensselaer Polytechnic Institute. MAKEMAP: tweak hash and btree parameters for better performance. Patch from Matt Dillon of Best Internet Communications. NEW FILES: src/Makefiles/Makefile.Linux.ppc cf/ostype/aix4.m4 cf/ostype/mklinux.m4 8.8.2/8.8.2 96/10/18 SECURITY: fix a botch in the 7-bit MIME patch; the previous patch changed the code but didn't fix the problem. PORTABILITY FIXES: Solaris: Don't use the system getusershell(3); it can apparently corrupt the heap in some circumstances. Problem found by Ken Pizzini of Spry, Inc. OP.ME: document several mailer flags that were accidently omitted from this document. These flags were F=d, F=j, F=R, and F=9. CONFIG: no changes. 8.8.1/8.8.1 96/10/17 SECURITY: unset all environment variables that the resolver will examine during queue runs and daemon mode. Problem noted by Dan Bernstein of the University of Illinois at Chicago. SECURITY: in some cases an illegal 7-bit MIME-encoded text/plain message could overflow a buffer if it was converted back to 8 bits. This caused core dumps and has the potential for a remote attack. Problem first noted by Gregory Shapiro of WPI. Avoid duplicate deliveries of error messages on systems that don't have flock(2) support. Patch from Motonori Nakamura of Kyoto University. Ignore null FallBackMX (V) options. If this option is null (as opposed to undefined) it can cause "null signature" syserrs on illegal host names. If a Base64 encoded text/plain message has no trailing newline in the encoded text, conversion back to 8 bits will drop the final line. Problem noted by Pierre David. If running with a RunAsUser, sendmail would give bogus "cannot setuid" (or seteuid, or setreuid) messages on some systems. Problem pointed out by Jordan Mendelson of Web Services, Inc. Always print error messages in -bv mode -- previously, -bv would be absolutely silent on errors if the error mode was sent to (say) mail-back. Problem noted by Kyle Jones of UUNET. If -qI/R/S is set (or the ETRN command is used), ignore all long term host status. This is necessary because it is common to do this when you know a host has just come back up. Disallow duplicate HELO/EHLO commands as required by RFC 1651 section 4.2. Excessive permissiveness noted by Lee Flight of the University of Leicester. If a service (such as NIS) is specified as the last entry in the service switch, but that service is not compiled in, sendmail would return a temporary failure when an entry was not found in the map. This caused the message to be queued instead of bouncing immediately. Problem noted by Harry Edmon of the University of Washington. PORTABILITY FIXES: Solaris 2.3 had compilation problems in conf.c. Several people pointed this out. NetBSD from Charles Hannum of MIT. AIX4 improvements based on info from Steve Bauer of South Dakota School of Mines & Technology. CONFIG: ``error:code message'' syntax was broken in virtusertable. Patch from Gil Kloepfer Jr. CONFIG: if FEATURE(nocanonify) was specified, hosts in $=M (set using MASQUERADE_DOMAIN) were not masqueraded unless they were also in $=w. Problem noted by Zoltan Basti of Softec. MAIL.LOCAL: patches to compile and link cleanly on AIX. Based on a patch from Eric Hagberg of Morgan Stanley. MAIL.LOCAL: patches to compile on NEXTSTEP. From Patrick Nolan of Stanford via Robert La Ferla. 8.8.0/8.8.0 96/09/26 Under some circumstances, Bcc: headers would not be properly deleted. Pointed out by Jonathan Kamens of OpenVision. Log a warning if the sendmail daemon is invoked without a full pathname, which prevents "kill -1" from working. I was urged to put this in by Andrey A. Chernov of DEMOS (Russia). Fix small buffer overflow. Since the data in this buffer was not read externally, there was no security problem (and in fact probably wouldn't really overflow on most compilers). Pointed out by KIZU takashi of Osaka University. Fix problem causing domain literals such as [1.2.3.4] to be ignored if a FallbackMXHost was specified in the configuration file -- all mail would be sent to the fallback even if the original host was accessible. Pointed out by Munenari Hirayama of NSC (Japan). A message that didn't terminate with a newline would (sometimes) not have the trailing "." added properly in the SMTP dialogue, causing SMTP to hang. Patch from Per Hedeland of Ericsson. The DaemonPortOptions suboption to bind to a particular address was incorrect and nonfunctional due to a misunderstanding of the semantics of binding on a passive socket. Patch from NIIBE Yutaka of Mitsubishi Research Institute. Increase the number of MX hosts for a single name to 100 to better handle the truly huge service providers such as AOL, which has 13 at the moment (and climbing). In order to avoid trashing memory, the buffer for all names has only been slightly increased in size, to 12.8K from 10.2K -- this means that if a single name had 100 MX records, the average size of those records could not exceed 128 bytes. Requested by Brad Knowles of America On Line. Restore use of IDENT returns where the OSTYPE field equals "OTHER". Urged by Dan Bernstein of U.C. Berkeley. Print q_statdate and q_specificity in address structure debugging printout. Expand MCI structure flag bits for debugging output. Support IPv6-style domain literals, which can have colons between square braces. Log open file descriptors for the "cannot dup" messages in deliver(); this is an attempt to track down a bug that one person seems to be having (it may be a Solaris bug!). DSN NOTIFY parameters were not properly propogated across queue runs; this caused the NOTIFY info to sometimes be lost. Problem pointed out by Claus Assmann of the Christian-Albrechts-University of Kiel. The statistics gathered in the sendmail.st file were too high; in some cases failures (e.g., user unknown or temporary failure) would count as a delivery as far as the statistics were concerned. Problem noted by Tom Moore of AT&T GIS. Systems that don't have flock() would not send split envelopes in the initial run. Problem pointed out by Leonard Zubkoff of Dandelion Digital. Move buffer overflow checking -- these primarily involve distrusting results that may come from NIS and DNS. 4.4-BSD-derived systems, including FreeBSD, NetBSD, and BSD/OS didn't include and hence had the wrong pathnames for a few things like /var/tmp. Reported by Matthew Green. Conditions were reversed for the Priority: header, resulting in all values being interpreted as non-urgent except for non-urgent, which was interpreted as normal. Patch from Bryan Costales. The -o (optional) flag was being ignored on hash and btree maps since 8.7.2. Fix from Bryan Costales. Content-Types listed in class "q" will always be encoded as Quoted-Printable (or more accurately, will never be encoded as base64). The class can have primary types (e.g., "text") or full types (e.g., "text/plain"). Based on a suggestion by Marius Olafsson of the University of Iceland. Define ${envid} to be the original envelope id (from the ESMTP DSN dialogue) so it can be passed to programs in mailers. Define ${bodytype} to be the body type (from the -B flag or the BODY= ESMTP parameter) so it can be passed to programs in mailers. Cause the VRFY command to return 252 instead of 250 unless the F=q flag is set in the mailer descriptor. Suggested by John Myers of CMU. Implement ESMTP ETRN command to flush the queue for a specific host. The command takes a host name; data for that host is immediately (and asynchronously) flushed. Because this shares the -qR implementation, other hosts may be attempted, but there should be no security implications. Implementation from John Beck of InReference, Inc. See RFC 1985 for details. Add three new command line flags to pass in DSN parameters: -V envid (equivalent to ENVID=envid on the MAIL command), -R ret (equivalent to RET=ret on the MAIL command), and -Nnotify (equivalent to NOTIFY=notify on the RCPT command). Note that the -N flag applies to all recipients; there is no way to specify per-address notifications on the command line, nor is there an equivalent for the ORCPT= per-address parameter. Restore LogLevel option to be safe (it can only be increased); apparently I went into paranoid mode between 8.6 and 8.7 and made it unsafe. Pointed out by Dabe Murphy of the University of Maryland. New logging on log level 15: all SMTP traffic. Patches from Andrew Gross of San Diego Supercomputer Center. NetInfo property value searching code wasn't stopping when it found a match. This was causing the wrong values to be found (and had a memory leak). Found by Bastian Schleuter of TU-Berlin. Add new F=0 (zero) mailer flag to turn off MX lookups. It was pointed out by Bill Wisner of Electronics for Imaging that you can't use the bracket address form for the MAIL_HUB macro, since that causes the brackets to remain in the envelope recipient address used for delivery. The simple fix (stripping off the brackets in the config file) breaks the use of IP literal addresses. This flag will solve that problem. Add MustQuoteChars option. This is a list of characters that must be quoted if they are found in the phrase part of an address (that is, the full name part). The characters @,;:\()[] are always in this list and cannot be removed. The default is this list plus . and ' to match RFC 822. Add AllowBogusHELO option; if set, sendmail will allow HELO commands that do not include a host name for back compatibility with some stupid SMTP clients. Setting this violates RFC 1123 section 5.2.5. Add MaxDaemonChildren option; if this is set, sendmail will start rejecting connections if it has more than this many outstanding children accepting mail. Note that you may see more processes than this because of outgoing mail; this is for incoming connections only. Add ConnectionRateThrottle option. If set to a positive value, the number of incoming SMTP connections that will be permitted in a single second is limited to this number. Connections are not refused during this time, just deferred. The intent is to flatten out demand so that load average limiting can kick in. It is less radical than MaxDaemonChildren, which will stop accepting connections even if all the connections are idle (e.g., due to connection caching). Add Timeout.hoststatus option. This interval (defaulting to 30m) specifies how long cached information about the state of a host will be kept before they are considered stale and the host is retried. If you are using persistent host status (i.e., the HostStatusDirectory option is set) this will apply between runs; otherwise, it applies only within a single queue run and hence is useful only for hosts that have large queues that take a very long time to run. Add SingleLineFromHeader option. If set, From: headers are coerced into being a single line even if they had newlines in them when read. This is to get around a botch in Lotus Notes. Text class maps were totally broken -- if you ever retrieved the last item in a table it would be truncated. Problem noted by Gregory Neil Shapiro of WPI. Extend the lines printed by the mailq command (== the -bp flag) when -v is given to 120 characters; this allows more information to be displayed. Suggested by Gregory Neil Shapiro of WPI. Allow macro definitions (`D' lines) with unquoted commas; previously this was treated as end-of-input. Problem noted by Bryan Costales. The RET= envelope parameter (used for DSNs) wasn't properly written to the queue file. Fix from John Hughes of Atlantic Technologies, Inc. Close /var/tmp/dead.letter after a successful write -- otherwise if this happens in a queue run it can cause nasty delays. Problem noted by Mark Horton of AT&T. If userdb entries pointed to userdb entries, and there were multiple values for a given key, the database cursor would get trashed by the recursive call. Problem noted by Roy Mongiovi of Georgia Tech. Fixed by reading all the values and creating a comma-separated list; thus, the -v output will be somewhat different for this case. Fix buffer allocation problem with Hesiod-based userdb maps when HES_GETMAILHOST is defined. Based on a patch by Betty Lee of Stanford University. When envelopes were split due to aliases with owner- aliases, and there was some error on one of the lists, more than one of the owners would get the message. Problem pointed out by Roy Mongiovi of Georgia Tech. Detect excessive recursion in macro expansions, e.g., $X defined in terms of $Y which is defined in terms of $X. Problem noted by Bryan Costales; patch from Eric Wassenaar. When using F=U to get "ugly UUCP" From_ lines, a buffer could in some cases get trashed causing bogus From_ lines. Fix from Kyle Jones of UUNET. When doing load average initialization, if the nlist call for avenrun failed, the second and subsequent lookups wouldn't notice that fact causing bogus load averages to be returned. Noted by Casper Dik of Sun Holland. Fix problem with incompatibility with some versions of inet_aton that have changed the return value to unsigned, so a check for an error return of -1 doesn't work. Use INADDR_NONE instead. This could cause mail to addresses such as [foo.com] to bounce or get dropped. Problem noted by Christophe Wolfhugel of the Pasteur Institute. DSNs were inconsistent if a failure occured during the DATA phase rather than the RCPT phase: the Action: would be correct, but the detailed status information would be wrong. Problem noted by Bob Snyder of General Electric Company. Add -U command line flag and the XUSR ESMTP extension, both indicating that this is the initial MUA->MTA submission. The flag current does nothing, but in future releases (when MUAs start using these flags) it will probably turn on things like DNS canonification. Default end-of-line string (E= specification on mailer [M] lines) to \r\n on SMTP mailers. Default remains \n on non-SMTP mailers. Change the internal definition for the *file* and *include* mailers to have $u in the argument vectors so that they aren't misinterpreted as SMTP mailers and thus use \r\n line termination. This will affect anyone who has redefined either of these in their configuration file. Don't assume that IDENT servers close the connection after a query; responses can be newline terminated. From Terry Kennedy of St. Peter's College. Avoid core dumps on erroneous configuration files that have $#mailer with nothing following. From Bryan Costales. Avoid null pointer dereference with high debug values in unlockqueue. Fix from Randy Martin of Clemson University. Fix possible buffer overrun when expanding very large macros. Fix from Kyle Jones of UUNET. After 25 EXPN or VRFY commands, start pausing for a second before processing each one. This avoids a certain form of denial of service attack. Potential attack pointed out by Bryan Costales. Allow new named (not numbered!) config file rules to do validity checking on SMTP arguments: check_mail for MAIL commands and check_rcpt for RCPT commands. These rulesets can do anything they want; their result is ignored unless they resolve to the $#error mailer, in which case the indicated message is printed and the command is rejected. Similarly, the check_compat ruleset is called before delivery with "from_addr $| to_addr" (the $| is a meta-symbol used to separate the two addresses); it can give a "this sender can't send to this recipient" notification. Note that this patch allows $| to stand alone in rulesets. Define new macros ${client_name}, ${client_addr}, and ${client_port} that have the name, IP address, and port number (respectively) of the SMTP client (that is, the entity at the other end of the connection. These can be used in (e.g.) check_rcpt to verify that someone isn't trying to relay mail through your host inappropriately. Be sure to use the deferred evaluation form, for example $&{client_name}, to avoid having these bound when sendmail reads the configuration file. Add new config file rule check_relay to check the incoming connection information. Like check_compat, it is passed the host name and host address separated by $| and can reject connections on that basis. Allow IDA-style recursive function calls. Code contributed by Mark Lovell and Paul Vixie. Eliminate the "No ! in UUCP From address!" message" -- instead, create a virtual UUCP address using either a domain address or the $k macro. Based on code contributed by Mark Lovell and Paul Vixie. Add Stanford LDAP map. Requires special libraries that are not included with sendmail. Contributed by Booker C. Bense ; contact him for support. See also the src/READ_ME file. Allow -dANSI to turn on ANSI escape sequences in debug output; this puts metasymbols (e.g., $+) in reverse video. Really useful only for debugging deep bits of code where it is important to distinguish between the single-character metasymbol $+ and the two characters $, +. Changed ruleset 89 (executed in dumpstate()) to a named ruleset, debug_dumpstate. Add new UnsafeGroupWrites option; if set, .forward and :include: files that are group writable are considered "unsafe" -- that is, programs and files referenced from such files are not valid recipients. Delete bogosity test for FallBackMX host; this prevented it to be a name that was not in DNS or was a domain-literal. Problem noted by Tom May. Change the introduction to error messages to more clearly delineate permanent from temporary failures; if both existed in a single message it could be confusing. Suggested by John Beck of InReference, Inc. The IngoreDot (i) option didn't work for lines that were terminated with CRLF. Problem noted by Ted Stockwell of Secure Computing Corporation. Add a heuristic to improve the handling of unbalanced `<' signs in message headers. Problem reported by Matt Dillon of Best Internet Communications. Check for bogus characters in the 0200-0237 range; since these are used internally, very strange errors can occur if those characters appear in headers. Problem noted by Anders Gertz of Lysator. Implement 7 -> 8 bit MIME conversions. This only takes place if the recipient mailer has the F=9 flag set, and only works on text/plain body types. Code contributed by Marius Olafsson of the University of Iceland. Special case "postmaster" name so that it is always treated as lower case in alias files regardless of configuration settings; this prevents some potential problems where "Postmaster" or "POSTMASTER" might not match "postmaster". In most cases this change is a no-op. The -o map flag was ignored for text maps. Problem noted by Bryan Costales. The -a map flag was ignored for dequote maps. Problem noted by Bryan Costales. Fix core dump when a lookup of a class "prog" map returns no response. Patch from Bryan Costales. Log instances where sendmail is deferring or rejecting connections on LogLevel 14. Suggested by Kyle Jones of UUNET. Include port number in process title for network daemons. Suggested by Kyle Jones of UUNET. Send ``double bounces'' (errors that occur when sending an error message) to the address indicated in the DoubleBounceAddress option (default: postmaster). Previously they were always sent to postmaster. Suggested by Kyle Jones of UUNET. Add new mode, -bD, that acts like -bd in all respects except that it runs in foreground. This is useful for using with a wrapper that "watches" system services. Suggested by Kyle Jones of UUNET. Fix botch in spacing around (parenthesized) comments in addresses when the comment comes before the address. Patch from Motonori Nakamura of Kyoto University. Use the prefix "Postmaster notify" on the Subject: lines of messages that are being bounced to postmaster, rather than "Returned mail". This permits the person who is postmaster more easily determine what messages are to their role as postmaster versus bounces to mail they actually sent. Based on a suggestion by Motonori Nakamura. Add new value "time" for QueueSortOrder option; this causes the queue to be sorted strictly by the time of submission. Note that this can cause very bad behaviour over slow lines (because large jobs will tend to delay small jobs) and on nodes with heavy traffic (because old things in the queue for hosts that are down delay processing of new jobs). Also, this does not guarantee that jobs will be delivered in submission order unless you also set DeliveryMode=queue. In general, it should probably only be used on the command line, and only in conjunction with -qRhost.domain. In fact, there are very few cases where it should be used at all. Based on an implementation by Motonori Nakamura. If a map lookup in ruleset 5 returns tempfail, queue the message in the same manner as other rulesets. Previously a temporary failure in ruleset 5 was ignored. Patch from Booker Bense of Stanford University. Don't proceed to the next MX host if an SMTP MAIL command returns a 5yz (permanent failure) code. The next MX host will still be tried if the connection cannot be opened in the first place or if the MAIL command returns a 4yz (temporary failure) code. (It's hard to know what to do here, since neither RFC 974 nor RFC 1123 specify when to proceed to the next MX host.) Suggested by Jonathan Kamens of OpenVision, Inc. Add new "-t" flag for map definitions (the "K" line in the .cf file). This causes map lookups that get a temporary failure (e.g., name server failure) to _not_ defer the delivery of the message. This should only be used if your configuration file is prepared to do something sensible in this case. Based on an idea by Gregory Shapiro of WPI. Fix problem finding network interface addresses. Patch from Motonori Nakamura. Don't reject qf entries that are not owned by your effective uid if you are not running setuid; this makes management of certain kinds of firewall setups difficult. Patch suggested by Eamonn Coleman of Qualcomm. Add persistent host status. This keeps the information normally maintained within a single queue run in disk files that are shared between sendmail instances. The HostStatusDirectory is the directory in which the information is maintained. If not set, persistent host status is turned off. If not a full pathname, it is relative to the queue directory. A common value is ".hoststat". There are also two new operation modes: * -bh prints the status of hosts that have had recent connections. * -bH purges the host statuses. No attempt is made to save recent status information. This feature was originally written by Paul Vixie of Vixie Enterprises for KJS and adapted for V8 by Mark Lovell of Bigrock Consulting. Paul's funding of Mark and Mark's patience with my insistence that things fit cleanly into the V8 framework is gratefully appreciated. New SingleThreadDelivery option (requires HostStatusDirectory to operate). Avoids letting two sendmails on the local machine open connections to the same remote host at the same time. This reduces load on the other machine, but can cause mail to be delayed (for example, if one sendmail is delivering a huge message, other sendmails won't be able to send even small messages). Also, it requires another file descriptor (for the lock file) per connection, so you may have to reduce ConnectionCacheSize to avoid running out of per-process file descriptors. Based on the persistent host status code contributed by Paul Vixie and Mark Lovell. Allow sending to non-simple files (e.g., /dev/null) even if the SafeFileEnvironment option is set. Problem noted by Bryan Costales. The -qR flag mistakenly matched flags in the "R" line of the queue file. Problem noted by Bryan Costales. If a job was aborted using the interrupt signal (e.g., control-C from the keyboard), on some occasions an empty df file would be left around; these would collect in the queue directory. Problem noted by Bryan Costales. Change the makesendmail script to enhance the search for Makefiles based on release number. For example, on SunOS 5.5.1, it will search for Makefile.SunOS.5.5.1, Makefile.SunOS.5.5, and then Makefile.SunOS.5.x (in addition to the other rules, e.g., adding $arch). Problem noted by Jason Mastaler of Atlanta Webmasters. When creating maps using "newaliases", always map the keys to lower case when creating the map unless the -f flag is specified on the map itself. Previously this was done based on the F=u flag in the local mailer, which meant you could create aliases that you could never access. Problem noted by Bob Wu of DEC. When a job was read from the queue, the bits causing notification on failure or delay were always set. This caused those notifications to be sent even if NOTIFY=NEVER had been specified. Problem noted by Steve Hubert of the University of Washington, Seattle. Add new configurable routine validate_connection (in conf.c). This lets you decide if you are willing to accept traffic from this host. If it returns FALSE, all SMTP commands will return "550 Access denied". -DTCPWRAPPERS will include support for TCP wrappers; you will need to add -lwrap to the link line. (See src/READ_ME for details.) Don't include the "THIS IS A WARNING MESSAGE ONLY" banner on postmaster bounces. Some people seemed to think that this could be confusing (even though it is true). Suggested by Motonori Nakamura. Add new RunAsUser option; this causes sendmail to do a setuid to that user early in processing to avoid potential security problems. However, this means that all .forward and :include: files must - be readable by that user, and on systems that don't support the - saved uid bit properly, all files to be written must be + be readable by that user, and all files to be written must be writable by that user and all programs will be executed by that user. It is also incompatible with the SafeFileEnvironment option. In other words, it may not actually add much to security. However, it should be useful on firewalls and other places where users don't have accounts and the aliases file is well constrained. Add Timeout.iconnect. This is like Timeout.connect except it is used only on the first attempt to delivery to an address. It could be set to be lower than Timeout.connect on the principle that the mail should go through quickly to responsive hosts; less responsive hosts get to wait for the next queue run. Fix a problem on Solaris that occassionally causes programs (such as vacation) to hang with their standard input connected to a UDP port. It also created some signal handling problems. The problems turned out to be an interaction between vfork(2) and some of the libraries, particularly NIS/NIS+. I am indebted to Tor Egge for this fix. Change user class map to do the same matching that actual delivery will do instead of just a /etc/passwd lookup. This adds fuzzy matching to the user map. Patch from Dan Oscarsson. The Timeout.* options are not safe -- they can be used to create a denial-of-service attack. Problem noted by Christophe Wolfhugel. Don't send PostMasterCopy messages in the event of a "delayed" notification. Suggested by Barry Bouwsma. Don't advertise "VERB" ESMTP extension if the "noexpn" privacy option is set, since this disables VERB mode. Suggested by John Hawkinson of MIT. Complain if the QueueDirectory (Q) option is not set. Problem noted by Motonori Nakamura of Kyoto University. Only queue messages on transient .forward open failures if there were no successful opens. The previous behaviour caused it to queue even if a "fall back" .forward was found. Problem noted by Ann-Kian Yeo of the Dept. of Information Systems and Computer Science (DISCS), NUS, Singapore. Don't do 8->7 bit conversions when bouncing a MIME message that is bouncing because of a MIME error during 8->7 bit conversion; the encapsulated message will bounce again, causing a loop. Problem noted by Steve Hubert of the University of Washington. Create xf (transcript) files using the TempFileMode option value instead of 0644. Suggested by Ann-Kian Yeo of the National University of Singapore. Print errors if setgid/setuid/etc. fail during delivery. This helps detect cases where DefaultUid is set to something that the system can't cope with. PORTABILITY FIXES: Support for AIX/RS 2.2.1 from Mark Whetzel of Western Atlas International. Patches for Intel Paragon OSF/1 1.3 from Leo Bicknell . On DEC OSF/1 3.2 and earlier, the MatchGECOS code would only work on the first recipient of a message due to a bug in the getpwent family. If this is something you use, you can define DEC_OSF_BROKEN_GETPWENT=1 for a workaround. From Maximum Entropy of Sanford C. Bernstein and Associates. FreeBSD 1.1.5.1 uname -r returns a string containing parentheses, which breaks makesendmail. Reported by Piero Serini . Sequent DYNIX/ptx 4.0.2 patches from Jack Woolley of Systems and Computer Technology Corporation. Solaris 2.x: omit the UUCP grade parameter (-g flag) because it is system-dependent. Problem noted by J.J. Bailey of Bailey Computer Consulting. Pyramid NILE running DC/OSx support from Earle F. Ake of Hassler Communication Systems Technology, Inc. HP-UX 10.x compile glitches, reported by Anne Brink of the U.S. Army and James Byrne of Harte & Lyne Limited. NetBSD from Matthew Green of the NetBSD crew. SCO 5.x from Keith Reynolds of SCO. IRIX 6.2 from Robert Tarrall of the University of Colorado and Kari Hurtta of the Finnish Meteorological Institute. UXP/DS (Fujitsu/ICL DS/90 series) support from Diego R. Lopez, CICA (Seville). NCR SVR4 MP-RAS 3.x support from Tom Moore of NCR. PTX 3.2.0 from Kenneth Stailey of the US Department of Labor Employment Standards Administration. Altos System V (5.3.1) from Tim Rice of Multitalents. Concurrent Systems Corporation Maxion from Donald R. Laster Jr. NetInfo maps (improved debugging and multi-valued aliases) from Adrian Steinmann of Steinmann Consulting. ConvexOS 11.5 (including SecureWare C2 and the Share Scheduler) from Eric Schnoebelen of Convex. Linux 2.0 mail.local patches from Horst von Brand. NEXTSTEP 3.x compilation from Robert La Ferla. NEXTSTEP 3.x code changes from Allan J. Nathanson of NeXT. Solaris 2.5 configuration fixes for mail.local by Jim Davis of the University of Arizona. Solaris 2.5 has a working setreuid. Noted by David Linn of Vanderbilt University. Solaris changes for praliases, makemap, mailstats, and smrsh. Previously you had to add -DSOLARIS in Makefile.dist; this auto-detects. Based on a patch from Randall Winchester of the University of Maryland. CONFIG: add generic-nextstep3.3.mc file. Contributed by Robert La Ferla of Hot Software. CONFIG: allow mailertables to resolve to ``error:code message'' (where "code" is an exit status) on domains (previously worked only on hosts). Patch from Cor Bosman of Xs4all Foundation. CONFIG: hooks for IPv6-style domain literals. CONFIG: predefine ALIAS_FILE and change the prototype file so that if it is undefined the AliasFile option is never set; this should be transparent for most everyone. Suggested by John Myers of CMU. CONFIG: add FEATURE(limited_masquerade). Without this feature, any domain listed in $=w is masqueraded. With it, only those domains listed in a MASQUERADE_DOMAIN macro are masqueraded. CONFIG: add FEATURE(masquerade_entire_domain). This causes masquerading specified by MASQUERADE_DOMAIN to apply to all hosts under those domains as well as the domain headers themselves. For example, if a configuration had MASQUERADE_DOMAIN(foo.com), then without this feature only foo.com would be masqueraded; with it, *.foo.com would be masqueraded as well. Based on an implementation by Richard (Pug) Bainter of U. Texas. CONFIG: add FEATURE(genericstable) to do a more general rewriting of outgoing addresses. Defaults to ``hash -o /etc/genericstable''. Keys are user names; values are outgoing mail addresses. Yes, this does overlap with the user database, and figuring out just when to use which one may be tricky. Based on code contributed by Richard (Pug) Bainter of U. Texas with updates from Per Hedeland of Ericsson. CONFIG: add FEATURE(virtusertable) to do generalized rewriting of incoming addresses. Defaults to ``hash -o /etc/virtusertable''. Keys are either fully qualified addresses or just the host part (with the @ sign). For example, a table containing: info@foo.com foo-info info@bar.com bar-info @baz.org jane@elsewhere.net would send all mail destined for info@foo.com to foo-info (which is presumably an alias), mail addressed to info@bar.com to bar-info, and anything addressed to anyone at baz.org will be sent to jane@elsewhere.net. The names foo.com, bar.com, and baz.org must all be in $=w. Based on discussions with a great many people. CONFIG: add nullclient configurations to define SMTP_MAILER_FLAGS. Suggested by Richard Bainter. CONFIG: add FAX_MAILER_ARGS to tweak the arguments passed to the "fax" mailer. CONFIG: allow mailertable entries to resolve to local:user; this passes the original user@host in to procmail-style local mailers as the "detail" information to allow them to do additional clever processing. From Joe Pruett of Teleport Corporation. Delivery to the original user can be done by specifying "local:" (with nothing after the colon). CONFIG: allow any context that takes "mailer:domain" to also take "mailer:user@domain" to force mailing to the given user; "local:user" can also be used to do local delivery. This applies on *_RELAY and in the mailertable entries. Based on a suggestion by Ribert Kiessling of Easynet. CONFIG: Allow FEATURE(bestmx_is_local) to take an argument that limits the possible domains; this reduces the number of DNS lookups required to support this feature. For example, FEATURE(bestmx_is_local, my.site.com) limits the lookups to domains under my.site.com. Code contributed by Anthony Thyssen . CONFIG: LOCAL_RULESETS introduces any locally defined rulesets, such as the check_rcpt ruleset. Suggested by Gregory Shapiro of WPI. CONFIG: MAILER_DEFINITIONS introduces any mailer definitions, in the event you have to define local mailers. Suggested by Gregory Shapiro of WPI. CONFIG: fix cases where a three- (or more-) stage route-addr could be misinterpreted as a list:...; syntax. Based on a patch by Vlado Potisk . CONFIG: Fix masquerading of UUCP addresses when the UUCP relay is remotely connected. The address host!user was being converted to host!user@thishost instead of host!user@uurelay. Problem noted by William Gianopoulos of Raytheon Company. CONFIG: add confTO_ICONNECT to set Timeout.iconnect. CONFIG: change FEATURE(redirect) message from "User not local" to "User has moved"; the former wording was confusing if the new address is still on the local host. Based on a suggestion by Andreas Luik. CONFIG: add support in FEATURE(nullclient) for $=E (exposed users). However, the class is not pre-initialized to contain root. Suggested by Gregory Neil Shapiro. CONTRIB: Remove XLA code at the request of the author, Christophe Wolfhugel. CONTRIB: Add re-mqueue.pl, contributed by Paul Pomes of Qualcomm. MAIL.LOCAL: make it possible to compile mail.local on Solaris. Note well: this produces a slightly different mailbox format (no Content-Length: headers), file ownerships and modes are different (not owned by group mail; mode 600 instead of 660), and the local mailer flags will have to be tweaked (make them match bsd4.4) in order to use this mailer. Patches from Paul Hammann of the Missouri Research and Education Network. MAIL.LOCAL: in some cases it could return EX_OK even though there was a delivery error, such as if the ownership on the file was wrong or the mode changed between the initial stat and the open. Problem reported by William Colburn of the New Mexico Institute of Mining and Technology. MAILSTATS: handle zero length files more reliably. Patch from Bryan Costales. MAILSTATS: add man page contributed by Keith Bostic of BSDI. MAKEMAP: The -d flag (to allow duplicate keys) to a btree map wasn't honored. Fix from Michael Scott Shappe. PRALIASES: add man page contributed by Keith Bostic of BSDI. NEW FILES: src/Makefiles/Makefile.AIX.2 src/Makefiles/Makefile.IRIX.6.2 src/Makefiles/Makefile.maxion src/Makefiles/Makefile.NCR.MP-RAS.3.x src/Makefiles/Makefile.SCO.5.x src/Makefiles/Makefile.UXPDSV20 mailstats/mailstats.8 praliases/praliases.8 cf/cf/generic-nextstep3.3.mc cf/feature/genericstable.m4 cf/feature/limited_masquerade.m4 cf/feature/masquerade_entire_domain.m4 cf/feature/virtusertable.m4 cf/ostype/aix2.m4 cf/ostype/altos.m4 cf/ostype/maxion.m4 cf/ostype/solaris2.ml.m4 cf/ostype/uxpds.m4 contrib/re-mqueue.pl DELETED FILES: src/Makefiles/Makefile.Solaris contrib/xla/README contrib/xla/xla.c RENAMED FILES: src/Makefiles/Makefile.NCR3000 => Makefile.NCR.MP-RAS.2.x src/Makefiles/Makefile.SCO.3.2v4.2 => Makefile.SCO.4.2 src/Makefiles/Makefile.UXPDS => Makefile.UXPDSV10 src/Makefiles/Makefile.NeXT => Makefile.NeXT.2.x src/Makefiles/Makefile.NEXTSTEP => Makefile.NeXT.3.x 8.7.6/8.7.3 96/09/17 SECURITY: It is possible to force getpwuid to fail when writing the queue file, causing sendmail to fall back to running programs as the default user. This is not exploitable from off-site. Workarounds include using a unique user for the DefaultUser (old u & g options) and using smrsh as the local shell. SECURITY: fix some buffer overruns; in at least one case this allows a local user to get root. This is not known to be exploitable from off-site. The workaround is to disable chfn(1) commands. 8.7.5/8.7.3 96/03/04 Fix glitch in 8.7.4 when putting certain internal lines; this can in some case cause connections to hang or messages to have extra spaces in odd places. Patch from Eric Wassenaar; reports from Eric Hall of Chiron Corporation, Stephen Hansen of Stanford University, Dean Gaudet of HotWired, and others. 8.7.4/8.7.3 96/02/18 SECURITY: In some cases it was still possible for an attacker to insert newlines into a queue file, thus allowing access to any user (except root). CONFIG: no changes -- it is not a bug that the configuration version number is unchanged. 8.7.3/8.7.3 95/12/03 Fix botch in name server timeout in RCPT code; this problem caused two responses in SMTP, which breaks things horribly. Fix from Gregory Neil Shapiro of WPI. Verify that L= value on M lines cannot be negative, which could cause negative array subscripting. Not a security problem since this has to be in the config file, but it could have caused core dumps. Pointed out by Bryan Costales. Fix -d21 debug output for long macro names. Pointed out by Bryan Costales. PORTABILITY FIXES: SCO doesn't have ftruncate. From Bill Aten of Computerizers. IBM's version of arpa/nameser.h defaults to the wrong byte order. Tweak it to work properly. Based on fixes from Fletcher Mattox of UTexas and Betty Lee of Stanford University. CONFIG: add confHOSTS_FILE m4 variable to set HostsFile option. Deficiency pointed out by Bryan Costales of ICSI. 8.7.2/8.7.2 95/11/19 REALLY fix the backslash escapes in SmtpGreetingMessage, OperatorChars, and UnixFromLine options. They were not properly repaired in 8.7.1. Completely delete the Bcc: header if and only if there are other valid recipient headers (To:, Cc: or Apparently-To:, the last being a historic botch, of course). If Bcc: is the only recipient header in the message, it's value is tossed, but the header name is kept. The old behaviour (always keep the header name and toss the value) allowed primary recipients to see that a Bcc: went to _someone_. Include queue id on ``Authentication-Warning: : set sender to using -f'' syslog messages. Suggested by Kari Hurtta. If a sequence or switch map lookup entry gets a tempfail but then continues on to another map type, but the name is not found, return a temporary failure from the sequence or switch map. For example, if hosts search ``dns files'' and DNS fails with a tempfail, the hosts map will go on and search files, but if it fails the whole thing should be a tempfail, not a permanent (host unknown) failure, even though that is the failure in the hosts.files map. This error caused hard bounces when it should have requeued. Aliases to files such as /users/bar/foo/inbox, with /users/bar/foo owned by bar mode 700 and inbox being setuid bar stopped working properly due to excessive paranoia. Pointed out by John Hawkinson of Panix. An SMTP RCPT command referencing a host that gave a nameserver timeout would return a 451 command (8.6 accepted it and queued it locally). Revert to the 8.6 behaviour in order to simplify queue management for clustered systems. Suggested by Gregory Neil Shapiro of WPI. The same problem could break MH, which assumes that the SMTP session will succeed (tsk, tsk -- mail gets lost!); this was pointed out by Stuart Pook of Infobiogen. Fix possible buffer overflow in munchstring(). This was not a security problem because you couldn't specify any argument to this without first giving up root privileges, but it is still a good idea to avoid future problems. Problem noted by John Hawkinson and Sam Hartman of MIT. ``452 Out of disk space for temp file'' messages weren't being printed. Fix from David Perlin of Nanosoft. Don't advertise the ESMTP DSN extension if the SendMIMEErrors option is not set, since this is required to get the actual DSNs created. Problem pointed out by John Gardiner Myers of CMU. Log permission problems that cause .forward and :include: files to be untrusted or ignored on log level 12 and higher. Suggestted by Randy Martin of Clemson University. Allow user ids in U= clauses of M lines to have hyphens and underscores. Fix overcounting of recipients -- only happened when sending to an alias. Pointed out by Mark Andrews of SGI and Jack Woolley of Systems and Computer Technology Corporation. If a message is sent to an address that fails, the error message that is returned could show some extraneous "success" information included even if the user did not request success notification, which was confusing. Pointed out by Allan Johannesen of WPI. Config files that had no AliasFile definition were defaulting to using /etc/aliases; this caused problems with nullclient configurations. Change it back to the 8.6 semantics of having no local alias file unless it is declared. Problem noted by Charles Karney of Princeton University. Fix compile problem if NOTUNIX is defined. Pointed out by Bryan Costales of ICSI. Map lookups of class "userdb" maps were always case sensitive; they should be controlled by the -f flag like other maps. Pointed out by Bjart Kvarme . Fix problem that caused some addresses to be passed through ruleset 5 even when they were tagged as "sticky" by prefixing the address with an "@". Patch from Thomas Dwyer III of Michigan Technological University. When converting a message to Quoted-Printable, prevent any lines with dots alone on a line by themselves. This is because of the preponderence of broken mailers that still get this wrong. Code contributed by Per Hedeland of Ericsson. Fix F{macro}/file construct -- it previously did nothing. Pointed out by Bjart Kvarme of USIT/UiO (Norway). Announce whether a cached connection is SMTP or ESMTP (in -v mode). Requested by Allan Johannesen. Delete check for text format of alias files -- it should be legal to have the database format of the alias files without the text version. Problem pointed out by Joe Rhett of Navigist, Inc. If "Ot" was specified with no value, the TZ variable was not properly imported from the environment. Pointed out by Frank Crawford . Some architectures core dumped on "program" maps that didn't have extra arguments. Patch from Booker C. Bense of Stanford University. Queue run processes would re-spawn daemons when given a SIGHUP; only the parent should do this. Fix from Brian Coan of the Association for Progressive Communications. If MinQueueAge was set and a message was considered but not run during a queue run and the Timeout.queuereturn interval was reached, a "timed out" error message would be returned that didn't include the failed address (and claimed to be a warning even though it was fatal). The fix is to not return such messages until they are actually tried, i.e., in the next MinQueueAge interval. Problem noted by Rein Tollevik of SINTEF RUNIT, Oslo. Add HES_GETMAILHOST compile flag to support MIT Hesiod distributions that have the hes_getmailhost() routine. DEC Hesiod distributions do not have this routine. Based on a patch from Betty Lee of Stanford University. Extensive cleanups to map open code to handle a locking race condition in ndbm, hash, and btree format database files on some (most non-4.4-BSD based) OS architectures. This should solve the occassional "user unknown" problem during alias rebuilds that has plagued me for quite some time. Based on a patch from Thomas Dwyer III of Michigan Technological University. PORTABILITY FIXES: Solaris: Change location of newaliases and mailq from /usr/ucb to /usr/bin to match Sun settings. From James B. Davis of TCI. DomainOS: Makefile.DomainOS doesn't require -ldbm. From Don Lewis of Silicon Systems. HP-UX 10: rename Makefile.HP-UX.10 => Makefile.HP-UX.10.x so that the makesendmail script will find it. Pointed out by Richard Allen of the University of Iceland. Also, use -Aa -D_HPUX_SOURCE instead of -Ae, which isn't supported on all compilers. UXPDS: compilation fixes from Diego R. Lopez. CONFIG: FAX mailer wasn't setting .FAX as a pseudo-domain unless you also had a FAX_RELAY. From Thomas.Tornblom@Hax.SE. CONFIG: Minor glitch in S21 -- attachment of local domain name didn't have trailing dot. From Jim Hickstein of Teradyne. CONFIG: Fix best_mx_is_local feature to allow nested addresses such as user%host@thishost. From Claude Scarpelli of Infobiogen (France). CONFIG: OSTYPE(hpux10) failed to define the location of the help file. Pointed out by Hannu Martikka of Nokia Telecommunications. CONFIG: Diagnose some inappropriate ordering in configuration files, such as FEATURE(smrsh) listed after MAILER(local). Based on a bug report submitted by Paul Hoffman of Proper Publishing. CONFIG: Make OSTYPE files consistently not override settings that have already been set. Previously it worked differently for different files. CONFIG: Change relay mailer to do masquerading like 8.6 did. My take is that this is wrong, but the change was causing problems for some people. From Per Hedeland of Ericsson. CONTRIB: bitdomain.c patch from John Gardiner Myers ; portability changes for Posix environments (no functional changes). 8.7.1/8.7.1 95/10/01 Old macros that have become options (SmtpGreetingMessage, OperatorChars, and UnixFromLine) didn't allow backslash escapes in the options, where they previously had. Bug pointed out by John Hawkinson of MIT. Fix strange case of an executable called by a program map that returns a value but also a non-zero exit status; this would give contradictory results in the higher level; in particular, the default clause in the map lookup would be ignored. Change to ignore the value if the program returns non-zero exit status. From Tom Moore of AT&T GIS. Shorten parameters passed to syslog() in some contexts to avoid a bug in many vendors' implementations of that routine. Although this isn't really a bug in sendmail per se, and my solution has to assume that syslog() has at least a 1K buffer size internally (I know some vendors have shortened this dramatically -- they're on their own), sendmail is a popular target. Also, limit the size of %s arguments in sprintf. These both have possible security implications. Solutions suggested by Casper Dik of Sun's Network Security Group (Holland), Mark Seiden, and others. Fix a problem that might cause a non-standard -B (body type) parameter to be passed to the next server with undefined results. This could have security implications. If a filesystem was at > 100% utilization, the freediskspace() routine incorrectly returned an error rather than zero. Problem noted by G. Paul Ziemba of Alantec. Change MX sort order so that local hostnames (those in $=w) always sort first within a given preference. This forces the bestmx map to always return the local host first, if it is included in the list of highest priority MX records. From K. Robert Elz. Avoid some possible null pointer dereferences. Fixes from Randy Martin When sendmail starts up on systems that have no fully qualified domain name (FQDN) anywhere in the first matching host map (e.g., /etc/hosts if the hosts service searches "files dns"), sendmail would sleep to try to find a FQDN, which it really really needs. This has been changed to fall through to the next map type if it can't find a FQDN -- i.e., if the hosts file doesn't have a FQDN, it will try dns even though the short name was found in /etc/hosts. This is probably a crock, but many people have hosts files without FQDNs. Remember: domain names are your friends. Log a high-priority message if you can't find your FQDN during startup. Suggested by Simon Barnes of Schlumberger Limited. When using Hesiod, initialize it early to improve error reporting. Patch from Don Lewis of Silicon Systems, Inc. Apparently at least some versions of Linux have a 90 !minute! TCP connection timeout in the kernel. Add a new "connect" timeout to limit this time. Defaults to zero (use whatever the kernel provides). Based on code contributed by J.R. Oldroyd of TerraNet. Under some circumstances, a failed message would not be properly removed from the queue, causing tons of bogus error messages. (This fix eliminates the problematic EF_KEEPQUEUE flag.) Problem noted by Allan E Johannesen and Gregory Neil Shapiro of WPI. PORTABILITY FIXES: On IRIX 5.x, there was an inconsistency in the setting of sendmail.st location. Change the Makefile to install it in /var/sendmail.st to match the OSTYPE file and SGI standards. From Andre . Support for Fujitsu/ICL UXP/DS (For the DS/90 Series) from Diego R. Lopez . Linux compilation patches from J.R. Oldroyd of TerraNet, Inc. LUNA 2 Mach patches from Motonori Nakamura. SunOS Makefile was including -ldbm, which is for the old dbm library. The ndbm library is part of libc. CONFIG: avoid bouncing ``user@host.'' (note trailing dot) with ``local configuration error'' in nullclient configuration. Patch from Gregory Neil Shapiro of WPI. CONFIG: don't allow an alias file in nullclient configurations -- since all addresses are relayed, they give errors during rebuild. Suggested by Per Hedeland of Ericsson. CONFIG: local mailer on Solaris 2 should always get a -f flag because otherwise the F=S causes the From_ line to imply that root is the sender. Problem pointed out by Claude Scarpelli of Infobiogen (France). NEW FILES: cf/feature/use_ct_file.m4 (omitted from 8.7 by mistake) src/Makefiles/Makefile.KSR (omitted from 8.7 by mistake) src/Makefiles/Makefile.UXPDS 8.7/8.7 95/09/16 Fix a problem that could cause sendmail to run out of file descriptors due to a trashed data structure after a vfork. Fix from Brian Coan of the Institute for Global Communications. Change the VRFY response if you have disabled VRFY -- some people seemed to think that it was too rude. Avoid reference to uninitialized file descriptor if HASFLOCK was not defined. This was used "safely" in the sense that it only did a stat, but it would have set the map modification time improperly. Problem pointed out by Roy Mongiovi of Georgia Tech. Clean up the Subject: line on warning messages and return receipts so that they don't say "Returned mail:"; this can be confusing. Move ruleset entry/exit debugging from 21.2 to 21.1 -- this is useful enough to make it worthwhile printing on "-d". Avoid logging alias statistics every time you read the alias file on systems with no database method compiled in. If you have a name with a trailing dot, and you try looking it up using gethostbyname without the dot (for /etc/hosts compatibility), be sure to turn off RES_DEFNAMES and RES_DNSRCH to avoid finding the wrong name accidently. Problem noted by Charles Amos of the University of Maryland. Don't do timeouts in collect if you are not running SMTP. There is nothing that says you can't have a long running program piped into sendmail (possibly via /bin/mail, which just execs sendmail). Problem reported by Don "Truck" Lewis of Silicon Systems. Try gethostbyname() even if the DNS lookup fails iff option I is not set. This allows you to have hosts listed in NIS or /etc/hosts that are not known to DNS. It's normally a bad idea, but can be useful on firewall machines. This should really be broken out on a separate flag, I suppose. Avoid compile warnings against BIND 4.9.3, which uses function prototypes. From Don Lewis of Silicon Systems. Avoid possible incorrect diagnosis of DNS-related errors caused by things like attempts to resolve uucp names using $[ ... $] -- the fix is to clear h_errno at appropriate times. From Kyle Jones of UUNET. SECURITY: avoid denial-of-service attacks possible by destroying the alias database file by setting resource limits low. This involves adding two new compile-time options: HASSETRLIMIT (indicating that setrlimit(2) support is available) and HASULIMIT (indicating that ulimit(2) support is available -- the Release 3 form is used). The former is assumed on BSD-based systems, the latter on System V-based systems. Attack noted by Phil Brandenberger of Swarthmore University. New syntaxes in test (-bt) mode: ``.Dmvalue'' will define macro "m" to "value". ``.Ccvalue'' will add "value" to class "c". ``=Sruleset'' will dump the contents of the indicated ruleset. ``=M'' will display the known mailers. ``-ddebug-spec'' is equivalent to the command-line -d debug flag. ``$m'' will print the value of macro $m. ``$=c'' will print the contents of class $=c. ``/mx host'' returns the MX records for ``host''. ``/parse address'' will parse address, returning the value of crackaddr (essentially, the comment information) - and the parsed address (the same as -bv). + and the parsed address. ``/try mailer address'' will rewrite address into the form it will have when presented to the indicated mailer. ``/tryflags flags'' will set flags used by parsing. The flags can be `H' for header or `E' for envelope, and `S' for sender or `R' for recipient. These can be combined, so `HR' sets flags for header recipients. ``/canon hostname'' will try to canonify hostname and return the result. ``/map mapname key'' will look up `key' in the indicated `mapname' and return the result. Somewhat better handling of UNIX-domain socket addresses -- it should show the pathname rather than hex bytes. Restore ``-ba'' mode -- this reads a file from stdin and parses the header for envelope sender information and uses CR-LF as message terminators. It was thought to be obsolete (used only for Arpanet NCP protocols), but it turns out that the UK ``Grey Book'' protocols require that functionality. Fix a fix in previous release -- if gethostname and gethostbyname return a name without dots, and if an attempt to canonify that name fails, wait one minute and try again. This can result in an extra 60 second delay on startup if your system hostname (as returned by hostname(1)) has no dot and no names listed in /etc/hosts or your NIS map have a dot. Check for proper domain name on HELO and EHLO commands per RFC 1123 section 5.2.5. Problem noted by Thomas Dwyer III of Michigan Technological University. Relax chownsafe rules slightly -- old version said that if you can't tell if _POSIX_CHOWN_RESTRICTED is set (that is, if fpathconf returned EINVAL or ENOSYS), assume that chown is not safe. The new version falls back to whether you are on a BSD system or not. This is important for SunOS, which apparently always returns one of those error codes. This impacts whether you can mail to files or not. Syntax errors such as unbalanced parentheses in the configuration file could be omitted if you had "Oem" prior to the syntax error in the config file. Change to always print the error message. It was especially weird because it would cause a "warning" message to be sent to the Postmaster for every message sent (but with no transcript). Problem noted by Gregory Paris of Motorola. Rewrite collect and putbody to handle full 8-bit data, including zero bytes. These changes are internally extensive, but should have minimal impact on external function. Allow full words for option names -- if the option letter is (apparently) a space, then take the word following -- e.g., O MatchGECOS=TRUE The full list of old and new names is as follows: 7 SevenBitInput 8 EightBitMode A AliasFile a AliasWait B BlankSub b MinFreeBlocks/MaxMessageSize C CheckpointInterval c HoldExpensive D AutoRebuildAliases d DeliveryMode E ErrorHeader e ErrorMode f SaveFromLine F TempFileMode G MatchGECOS H HelpFile h MaxHopCount i IgnoreDots I ResolverOptions J ForwardPath j SendMimeErrors k ConnectionCacheSize K ConnectionCacheTimeout L LogLevel l UseErrorsTo m MeToo n CheckAliases O DaemonPortOptions o OldStyleHeaders P PostmasterCopy p PrivacyOptions Q QueueDirectory q QueueFactor R DontPruneRoutes r, T Timeout S StatusFile s SuperSafe t TimeZoneSpec u DefaultUser U UserDatabaseSpec V FallbackMXhost v Verbose w TryNullMXList x QueueLA X RefuseLA Y ForkEachJob y RecipientFactor z ClassFactor Z RetryFactor The old macros that passed information into sendmail have been changed to options; those correspondences are: $e SmtpGreetingMessage $l UnixFromLine $o OperatorChars $q (deleted -- not necessary) To avoid possible problems with an older sendmail, configuration level 6 is accepted by this version of sendmail; any config file using the new names should specify "V6" in the configuration. Change address parsing to properly note that a phrase before a colon and a trailing semicolon are essentially the same as text outside of angle brackets (i.e., sendmail should treat them as comments). This is to handle the ``group name: addr1, addr2, ..., addrN;'' syntax (it will assume that ``group name:'' is a comment on the first address and the ``;'' is a comment on the last address). This requires config file support to get right. It does understand that :: is NOT this syntax, and can be turned off completely by setting the ColonOkInAddresses option. Level 6 config files added with new mailer flags: A Addresses are aliasable. i Do udb rewriting on envelope as well as header sender lines. Applies to the from address mailer flags rather than the recipient mailer flags. j Do udb rewriting on header recipient addresses. Applies to the sender mailer flags rather than the recipient mailer flags. k Disable check for loops when doing HELO command. o Always run as the mail recipient, even on local delivery. w Check for an /etc/passwd entry for this user. 5 Pass addresses through ruleset 5. : Check for :include: on this address. | Check for |program on this address. / Check for /file on this address. @ Look up sender header addresses in the user database. Applies to the mailer flags for the mailer corresponding to the envelope sender address, rather than to recipient mailer flags. Pre-level 6 configuration files set A, w, 5, :, |, /, and @ on the "local" mailer, the o flag on the "prog" and "*file*" mailers, and the ColonOkInAddresses option. Eight-to-seven bit MIME conversions. This borrows ideas from John Beck of Hewlett-Packard, who generously contributed their implementation to me, which I then didn't use (see mime.c for an explanation of why). This adds the EightBitMode option (a.k.a. `8') and an F=8 mailer flag to control handling of 8-bit data. These have to cope with two types of 8-bit data: unlabelled 8-bit data (that is, 8-bit data that is entered without declaring it as 8-bit MIME -- technically this is illegal according to the specs) and labelled 8-bit data (that is, it was declared as 8BITMIME in the ESMTP session or by using the -B8BITMIME command line flag). If the F=8 mailer flag is set then 8-bit data is sent to non-8BITMIME machines instead of converting to 7 bit (essentially using just-send-8 semantics). The values for EightBitMode are: m convert unlabelled 8-bit input to 8BITMIME, and do any necessary conversion of 8BITMIME to 7BIT (essentially, the full MIME option). p pass unlabelled 8-bit input, but convert labelled 8BITMIME input to 7BIT as required (default). s strict adherence: reject unlabelled 8-bit input, convert 8BITMIME to 7BIT as required. The F=8 flag is ignored. Unlabelled 8-bit data is rejected in mode `s' regardless of the setting of F=8. Add new internal class 'n', which is the set of MIME Content-Types which can not be 8 to 7 bit encoded because of other considerations. Types "multipart/*" and "message/*" are never directly encoded (although their components can be). Add new internal class 's', which is the set of subtypes of the MIME message/* content type that can be treated as though they are an RFC822 message. It is predefined to have "rfc822". Suggested By Kari Hurtta. Add new internal class 'e'. This is the set of MIME Content-Transfer-Encodings that can be converted to a seven bit format (Quoted-Printable or Base64). It is preinitialized to contain "7bit", "8bit", and "binary". Add C=charset mailer parameter and the the DefaultCharSet option (no short name) to set the default character set to use in the Content-Type: header when doing encoding of an 8-bit message which isn't marked as MIME into MIME format. If the C= parameter is set on the Envelope From address, use that as the default encoding; else use the DefaultCharSet option. If neither is set, it defaults to "unknown-8bit" as suggested by RFC 1428 section 3. Allow ``U=user:group'' field in mailer definition to set a default user and group that a mailer will be executed as. This overrides the 'u' and 'g' options, and if the `F=S' flag is also set, it is the uid/gid that will always be used (that is, the controlling address is ignored). The values may be numeric or symbolic; if only a symbolic user is given (no group) that user's default group in the passwd file is used as the group. Based on code donated by Chip Rosenthal of Unicom. Allow `u' option to also accept user:group as a value, in the same fashion as the U= mailer option. Add the symbolic time zone name in the Arpanet format dates (as a comment). This adds a new compile-time configuration flag: TZ_TYPE can be set to TZ_TM_NAME (use the value of (struct tm *)->tm_name), TZ_TM_ZONE (use the value of (struct tm *)->tm_zone), TZ_TZNAME (use extern char *tzname[(struct tm *)->tm_isdst]), TZ_TIMEZONE (use timezone()), or TZ_NONE (don't include the comment). Code from Chip Rosenthal. The "Timeout" option (formerly "r") is extended to allow suboptions. For example, O Timeout.helo = 2m There are also two new suboptions "queuereturn" and "queuewarn"; these subsume the old T option. Thus, to set them both the preferred new syntax is O Timeout.queuereturn = 5d O Timeout.queuewarn = 4h Sort queue by host name instead of by message priority if the QueueSortOrder option (no short name) is set is set to ``host''. This makes better use of the connection cache, but may delay more ``interactive'' messages behind large backlogs under some circumstances. This is probably a good option if you have high speed links or don't do lots of ``batch'' messages, but less good if you are using something like PPP on a 14.4 modem. Based on code contributed by Roy Mongiovi of Georgia Tech (my main contribution was to make it configurable). Save i-number of df file in qf file to simplify rebuilding of queue after disasterous disk crash. Suggested by Kyle Jones of UUNET; closely based on code from KJS DECWRL code written by Paul Vixie. NOTA BENE: The qf files produced by 8.7 are NOT back compatible with 8.6 -- that is, you can convert from 8.6 to 8.7, but not the other direction. Add ``F=d'' mailer flag to disable all use of angle brackets in route-addrs in envelopes; this is because in some cases they can be sent to the shell, which interprets them as I/O redirection. Don't include error file (option E) with return-receipts; this can be confusing. Don't send "Warning: cannot send" messages to owner-* or *-request addresses. Suggested by Christophe Wolfhugel of the Institut Pasteur, Paris. Allow -O command line flag to set long form options. Add "MinQueueAge" option to set the minimum time between attempts to run the queue. For example, if the queue interval (-q value) is five minutes, but the minimum queue age is fifteen minutes, jobs won't be tried more often than once every fifteen minutes. This can be used to give you more responsiveness if your delivery mode is set to queue-only. Allow "fileopen" timeout (default: 60 seconds) for opening :include: and .forward files. Add "-k", "-v", and "-z" flags to map definitions; these set the key field name, the value field name, and the field delimiter. The field delimiter can be a single character or the sequence "\t" or "\n" for tab or newline. These are for use by NIS+ and similar access methods. Change maps to always strip quotes before lookups; the -q flag turns off this behaviour. Suggested by Motonori Nakamura. Add "nisplus" map class. Takes -k and -v flags to choose the key and value field names respectively. Code donated by Sun Microsystems. Add "hesiod" map class. The "file name" is used as the "HesiodNameType" parameter to hes_resolve(3). Returns the first value found for the match. Code donated by Scott Hutton of Indiana University. Add "netinfo" (NeXT NetInfo) map class. Maps can have a -k flag to specify the name of the property that is searched as the key and a -v flag to specify the name of the property that is returned as the value (defaults to "members"). The default map is "/aliases". Some code based on code contributed by Robert La Ferla of Hot Software. Add "text" map class. This does slow, linear searches through text files. The -z flag specifies a column delimiter (defaults to any sequence of white space), the -k flag sets the key column number, and the -v flag sets the value column number. Lines beginning with `#' are treated as comments. Add "program" map class to execute arbitrary programs. The search key is presented as the last argument; the output is one line read from the programs standard output. Exit statuses are from sysexits.h. Add "sequence" map class -- searches maps in sequence until it finds a match. For example, the declarations: Kmap1 ... Kmap2 ... Kmapseq sequence map1 map2 defines a map "mapseq" that first searches map1; if the value is found it is returned immediately, otherwise map2 is searched and the value returned. Add "switch" map class. This is much like "sequence" except that the ordering is fetched from an external file, usually the system service switch. The parameter is the name of the service to switch on, and the maps that it will use are the name of the switch map followed by ".service_type". For example, if the declaration of the map is Ksample switch hosts and the system service switch specifies that hosts are looked up using dns and nis in that order, then this is equivalent to Ksample sequence sample.dns sample.nis The subordinate maps (sample.*) must already be defined. Add "user" map class -- looks up users using getpwnam. Takes a "-v field" flag on the definition that tells what passwd entry to return -- legal values are name, passwd, uid, gid, gecos, dir, and shell. Generally expected to be used with the -m (matchonly) flag. Add "bestmx" map class -- returns the best MX value for the host listed as the value. If there are several "best" MX records for this host, one will be chosen at random. Add "userdb" map class -- looks up entries in the user database. The "file name" is actually the tag that will be used, typically "mailname". If there are multiple entries matching the name, the one chosen is undefined. Add multiple queue timeouts (both return and warning). These are set by the Precedence: or Priority: header fields to one of three values. If a Priority: is set and has value "normal", "urgent", or "non-urgent" the corresponding timeouts are used. If no priority is set, the Precedence: is consulted; if negative, non-urgent timeouts are used; if greater than zero, urgent timeouts are used. Otherwise, normal timeouts are used. The timeouts are set by setting the six timeouts queue{warn,return}.{urgent,normal,non-urgent}. Fix problem when a mail address is resolved to a $#error mailer with a temporary failure indication; it works in SMTP, but when delivering locally the mail is silently discarded. This patch, from Kyle Jones of UUNET, bounces it instead of queueing it (queueing is very hard). When using /etc/hosts or NIS-style lookups, don't assume that the first name in the list is the best one -- instead, search for the first one with a dot. For example, if an /etc/hosts entry reads 128.32.149.68 mammoth mammoth.CS.Berkeley.EDU this change will use the second name as the canonical machine name instead of the initial, unqualified name. Change dequote map to replace spaces in quoted text with a value indicated by the -s flag on the dequote map definition. For example, ``Mdequote dequote -s_'' will change "Foo Bar" into an unquoted Foo_Bar instead of leaving it quoted (because of the space character). Suggested by Dan Oscarsson for use in X.400 addresses. Implement long macro names as ${name}; long class names can be similarly referenced as $={name} and $~{name}. Definitions are (e.g.) ``D{name}value''. Names that have a leading lower case letter or punctuation characters are reserved for internal use by sendmail; i.e., config files should use names that begin with a capital letter. Based on code contributed by Dan Oscarsson. Fix core dump if getgrgid returns a null group list (as opposed to an empty group list, that is, a pointer to a list with no members). Fix from Andrew Chang of Sun Microsystems. Fix possible core dump if malloc fails -- if the malloc in xalloc failed, it called syserr which called newstr which called xalloc.... The newstr is now avoided for "panic" messages. Reported by Stuart Kemp of James Cook University. Improve connection cache timeouts; previously, they were not even checked if you were delivering to anything other than an IPC-connected host, so a series of (say) local mail deliveries could cause cached connections to be open much longer than the specified timeout. If an incoming message exceeds the maximum message size, stop writing the incoming bytes to the queue data file, since this can fill your mqueue partition -- this is a possible denial-of-service attack. Don't reject all numeric local user names unless HESIOD is defined. It turns out that Posix allows all-numeric user names. Fix from Tony Sanders of BSDI. Add service switch support. If the local OS has a service switch (e.g., /etc/nsswitch.conf on Solaris or /etc/svc.conf on DEC systems) that will be used; otherwise, it falls back to using a local mechanism based on the ServiceSwitchFile option (default: /etc/service.switch). For example, if the service switch lists "files" and "nis" for the aliases service, that will be the default lookup order. the "files" ("local" on DEC) service type expands to any alias files you listed in the configuration file, even if they aren't actually file lookups. Option I (NameServerOptions) no longer sets the "UseNameServer" variable which tells whether or not DNS should be considered canonical. This is now determined based on whether or not "dns" is in the service list for "hosts". Add preliminary support for the ESMTP "DSN" extension (Delivery Status Notifications). DSN notifications override Return-Receipt-To: headers, which are bogus anyhow -- support for them has been removed. Add T=mts-name-type/address-type/diagnostic-type keyletter to mailer definitions to define the types used in DSN returns for MTA names, addresses, and diagnostics respectively. Extend heuristic to force running in ESMTP mode to look for the five-character string "ESMTP" anywhere in the 220 greeting message (not just the second line). This is to provide better compatibility with other ESMTP servers. Print sequence number of job when running the queue so you can easily see how much progress you have made. Suggested by Peter Wemm of DIALix. Map newlines to spaces in logged message-ids; some versions of syslog truncate the rest of the line after newlines. Suggested by Fletcher Mattox of U. Texas. Move up forking for job runs so that if a message is split into multiple envelopes you don't get "fork storms" -- this also improves the connection cache utilization. Accept "<<>>", "<<<>>>", and so forth as equivalent to "<>" for the purposes of refusing to send error returns. Suggested by Motonori Nakamura of Ritsumeikan University. Relax rules on when a file can be written when referenced from the aliases file: use the default uid/gid instead of the real uid/gid. This allows you to create a file owned by and writable only by the default uid/gid that will work all the time (without having the setuid bit set). Change suggested by Shau-Ping Lo and Andrew Cheng of Sun Microsystems. Add "DialDelay" option (no short name) to provide an "extra" delay for dial on demand systems. If this is non-zero and a connect fails, sendmail will wait this long and then try again. If it takes longer than the kernel timeout interval to establish the connection, this option can give the network software time to establish the link. The default units are seconds. Move logging of sender information to be as early as possible; previously, it could be delayed a while for SMTP mail sent to aliases. Suggested by Brad Knowles of the Defense Information Systems Agency. Call res_init() before setting RES_DEBUG; this is required by BIND 4.9.3, or so I'm told. From Douglas Anderson of the National Computer Security Center. Add xdelay= field in logs -- this is a transaction delay, telling you how long it took to deliver to this address on the last try. It is intended to be used for sorting mailing lists to favor "quick" addresses. Provided for use by the mailprio scripts (see below). If a map cannot be opened, and that map is non-optional, and an address requires that map for resolution, queue the map instead of bouncing it. This involves creating a pseudo-class of maps called "bogus-map" -- if a required map cannot be opened, the class is changed to bogus-map; all queries against bogus-map return "tempfail". The bogus-map class is not directly accessible. A sample implementation was donated by Jem Taylor of Glasgow University Computing Service. Fix a possible core dump when mailing to a program that talks SMTP on its standard input. Fix from Keith Moore of the University of Kentucky. Make it possible to resolve filenames to $#local $: @ /filename; previously, the "@" would cause it to not be recognized as a file. Problem noted by Brian Hill of U.C. Davis. Accept a -1 signal to re-exec the daemon. This only works if argv[0] is a full path to sendmail. Fix bug in "addr=..." field in O option on little-endian machines -- the network number wasn't being converted to network byte order. Patch from Kurt Lidl of Pix Technologies Corporation. Pre-initialize the resolver early on; this is to avoid a bug with BIND 4.9.3 that can cause the _res.retry field to get reset to zero, causing all name server lookups to time out. Fix from Matt Day of Artisoft. Restore T line (trusted users) in config file -- but instead of locking out the -f flag, they just tell whether or not an X-Authentication-Warning: will be added. This really just creates new entries in class 't', so "Ft/file/name" can be used to read trusted user names from a file. Trusted users are also allowed to execute programs even if they have a shell that isn't in /etc/shells. Improve NEWDB alias file rebuilding so it will create them properly if they do not already exist. This had been a MAYBENEXTRELEASE feature in 8.6.9. Check for @:@ entry in NIS maps before starting up to avoid (but not prevent, sigh) race conditions. This ought to be handled properly in ypserv, but isn't. Suggested by Michael Beirne of Motorola. Refuse connections if there isn't enough space on the filesystem holding the queue. Contributed by Robert Dana of Wolf Communications. Skip checking for directory permissions in the path to a file when checking for file permissions iff setreuid() succeeded -- it is unnecessary in that case. This avoids significant performance problems when looking for .forward files. Based on a suggestion by Win Bent of USC. Allow symbolic ruleset names. Syntax can be "Sname" to get an arbitrary ruleset number assigned or "Sname = integer" to assign a specific ruleset number. Reference is $>name_or_number. Names can be composed of alphas, digits, underscore, or hyphen (first character must be non-numeric). Allow -o flag on AliasFile lines to make the alias file optional. From Bryan Costales of ICSI. Add NoRecipientAction option to handle the case where there is no legal recipient header in the message. It can take on values: None Leave the message as is. The message will be passed on even though it is in technically illegal syntax. Add-To Add a To: header with any recipients that it can find from the envelope. This risks exposing Bcc: recipients. Add-Apparently-To Add an Apparently-To: header. This has almost no redeeming social value, and is provided only for back compatibility. Add-To-Undisclosed Add a header reading To: undisclosed-recipients:; which will have the effect of making the message legal without exposing Bcc: recipients. Add-Bcc To add an empty Bcc: header. There is a chance that mailers down the line will delete this header, which could cause exposure of Bcc: recipients. The default is NoRecipientAction=None. Truncate (rather than delete) Bcc: lines in the header. This should prevent later sendmails (at least, those that don't themselves delete Bcc:) from considering this message to be non-conforming -- although it does imply that non-blind recipients can see that a Bcc: was sent, albeit not to whom. Add SafeFileEnvironment option. If declared, files named as delivery targets must be regular files in addition to the regular checks. Also, if the option is non-null then it is used as the name of a directory that is used as a chroot(2) environment for the delivery; the file names listed in an alias or forward should include the name of this root. For example, if you run with O SafeFileEnvironment=/arch then aliases should reference "/arch/rest/of/path". If a value is given, sendmail also won't try to save to /usr/tmp/dead.letter (instead it just leaves the job in the queue as Qfxxxxxx). Inspired by *Hobbit*'s sendmail patch kit. Support -A flag for alias files; this will comma concatenate like entries. For example, given the aliases: list: member1 list: member2 and an alias file declared as: OAhash:-A /etc/aliases the final alias inserted will be "list: member1,member2"; without -A you will get an error on the second and subsequent alias for "list". Contributed by Bryan Costales of ICSI. Line-buffer transcript file. Suggested by Liudvikas Bukys. Fix a problem that could cause very long addresses to core dump in some special circumstances. Problem pointed out by Allan Johannesen. (Internal change.) Change interface to expand() (macro expansion) to be simpler and more consistent. Delete check for funny qf file names. This didn't really give any extra security and caused some people some problems. (If you -really- want this, define PICKY_QF_NAME_CHECK at compile time.) Suggested by Kyle Jones of UUNET. (Internal change.) Change EF_NORETURN to EF_NO_BODY_RETN and merge with DSN code; this is simpler and more consistent. This may affect some people who have written their own checkcompat() routine. (Internal change.) Eliminate `D' line in qf file. The df file is now assumed to be the same name as the qf file (with the `q' changed to a `d', of course). Avoid forking for delivery if all recipient mailers are marked as "expensive" -- this can be a major cost on some systems. Essentially, this forces sendmail into "queue only" mode if all it is going to do is queue anyway. Avoid sending a null message in some rather unusual circumstances (specifically, the RCPT command returns a temporary failure but the connection is lost before the DATA command). Fix from Scott Hammond of Secure Computing Corporation. Change makesendmail to use a somewhat more rational naming scheme: Makefiles and obj directories are named $os.$rel.$arch, where $os is the operating system (e.g., SunOS), $rel is the release number (e.g., 5.3), and $arch is the machine architecture (e.g., sun4). Any of these can be omitted, and anything after the first dot in a release number can be replaced with "x" (e.g., SunOS.4.x.sun4). The previous version used $os.$arch.$rel and was rather less general. Change makesendmail to do a "make depend" in the target directory when it is being created. This involves adding an empty "depend:" entry in most Makefiles. Ignore IDENT return value if the OSTYPE field returns "OTHER", as indicated by RFC 1413. Pointed out by Kari Hurtta of the Finnish Meteorological Institute. Fix problem that could cause multiple responses to DATA command on header syntax errors (e.g., lines beginning with colons). Problem noted by Jens Thomassen of the University of Oslo. Don't let null bytes in headers cause truncation of the rest of the header. Log Authentication-Warning:s. Suggested by Motonori Nakamura. Increase timeouts on message data puts to allow time for receivers to canonify addresses in headers on the fly. This is still a rather ugly heuristic. From Motonori Nakamura. Add "HasWildcardMX" suboption to ResolverOptions; if set, MX records are not used when canonifying names, and when MX lookups are done for addressing they must be fully qualified. This is useful if you have a wildcard MX record, although it may cause other problems. In general, don't use wildcard MX records. Patch from Motonori Nakamura. Eliminate default two-line SMTP greeting message. Instead of adding an extra "ESMTP spoken here" line, the word "ESMTP" is added between the first and second word of the first line of the greeting message (i.e., immediately after the host name). This eliminates the need for the BROKEN_SMTP_PEERS compile flag. Old sendmails won't see the ESMTP, but that's acceptable because SIZE was the only useful extension that old sendmails understand. Avoid gethostbyname calls on UNIX domain sockets during SIGUSR1 invoked state dumps. From Masaharu Onishi. Allow on-line comments in .forward and :include: files; they are introduced by the string "#@#", where is a space or a tab. This is intended for native representation of non-ASCII sets such as Japanese, where existing encodings would be unreadable or would lose data -- for example, NAKAMURA Motonori (romanized/less information) =?ISO-2022-JP?B?GyRCQ2ZCPBsoQg==?= =?ISO-2022-JP?B?GyRCQUdFNRsoQg==?= (with MIME encoding, not human readable) #@# ^[$BCfB<^[(B ^[$BAGE5^[(B (native encoding with ISO-2022-JP) The last form is human readable in the Japanese environment. Based on a fix from (surprise!) Motonori Nakamura. Don't make SMTP error returns on MAIL FROM: line be "sticky" for all messages to that host; these are most frequently associated with addresses rather than the host, with the exception of 421 (service shutting down). The effect was to cause queues to sometimes take an excessive time to flush. Reported by Robert Sargent of Southern Geographics Technologies and Eric Prestemon of American University. Add Nice=N mailer option to set the niceness at which a mailer will run. This is actually a relative niceness (that is, an increment on the background value). Log queue runs that are skipped due to high loads. They are logged at LOG_INFO priority iff the log level is > 8. Contributed by Bruce Nagel of Data General. Allow the error mailer to accept a DSN-style error status code instead of an sysexits status code in the host part. Anything with a dot will be interpreted as a DSN-style code. Add new mailer flag: F=3 will tell translations to Quoted-Printable to encode characters that might be munged by an EBCDIC system in addition to the set required by RFC 1521. The additional characters are !, ", #, $, @, [, \, ], ^, `, {, |, }, and ~. (Think of "IBM 360" as the mnemonic for this flag.) Change check for mailing to files to look for a pathname of [FILE] rather than looking for the mailer named *file*. The mapping of leading slashes still goes to the *file* mailer. This allows you to implement the *file* mailer as a separate program, for example, to insert a Content-Length: header or do special security policy. However, note that the usual initial checking for the file permissions is still done, and the program in question needs to be very careful about how it does the file write to avoid security problems. Be able to read ~root/.forward even if the path isn't accessible to regular users. This is disrecommended because sendmail sometimes does not run as root (e.g., when an unsafe option is specified on the command line), but should otherwise be safe because .forward files must be owned by the user for whom mail is being forwarded, and cannot be a symbolic link. Suggested by Forrest Aldrich of Wang Laboratories. Add new "HostsFile" option that is the pathname to the /etc/hosts file. This is used for canonifying hostnames when the service type is "files". Implement programs on F (read class from file) line. The syntax is Fc|/path/to/program to read the output from the program into class "c". Probe the network interfaces to find alternate names for this host. Requires the SIOCGIFCONF ioctl call. Code contributed by SunSoft. Add "E" configuration line to set or propogate environment variables into children. "E" will propogate the named variable from the environment when sendmail was invoked into any children it calls; "E=" sets the named variable to the indicated value. Any variables not explicitly named will not be in the child environment. However, sendmail still forces an "AGENT=sendmail" environment variable, in part to enforce at least one environment variable, since many programs and libraries die horribly if this is not guaranteed. Change heuristic for rebuilding both NEWDB and NDBM versions of alias databases -- new algorithm looks for the substring "/yp/" in the file name. This is more portable and involves less overhead. Suggested by Motonori Nakamura. Dynamically allocate the queue work list so that you don't lose jobs in large queue runs. The old QUEUESIZE compile parameter is replaced by QUEUESEGSIZE (the unit of allocation, which should not need to be changed) and the MaxQueueRunSize option, which is the absolute maximum number of jobs that will ever be handled in a single queue run. Based on code contributed by Brian Coan of the Institute for Global Communications. Log message when a message is dropped because it exceeds the maximum message size. Suggested by Leo Bicknell of Virginia Tech. Allow trusted users (those on a T line or in $=t) to use -bs without an X-Authentication-Warning: added. Suggested by Mark Thomas of Mark G. Thomas Consulting. Announce state of compile flags on -d0.1 (-d0.10 throws in the OS-dependent defines). The old semantic of -d0.1 to not run the daemon in background has been moved to -d99.100, and the old 52.5 flag (to avoid disconnect() from closing all output files) has been moved to 52.100. This makes things more consistent (flags below .100 don't change semantics) and separates out the backgrounding so that it doesn't happen automatically on other unrelated debugging flags. If -t is used but no addresses are found in the header, give an error message rather than just doing nothing. Fix from Motonori Nakamura. On systems (like SunOS) where the effective gid is not necessarily included in the group list returned by getgroups(), the `restrictmailq' option could sometimes cause an authorized user to not be able to use `mailq'. Fix from Charles Hannum of MIT. Allow symbolic service names for [IPC] mailers. Suggested by Gerry Magennis of Logica International. Add DontExpandCnames option to prevent $[ ... $] from expanding CNAMEs when running DNS. For example, if the name FTP.Foo.ORG is a CNAME for Cruft.Foo.ORG, then when sitting on a machine in the Foo.ORG domain a lookup of "FTP" returns "Cruft.Foo.ORG" if this option is not set, or "FTP.Foo.ORG" if it is set. This is technically illegal under RFC 822 and 1123, but the IETF is moving toward legalizing it. Note that turning on this option is not sufficient to guarantee that a downstream neighbor won't rewrite the address for you. Add "-m" flag to makesendmail script -- this tells you what object directory and Makefile it will use, but doesn't actually do the make. Do some additional checking on the contents of the qf file to try to detect attacks against the qf file. In particular, abort on any line beginning "From ", and add an "end of file" line -- any data after that line is prohibited. Always use /etc/sendmail.cf, regardless of the arbitrary vendor choices. This can be overridden in the Makefile by using either -DUSE_VENDOR_CF_PATH to get the vendor location (to the extent that we know it) or by defining _PATH_SENDMAILCF (which is a "hard override"). This allows sendmail 8 to have more consistent installation instructions. Allow macros on `K' line in config file. Suggested by Andrew Chang of Sun Microsystems. Improved symbol table hash function from Eric Wassenaar. This one is at least 50% faster. Fix problem that didn't notice that timeout on file open was a transient error. Fix from Larry Parmelee of Cornell University. Allow comments (lines beginning with a `#') in files read for classes. Suggested by Motonori Nakamura. Make SIGINT (usually ^C) in test mode return to the prompt instead of dropping out entirely. This makes testing some of the name server lookups easier to deal with when there are hung servers. From Motonori Nakamura. Add new ${opMode} macro that is set to the current operation mode (e.g., `s' for -bs, `t' for -bt, etc.). Suggested by Claude Marinier . Add new delivery mode (Odd) that defers all map lookups to queue runs. Kind of like queue-only mode (Odq) except it tries to avoid any external service requests; for dial-on-demand hosts that want to minimize DNS lookups when mail is being queued. For this to work you will also have to make sure that gethostbyname of your local host name does not do a DNS lookup. Improved handling of "out of space" conditions from John Myers of Carnegie Mellon. Improved security for mailing to files on systems that have fchmod(2) support. Improve "cannot send message for N days" message -- now says "could not send for past N days". Suggested by Tom Moore of AT&T Global Information Solutions. Less misleading Subject: line on messages sent to postmaster only. From Motonori Nakamura. Avoid duplicate error messages on bad command line flags. From Motonori Nakamura. Better error message for case where ruleset 0 falls off the end or otherwise does not resolve to a canonical triple. Fix a problem that could cause multiple bounce messages if a bad address was sent along with a good address to an SMTP site where that SMTP site returned a 4yz code in response to the final dot of the data. Problem reported by David James of British Telecom. Add "volatile" declarations so that gcc -O2 will work. Patches from Alexander Dupuy of System Management ARTS. Delete duplicates in MX lists -- believe it or not, there are sites that list the same host twice in an MX list. This deletion only works on adjacent preferences, so an MX list that had A=5, B=10, A=15 would leave both As, but one that had A=5, A=10, B=15 would reduce to A, B. This is intentional, just in case there is something weird I haven't thought of. Suggested by Barry Shein of Software Tool & Die. SECURITY: .forward files cannot be symbolic links. If they are, a bad guy can read your private files. PORTABILITY FIXES: Solaris 2 from Rob McMahon . System V Release 4 from Motonori Nakamura of Ritsumeikan University. This expands the disk size checking to include all (?) SVR4 configurations. System V Release 4 from Kimmo Suominen -- initgroups(3) and setrlimit(2) are both available. System V Release 4 from sob@sculley.ffg.com -- some versions apparently "have EX_OK defined in other headerfiles." Linux Makefile typo. Linux getusershell(3) is broken in Slackware 2.0 -- from Andrew Pam of Xanadu Australia. More Linux tweaking from John Kennedy of California State University, Chico. Cray changes from Eric Wassenaar: ``On Cray, shorts, ints, and longs are all 64 bits, and all structs are multiples of 64 bits. This means that the sizeof operator returns only multiples of 8. This requires adaptation of code that really deals with 32 bit or 16 bit fields, such as IP addresses or nameserver fields.'' DG/UX 5.4.3 from Mark T. Robinson . To get the old behaviour, use -DDGUX_5_4_2. DG/UX hack: add _FORCE_MAIL_LOCAL_=yes environment variable to fix bogus /bin/mail behaviour. Tandem NonStop-UX from Rick McCarty . This also cleans up some System V Release 4 compile problems. Solaris 2: sendmail.cw file should be in /etc/mail to match all the other configuration files. Fix from Glenn Barry of Emory University. Solaris 2.3: compile problem in conf.c. Fix from Alain Nissen of the University of Liege, Belgium. Ultrix: freespace calculation was incorrect. Fix from Takashi Kizu of Osaka University. SVR4: running in background gets a SIGTTOU because the emulation code doesn't realize that "getpeername" doesn't require reading the file. Fix from Peter Wemm of DIALix. Solaris 2.3: due to an apparent bug in the socket emulation library, sockets can get into a "wedged" state where they just return EPROTO; closing and re-opening the socket clears the problem. Fix from Bob Manson of Ohio State University. Hitachi 3050R & 3050RX running HI-UX/WE2: portability fixes from Akihiro Hashimoto ("Hash") of Chiba University. AIX changes to allow setproctitle to work from Rainer Schöpf of Zentrum für Datenverarbeitung der Universität Mainz. AIX changes for load average from Ed Ravin of NASA/Goddard. SCO Unix from Chip Rosenthal of Unicom (code was using the wrong statfs call). ANSI C fixes from Adam Glass (NetBSD project). Stardent Titan/ANSI C fixes from Kate Hedstrom of Rutgers University. DG-UX fixes from Bruce Nagel of Data General. IRIX64 updates from Mark Levinson of the University of Rochester Medical Center. Altos System V (``the first UNIX/XENIX merge the Altos did for their Series 1000 & Series 2000 line; their merged code was licenced back to AT&T and Microsoft and became System V release 3.2'') from Tim Rice . OSF/1 running on Intel Paragon from Jeff A. Earickson of Intel Scalable Systems Divison. Amdahl UTS System V 2.1.5 (SVr3-based) from Janet Jackson . System V Release 4 (statvfs semantic fix) from Alain Durand of I.M.A.G. HP-UX 10.x multiprocessor load average changes from Scott Hutton and Jeff Sumler of Indiana University. Cray CSOS from Scott Bolte of Cray Computer Corporation. Unicos 8.0 from Douglas K. Rand of the University of North Dakota, Scientific Computing Center. Solaris 2.4 fixes from Sanjay Dani of Dani Communications. ConvexOS 11.0 from Christophe Wolfhugel. IRIX 4.0.5 from David Ashton-Reader of CADcentre. ISC UNIX from J. J. Bailey. HP-UX 9.xx on the 8xx series machines from Remy Giraud of Meteo France. HP-UX configuration from Tom Lane . IRIX 5.2 and 5.3 from Kari E. Hurtta. FreeBSD 2.0 from Mike Hickey of Federal Data Corporation. Sony NEWS-OS 4.2.1R and 6.0.3 from Motonori Nakamura. Omron LUNA unios-b, mach from Motonori Nakamura. NEC EWS-UX/V 4.2 from Motonori Nakamura. NeXT 2.1 from Bryan Costales. AUX patch thanks to Mike Erwin of Apple Computer. HP-UX 10.0 from John Beck of Hewlett-Packard. Ultrix: allow -DBROKEN_RES_SEARCH=0 if you are using a non-DEC resolver. Suggested by Allan Johannesen. UnixWare 2.0 fixes from Petr Lampa of the Technical University of Brno (Czech Republic). KSR OS 1.2.2 support from Todd Miller of the University of Colorado. UX4800 support from Kazuhisa Shimizu of NEC. MAKEMAP: allow -d flag to allow insertion of duplicate aliases in type ``btree'' maps. The semantics of this are undefined for regular maps, but it can be useful for the user database. MAKEMAP: lock database file while rebuilding to avoid sendmail lookups while the rebuild is going on. There is a race condition between the open(... O_TRUNC ...) and the lock on the file, but it should be quite small. SMRSH: sendmail restricted shell added to the release. This can be used as an alternative to /bin/sh for the "prog" mailer, giving the local administrator more control over what programs can be run from sendmail. MAIL.LOCAL: add this local mailer to the tape. It is not really part of the release proper, and isn't fully supported; in particular, it does not run on System V based systems and never will. CONTRIB: a patch to rmail.c from Bill Gianopoulos of Raytheon to allow rmail to compile on systems that don't have function prototypes and systems that don't have snprintf. CONTRIB: add the "mailprio" scripts that will help you sort mailing lists by transaction delay times so that addresses that respond quickly get sent first. This is to prevent very sluggish servers from delaying other peoples' mail. Contributed by Tony Sanders of BSDI. CONTRIB: add the "bsdi.mc" file as contributed by Tony Sanders of BSDI. This has a lot of comments to help people out. CONFIG: Don't have .mc files include(../m4/cf.m4) -- instead, put this on the m4 command line. On GNU m4 (which supports the __file__ primitive) you can run m4 in an arbitrary directory -- use either: m4 ${CFDIR}/m4/cf.m4 config.mc > config.cf or m4 -I${CFDIR} m4/cf.m4 config.mc > config.cf On other versions of m4 that don't support __file__, you can use: m4 -D_CF_DIR_=${CFDIR}/ ${CFDIR}/m4/cf.m4 ... (Note the trailing slash on the _CF_DIR_ definition.) Old versions of m4 will default to _CF_DIR_=.. for back compatibility. CONFIG: fix mail from <> so it will properly convert to MAILER-DAEMON on local addresses. CONFIG: fix code that was supposed to catch colons in host names. Problem noted by John Gardiner Myers of CMU. CONFIG: allow use of SMTP_MAILER_MAX in nullclient configuration. From Paul Riddle of the University of Maryland, Baltimore County. CONFIG: Catch and reject "." as a host address. CONFIG: Generalize domaintable to look up all domains, not just unqualified ones. CONFIG: Delete OLD_SENDMAIL support -- as near as I can tell, it was never used and didn't work anyway. CONFIG: Set flags A, w, 5, :, /, |, and @ on the "local" mailer and d on all mailers in the UUCP class. CONFIG: Allow "user+detail" to be aliased specially: it will first look for an alias for "user+detail", then for "user+*", and finally for "user". This is intended for forwarding mail for system aliases such as root and postmaster to a centralized hub. CONFIG: add confEIGHT_BIT_HANDLING to set option 8 (see above). CONFIG: add smtp8 mailer; this has the F=8 (just-send-8) flag set. The F=8 flag is also set on the "relay" mailer, since this is expected to be another sendmail. CONFIG: avoid qualifying all UUCP addresses sent via SMTP with the name of the UUCP_RELAY -- in some cases, this is the wrong value (e.g., when we have local UUCP connections), and this can create unreplyable addresses. From Chip Rosenthal of Unicom. CONFIG: add confRECEIVED_HEADER to change the format of the Received: header inserted into all messages. Suggested by Gary Mills of the University of Manitoba. CONFIG: Make "notsticky" the default; use FEATURE(stickyhost) to get the old behaviour. I did this upon observing that almost everyone needed this feature, and that the concept I was trying to make happen didn't work with some user agents anyway. FEATURE(notsticky) still works, but it is a no-op. CONFIG: Add LUSER_RELAY -- the host to which unrecognized user names are sent, rather than immediately diagnosing them as User Unknown. CONFIG: Add SMTP_MAILER_ARGS, ESMTP_MAILER_ARGS, SMTP8_MAILER_ARGS, and RELAY_MAILER_ARGS to set the arguments for the indicated mailers. All default to "IPC $h". Patch from Larry Parmelee of Cornell University. CONFIG: pop mailer needs F=n flag to avoid "annoying side effects on the client side" and F=P to get an appropriate return-path. From Kimmo Suominen. CONFIG: add FEATURE(local_procmail) to use the procmail program as the local mailer. For addresses of the form "user+detail" the "detail" part is passed to procmail via the -a flag. Contributed by Kimmo Suominen. CONFIG: add MAILER(procmail) to add an interface to procmail for use from mailertables. This lets you execute arbitrary procmail scripts. Contributed by Kimmo Suominen. CONFIG: add T= fields (MTS type) to local, smtp, and uucp mailers. CONFIG: add OSTYPE(ptx2) for DYNIX/ptx 2.x from Sequent. From Paul Southworth of CICNet Systems Support. CONFIG: use -a$g as default to UUCP mailers, instead of -a$f. This causes the null return path to be rewritten as MAILER-DAEMON; otherwise UUCP gets horribly confused. From Michael Hohmuth of Technische Universitat Dresden. CONFIG: Add FEATURE(bestmx_is_local) to cause any hosts that list us as the best possible MX record to be treated as though they were local (essentially, assume that they are included in $=w). This can cause additional DNS traffic, but is easier to administer if this fits your local model. It does not work reliably if there are multiple hosts that share the best MX preference. Code contributed by John Oleynick of Rutgers. CONFIG: Add FEATURE(smrsh) to use smrsh (the SendMail Restricted SHell) instead of /bin/sh as the program used for delivery to programs. If an argument is included, it is used as the path to smrsh; otherwise, /usr/local/etc/smrsh is assumed. CONFIG: Add LOCAL_MAILER_MAX and PROCMAILER_MAILER_MAX to limit the size of messages to the local and procmail mailers respectively. Contributed by Brad Knowles of the Defense Information Systems Agency. CONFIG: Handle leading ``phrase:'' and trailing ``;'' as comments (just like text outside of angle brackets) in order to properly deal with ``group: addr1, ... addrN;'' syntax. CONFIG: Require OSTYPE macro (the defaults really don't apply to any real systems any more) and tweak the DOMAIN macro so that it is less likely that users will accidently use the Berkeley defaults. Also, create some generic files that really can be used in the real world. CONFIG: Add new configuration macros to set character sets for messages _arriving from_ various mailers: LOCAL_MAILER_CHARSET, SMTP_MAILER_CHARSET, and UUCP_MAILER_CHARSET. CONFIG: Change UUCP_MAX_SIZE to UUCP_MAILER_MAX for consistency. The old name will still be accepted for a while at least. CONFIG: Implement DECNET_RELAY as spec for host to which DECNET mail (.DECNET pseudo-domain or node::user) will be sent. As with all relays, it can be ``mailer:hostname''. Suggested by Scott Hutton. CONFIG: Add MAILER(mail11) to get DECnet support. Code contributed by Barb Dijker of Labyrinth Computer Services. CONFIG: change confCHECK_ALIASES to default to False -- it has poor performance for large alias files, and this confused many people. CONFIG: Add confCF_VERSION to append local information to the configuration version number displayed during SMTP startup. CONFIG: fix some.newsgroup.usenet@local.host syntax (previously it would only work when locally addressed. Fix from Edvard Tuinder of Cistron Internet Services. CONFIG: use ${opMode} to avoid error on .REDIRECT addresses if option "n" (CheckAlaises) is set when rebuilding alias database. Based on code contributed by Claude Marinier. CONFIG: Allow mailertable to have values of the form ``error:code message''. The ``code'' is a status code derived from the sysexits codes -- e.g., NOHOST or UNAVAILABLE. Contributed by David James . CONFIG: add MASQUERADE_DOMAIN(domain list) to extend the list of sender domains that will be replaced with the masquerade name. These domains will not be treated as local, but if mail passes through with sender addresses in those domains they will be replaced by the masquerade name. These can also be specified in a file using MASQUERADE_DOMAIN_FILE(filename). CONFIG: add FEATURE(masquerade_envelope) to masquerade the envelope as well as the header. Substantial improvements to this code were contributed by Per Hedeland. CONFIG: add MAILER(phquery) to define a new "ph" mailer; this can be accessed from a mailertable to do CCSO ph lookups. Contributed by Kimmo Suominen. CONFIG: add MAILER(cyrus) to define a new Cyrus mailer; this can be used to define cyrus and cyrusbb mailers (for IMAP support). Contributed by John Gardiner Myers of Carnegie Mellon. CONFIG: add confUUCP_MAILER to select default mailer to use for UUCP addressing. Suggested by Tom Moore of AT&T GIS. NEW FILES: cf/cf/cs-hpux10.mc cf/cf/cs-solaris2.mc cf/cf/cyrusproto.mc cf/cf/generic-bsd4.4.mc cf/cf/generic-hpux10.mc cf/cf/generic-hpux9.mc cf/cf/generic-osf1.mc cf/cf/generic-solaris2.mc cf/cf/generic-sunos4.1.mc cf/cf/generic-ultrix4.mc cf/cf/huginn.cs.mc cf/domain/berkeley-only.m4 cf/domain/generic.m4 cf/feature/bestmx_is_local.m4 cf/feature/local_procmail.m4 cf/feature/masquerade_envelope.m4 cf/feature/smrsh.m4 cf/feature/stickyhost.m4 cf/feature/use_ct_file.m4 cf/m4/cfhead.m4 cf/mailer/cyrus.m4 cf/mailer/mail11.m4 cf/mailer/phquery.m4 cf/mailer/procmail.m4 cf/ostype/amdahl-uts.m4 cf/ostype/bsdi2.0.m4 cf/ostype/hpux10.m4 cf/ostype/irix5.m4 cf/ostype/isc4.1.m4 cf/ostype/ptx2.m4 cf/ostype/unknown.m4 contrib/bsdi.mc contrib/mailprio contrib/rmail.oldsys.patch mail.local/mail.local.0 makemap/makemap.0 smrsh/README smrsh/smrsh.0 smrsh/smrsh.8 smrsh/smrsh.c src/Makefiles/Makefile.CSOS src/Makefiles/Makefile.EWS-UX_V src/Makefiles/Makefile.HP-UX.10 src/Makefiles/Makefile.IRIX.5.x src/Makefiles/Makefile.IRIX64 src/Makefiles/Makefile.ISC src/Makefiles/Makefile.KSR src/Makefiles/Makefile.NEWS-OS.4.x src/Makefiles/Makefile.NEWS-OS.6.x src/Makefiles/Makefile.NEXTSTEP src/Makefiles/Makefile.NonStop-UX src/Makefiles/Makefile.Paragon src/Makefiles/Makefile.SCO.3.2v4.2 src/Makefiles/Makefile.SunOS.5.3 src/Makefiles/Makefile.SunOS.5.4 src/Makefiles/Makefile.SunOS.5.5 src/Makefiles/Makefile.UNIX_SV.4.x.i386 src/Makefiles/Makefile.uts.systemV src/Makefiles/Makefile.UX4800 src/aliases.0 src/mailq.0 src/mime.c src/newaliases.0 src/sendmail.0 test/t_seteuid.c RENAMED FILES: cf/cf/alpha.mc => cf/cf/s2k-osf1.mc cf/cf/chez.mc => cf/cf/chez.cs.mc cf/cf/hpux-cs-exposed.mc => cf/cf/cs-hpux9.mc cf/cf/osf1-cs-exposed.mc => cf/cf/cs-osf1.mc cf/cf/s2k.mc => cf/cf/s2k-ultrix4.mc cf/cf/sunos4.1-cs-exposed.mc => cf/cf/cs-sunos4.1.mc cf/cf/ultrix4.1-cs-exposed.mc => cf/cf/cs-ultrix4.mc cf/cf/vangogh.mc => cf/cf/vangogh.cs.mc cf/domain/Berkeley.m4 => cf/domain/Berkeley.EDU.m4 cf/domain/cs-exposed.m4 => cf/domain/CS.Berkeley.EDU.m4 cf/domain/eecs-hidden.m4 => cf/domain/EECS.Berkeley.EDU.m4 cf/domain/s2k.m4 => cf/domain/S2K.Berkeley.EDU.m4 cf/ostype/hpux.m4 => cf/ostype/hpux9.m4 cf/ostype/irix.m4 => cf/ostype/irix4.m4 cf/ostype/ultrix4.1.m4 => cf/ostype/ultrix4.m4 src/Makefile.* => src/Makefiles/Makefile.* src/Makefile.AUX => src/Makefiles/Makefile.A-UX src/Makefile.BSDI => src/Makefiles/Makefile.BSD-OS src/Makefile.DGUX => src/Makefiles/Makefile.dgux src/Makefile.RISCos => src/Makefiles/Makefile.UMIPS src/Makefile.SunOS.4.0.3 => src/Makefiles/Makefile.SunOS.4.0 OBSOLETED FILES: cf/cf/cogsci.mc cf/cf/cs-exposed.mc cf/cf/cs-hidden.mc cf/cf/hpux-cs-hidden.mc cf/cf/knecht.mc cf/cf/osf1-cs-hidden.mc cf/cf/sunos3.5-cs-exposed.mc cf/cf/sunos3.5-cs-hidden.mc cf/cf/sunos4.1-cs-hidden.mc cf/cf/ultrix4.1-cs-hidden.mc cf/domain/cs-hidden.m4 contrib/rcpt-streaming src/Makefiles/Makefile.SunOS.5.x 8.6.13/8.6.12 96/01/25 SECURITY: In some cases it was still possible for an attacker to insert newlines into a queue file, thus allowing access to any user (except root). CONFIG: no changes -- it is not a bug that the configuration version number is unchanged. 8.6.12/8.6.12 95/03/28 Fix to IDENT code (it was getting the size of the reply buffer too small, so nothing was ever accepted). Fix from several people, including Allan Johannesen, Shane Castle of the Boulder County Information Services, and Jeff Smith of Warwick University (all arrived within a few hours of each other!). Fix a problem that could cause large jobs to run out of file descriptors on systems that use vfork() rather than fork(). 8.6.11/8.6.11 95/03/08 The ``possible attack'' message would be logged more often than necessary if you are using Pine as a user agent. The wrong host would be reported in the ``possible attack'' message when attempted from IDENT. In some cases the syslog buffer could be overflowed when reporting the ``possible attack'' message. This can cause denial of service attacks. Truncate the message to 80 characters to prevent this problem. When reading the IDENT response a loop is needed around the read from the network to ensure that you don't get partial lines. Password entries without any shell listed (that is, a null shell) wouldn't match as "ok". Problem noted by Rob McMahon. When running BIND 4.9.x a problem could occur because the _res.options field is initialized differently than it was historically -- this requires that sendmail call res_init before it tweaks any bits. Fix an incompatibility in openxscript() between the file open mode and the stdio mode passed to fdopen. This caused UnixWare 2.0 to have conniptions. Fix from Martin Sohnius of Novell Labs Europe. Fix problem with static linking of local getopt routine when using GNU's ld command. Fix from John Kennedy of Cal State Chico. It was possible to turn off privacy flags. Problem noted by *Hobbit*. Be more paranoid about writing files. Suggestions by *Hobbit* and Liudvikas Bukys. MAKEMAP: fixes for 64 bit machines (DEC Alphas in particular) from Spider Boardman. CONFIG: No changes (version number only, to keep it in sync with the binaries). 8.6.10/8.6.10 95/02/10 SECURITY: Diagnose bogus values to some command line flags that could allow trash to get into headers and qf files. Validate the name of the user returned by the IDENT protocol. Some systems that really dislike IDENT send intentionally bogus information. Problem pointed out by Michael Bushnell of the Free Software Foundation. Has some security implications. Fix a problem causing error messages about DNS problems when the host name contained a percent sign to act oddly because it was passed as a printf-style format string. In some cases this could cause core dumps. Avoid possible buffer overrun in returntosender() if error message is quite ling. From Fletcher Mattox of the University of Texas. Fix a problem that would silently drop "too many hops" error messages if and only if you were sending to an alias. From Jon Giltner of the University of Colorado and Dan Harton of Oak Ridge National Laboratory. Fix a bug that caused core dumps on some systems if -d11.2 was set and e->e_message was null. Fix from Bruce Nagel of Data General. Fix problem that can still cause df files to be left around after "hop count exceeded" messages. Fix from Andrew Chang and Shau-Ping Lo of SunSoft. Fix a problem that can cause buffer overflows on very long user names (as might occur if you piped to a program with a lot of arguments). Avoid returning an error and re-queueing if the host signature is null; this can occur on addresses like ``user@.''. Problem noted by Wesley Craig and the University of Michigan. Avoid possible calls to malloc(0) if MCI caching is turned off. Bug fix from Pierre David of the Laboratoire Parallelisme, Reseaux, Systemes et Modelisation (PRiSM), Universite de Versailles - St Quentin, and Jacky Thibault. Make a local copy of the line being sent via senttolist() -- in some cases, buffers could get trashed by map lookups causing it to do unexpected things. This also simplifies some of the map code. CONFIG: No changes (version number only, to keep it in sync with the binaries). 8.6.9/8.6.9 94/04/19 Do all mail delivery completely disconnected from any terminal. This provides consistency with daemon delivery and may have some security implications. Make sure that malloc doesn't get called with zero size, since that fails on some systems. Reported by Ed Hill of the University of Iowa. Fix multi-line values for $e (SMTP greeting message). Reported by Mike O'Connor of Ford Motor Company. Avoid syserr if no NIS domain name is defined, but the map it is trying to open is optional. From Win Bent of USC. Changes for picky compilers from Ed Gould of Digital Equipment. Hesiod support for UDB from Todd Miller of the University of Colorado. Use "hesiod" as the service name in the U option. Fix a problem that failed to set the "authentic" host name (that is, the one derived from the socket info) if you called sendmail -bs from inetd. Based on code contributed by Todd Miller (this problem was also reported by Guy Helmer of Dakota State University). This also fixes a related problem reported by Liudvikas Bukys of the University of Rochester. Parameterize "nroff -h" in all the Makefiles so people with variant versions can use them easily. Suggested by Peter Collinson of Hillside Systems. SMTP "MAIL" commands with multiple ESMTP parameters required two spaces between parameters instead of one. Reported by Valdis Kletnieks of Virginia Tech. Reduce the number of system calls during message collection by using global timeouts around the collect() loop. This code was contributed by Eric Wassenaar. If the initial hostname name gathering results in a name without a dot (usually caused by NIS misconfiguration) and BIND is compiled in, directly access DNS to get the canonical name. This should make life easier for Solaris systems. If it still can't be resolved, and if the name server is listed as "required", try again in 30 seconds. If that also fails, exit immediately to avoid bogus "config error: mail loops back to myself" messages. Improve the "MAIL DELETED BECAUSE OF LACK OF DISK SPACE" error message to explain how much space was available and sound a bit less threatening. Suggested by Stan Janet of the National Institute of Standards and Technology. If mail is delivered to an alias that has an owner, deliver any requested return-receipt immediately, and strip the Return-Receipt-To: header from the subsequent message. This prevents a certain class of denial of service attack, arguably gives more reasonable semantics, and moves things more towards what will probably become a network standard. Suggested by Christopher Davis of Kapor Enterprises. Add a "noreceipts" privacy flag to turn off all return receipts without recompiling. Avoid printing ESMTP parameters as part of the error message if there are errors during parsing. This change is purely cosmetic. Avoid sending out error messages during the collect phase of SMTP; there is an MVS mailer from UCLA that gets confused by this. Of course, I think it's their bug.... Check for the $j macro getting undefined, losing a dot, or getting lost from $=w in the daemon before accepting a connection; if it is, it dumps state, prints a LOG_ALERT message, and drops core for debugging. This is an attempt to track down a bug that I thought was long since gone. If you see this, please forward the log fragment to sendmail@sendmail.ORG. Change OLD_NEWDB from a #ifdef to a #if so it can be turned off with -DOLD_NEWDB=0 on the command line. From Christophe Wolfhugel. Instead of trying to truncate the listen queue for the server SMTP port when the load average is too high, just close the port completely and reopen it later as needed. This ensures that the other end gets a quick "connection refused" response, and that the connection can be recovered later. In particular, some socket emulations seem to get confused if you tweak the listen queue size around and can never start listening to connections again. The down side is that someone could start up another daemon process in the interim, so you could have multiple daemons all not listening to connections; this could in turn cause the sendmail.pid file to be incorrect. A better approach might be to accept the connection and give a 421 code, but that could break other mailers in mysterious ways and have paging behaviour implications. Fix a glitch in TCP-level debugging that caused flag 16.101 to set debugging on the wrong socket. From Eric Wassenaar. When creating a df* temporary file, be sure you truncate any existing data in the file -- otherwise system crashes and the like could result in extra data being sent. DOC: Replace the CHANGES-R5-R8 readme file with a paper in the doc directory. This includes some additional information. CONFIG: change UUCP rules to never add $U! or $k! on the front of recipient envelope addresses. This should have been handled by the $&h trick, but broke if people were mixing domainized and UUCP addresses. They should probably have converted all the way over to uucp-uudom instead of uucp-{new,old}, but the failure mode was to loop the mail, which was bad news. Portability fixes: Newer BSDI systems (several people). Older BSDI systems from Christophe Wolfhugel. Intergraph CLIX, from Paul Southworth of CICNet. UnixWare, from Evan Champion. NetBSD from Adam Glass. Solaris from Quentin Campbell of the University of Newcastle upon Tyne. IRIX from Dean Cookson and Bill Driscoll of Mitre Corporation. NCR 3000 from Kevin Darcy of Chrysler Financial Corporation. SunOS (it has setsid() and setvbuf() calls) from Jonathan Kamens of OpenVision Technologies. HP-UX from Tor Lillqvist. New Files: src/Makefile.CLIX src/Makefile.NCR3000 doc/changes/Makefile doc/changes/changes.me doc/changes/changes.ps 8.6.8/8.6.6 94/03/21 SECURITY: it was possible to read any file as root using the E (error message) option. Reported by Richard Jones; fixed by Michael Corrigan and Christophe Wolfhugel. 8.6.7/8.6.6 94/03/14 SECURITY: it was possible to get root access by using weird values to the -d flag. Thanks to Alain Durand of INRIA for forwarding me the notice from the bugtraq list. 8.6.6/8.6.6 94/03/13 SECURITY: the ability to give files away on System V-based systems proved dangerous -- don't run as the owner of a :include: file on a system that allows giveaways. Unfortunately, this also applies to determining a valid shell. IMPORTANT: Previous versions weren't expiring old connections in the connection cache for a long time under some circumstances. This could result in resource exhaustion, both at your end and at the other end. This checks the connections for timeouts much more frequently. From Doug Anderson of NCSC. Fix a glitch that snuck in that caused programs to be run as the sender instead of the recipient if the mail was from a local user to another local user. From Motonori Nakamura of Kyoto University. Fix "wildcard" on /etc/shells matching -- instead of looking for "*", look for "/SENDMAIL/ANY/SHELL/". From Bryan Costales of ICSI. Change the method used to declare the "statfs" availability; instead of HASSTATFS and/or HASUSTAT with a ton of tweaking in conf.c, there is a single #define called SFS_TYPE which takes on one of six values (SFS_NONE for no statfs availability, SFS_USTAT for the ustat(2) syscall, SFS_4ARGS for a four argument statfs(2) call, and SFS_VFS, SFS_MOUNT, or SFS_STATFS for a two argument statfs(2) call with the declarations in , , or respectively). Fix glitch in NetInfo support that could return garbage if there was no "/locations/sendmail" property. From David Meyer of the University of Virginia. Change HASFLOCK from defined/not-defined to a 0/1 definition to allow Linux to turn it off even though it is a BSD-like system. Allow setting of "ident" timeout to zero to turn off the ident protocol entirely. Make 7-bit stripping local to a connection (instead of to a mailer); this allows you to specify that SMTP is a 7-bit channel, but revert to 8-bit should it advertise that it supports 8BITMIME. You still have to specify mailer flag 7 to get this stripping at all. Improve makesendmail script so it handles more cases automatically. Tighten up restrictions on taking ownership of :include: files to avoid problems on systems that allow you to give away files. Fix a problem that made it impossible to rebuild the alias file if it was on a read-only file system. From Harry Edmon of the University of Washington. Improve MX randomization function. From John Gardiner Myers of CMU. Fix a minor glitch causing a bogus message to be printed (used %s instead of %d in a printf string for the line number) when a bad queue file was read. From Harry Edmon. Allow $s to remain NULL on locally generated mail. I'm not sure this is necessary, but a lot of people have complained about it, and there is a legitimate question as to whether "localhost" is legal as an 822-style domain. Fix a problem with very short line lengths (mailer L= flag) in headers. This causes a leading space to be added onto continuation lines (including in the body!), and also tries to wrap headers containing addresses (From:, To:, etc) intelligently at the shorter line lengths. Problem Reported by Lars-Johan Liman of SUNET Operations Center. Log the real user name when logging syserrs, since these can have security implications. Suggested by several people. Fix address logging of cached connections -- it used to always log the numeric address as zero. This is a somewhat bogus implementation in that it does an extra system call, but it should be an inexpensive one. Fix from Motonori Nakamura. Tighten up handling of short syslog buffers even more -- there were cases where the outgoing relay= name was too long to share a line with delay= and mailer= logging. Limit the overhead on split envelopes to one open file descriptor per envelope -- previously the overhead was three descriptors. This was in response to a problem reported by P{r (Pell) Emanuelsson. Fixes to better handle the case of unexpected connection closes; this redirects the output to the transcript so the info is not lost. From Eric Wassenaar. Fix potential string overrun if you macro evaluate a string that has a naked $ at the end. Problem noted by James Matheson . Make default error number on $#error messages 553 (``Requested action not taken: mailbox name not allowed'') instead of 501 (``Syntax error in parameters or arguments'') to avoid bogus "protocol error" messages. Strip off any existing trailing dot on names during $[ ... $] lookup. This prevents it from ending up with two dots on the end of dot terminated names. From Wesley Craig of the University of Michigan and Bryan Costales of ICSI. Clean up file class reading so that the debugging information is more informative. It hadn't been using setclass, so you didn't see the class items being added. Avoid core dump if you are running a version of sendmail where NIS is compiled in, and you specify an NIS map, but NIS is not running. Fix from John Oleynick of Rutgers. Diagnose bizarre case where res_search returns a failure value, but sets h_errno to a success value. Make sure that "too many hops" messages are considered important enough to send an error to the Postmaster (that is, the address specified in the P option). This fix should help problems that cause the df file to be left around sometimes -- unfortunately, I can't seem to reproduce the problem myself. Avoid core dump (null pointer reference) on EXPN command; this only occurred if your log level was set to 10 or higher and the target account was an alias or had a .forward file. Problem noted by Janne Himanka. Avoid "denial of service" attacks by someone who is flooding your SMTP port with bad commands by shutting the connection after 25 bad commands are issued. From Kyle Jones of UUNET. Fix core dump on error messages with very long "to" buffers; fmtmsg overflows the message buffer. Fixed by trimming the to address to 203 characters. Problem reported by John Oleynick. Fix configuration for HASFLOCK -- there were some spots where a #ifndef was incorrectly #ifdef. Pointed out by George Baltz of the University of Maryland. Fix a typo in savemail() that could cause the error message To: lists to be incorrect in some places. From Motonori Nakamura. Fix a glitch that can cause duplicate error messages on split envelopes where an address on one of the lists has a name server failure. Fix from Voradesh Yenbut of the University of Washington. Fix possible bogus pointer reference on ESMTP parameters that don't have an ``=value'' part. CNAME loops caused an error message to be generated, but also re-queued the message. Changed to just re-queue the message (it's really hard to just bounce it because of the weird way the name server works in the presence of CNAME loops). Problem noted by James M.R.Matheson of Cambridge University. Avoid giving ``warning: foo owned process doing -bs'' messages if they use ``MAIL FROM:'' where foo is their true user name. Suggested by Andreas Stolcke of ICSI. Change the NAMED_BIND compile flag to be a 0/1 flag so you can override it easily in the Makefile -- that is, you can turn it off using -DNAMED_BIND=0. If a gethostbyname(...) of an address with a trailing dot fails, try it without the trailing dot. This is because if you have a version of gethostbyname() that falls back to NIS or the /etc/hosts file it will fail to find perfectly reasonable names that just don't happen to be dot terminated in the hosts file. You don't want to strip the dot first though because we're trying to ensure that country names that match one of your subdomains get a chance. PRALIASES: fix bogus output on non-null-terminated strings. From Bill Gianopoulos of Raytheon. CONFIG: Avoid rewriting anything that matches $w to be $j. This was in code intended to only catch the self-literal address (that is, [1.2.3.4], where 1.2.3.4 is your IP address), but the code was broken. However, it will still do this if $M is defined; this is necessary to get client configurations to work (sigh). Note that this means that $M overrides :mailname entries in the user database! Problem noted by Paul Southworth. CONFIG: Fix definition of Solaris help file location. From Steve Cliffe . CONFIG: Fix bug that broke news.group.USENET mappings. CONFIG: Allow declaration of SMTP_MAILER_MAX, FAX_MAILER_MAX, and USENET_MAILER_MAX to tweak the maximum message size for various mailers. CONFIG: Change definition of USENET_MAILER_ARGS to include argv[0] instead of assuming that it is "inews" for consistency with other mailers. From Michael Corrigan of UC San Diego. CONFIG: When mail is forwarded to a LOCAL_RELAY or a MAIL_HUB, qualify the address in the SMTP envelope as user@{relay|hub} instead of user@$j. From Bill Wisner of The Well. CONFIG: Fix route-addr syntax in nullrelay configuration set. CONFIG: Don't turn off case mapping of user names in the local mailer for IRIX. This was different than most every other system. CONFIG: Avoid infinite loops on certainly list:; syntaxes in envelope. Noted by Thierry Besancon . CONFIG: Don't include -z by default on uux line -- most systems don't want it set by default. Pointed out by Philippe Michel of Thomson CSF. CONFIG: Fix some bugs with mailertables -- for example, if your host name was foo.bar.ray.com and you matched against ".ray.com", the old implementation bound %1 to "bar" instead of "foo.bar". Also, allow "." in the mailertable to match anything -- essentially, take over SMART_HOST. This also moves matching of explicit local host names before the mailertable so they don't have to be special cased in the mailertable data. Reported by Bill Gianopoulos of Raytheon; the fix for the %1 binding problem was contributed by Nicholas Comanos of the University of Sydney. CONFIG: Don't include "root" in class $=L (users to deliver locally, even if a hub or relay exists) by default. This is because of the known bug where definition of both a LOCAL_RELAY and a MAIL_HUB causes $=L to ignore both and deliver into the local mailbox. CONFIG: Move up bitdomain and uudomain handling so that they are done before .UUCP class matching; uudomain was reported as ineffective before. This also frees up diversion 8 for future use. Problem reported by Kimmo Suominen. CONFIG: Don't try to convert dotted IP address (e.g., [1.2.3.4]) into host names. As pointed out by Jonathan Kamens, these are often used because either the forward or reverse mapping is broken; this translation makes it broken again. DOC: Clarify $@ and $: in the Install & Op Guide. From Kimmo Suominen. Portability fixes: Unicos from David L. Kensiski of Sterling Sofware. DomainOS from Don Lewis of Silicon Systems. GNU m4 1.0.3 from Karst Koymans of Utrecht University. Convex from Kimmo Suominen . NetBSD from Adam Glass . BSD/386 from Tony Sanders of BSDI. Apollo from Eric Wassenaar. DGUX from Doug Anderson. Sequent DYNIX/ptx 2.0 from Tim Wright of Sequent. NEW FILES: src/Makefile.DomainOS src/Makefile.PTX src/Makefile.SunOS.5.1 src/Makefile.SunOS.5.2 src/Makefile.SunOS.5.x src/mailq.1 cf/ostype/domainos.m4 doc/op/Makefile doc/intro/Makefile doc/usenix/Makefile 8.6.5/8.6.5 94/01/13 Security fix: /.forward could be owned by anyone (the test to allow root to own any file was backwards). From Bob Campbell at U.C. Berkeley. Security fix: group ids were not completely set when programs were invoked. This caused programs to have group permissions they should not have had (usually group daemon instead of their own group). In particular, Perl scripts would refuse to run. Security: check to make sure files that are written are not symbolic links (at least under some circumstances). Although this does not respond to a specific known attack, it's just a good idea. Suggested by Christian Wettergren. Security fix: if a user had an NFS mounted home directory on a system with a restricted shell listed in their /etc/passwd entry, they could still execute any program by putting that in their .forward file. This fix prevents that by insisting that their shell appear in /etc/shells before allowing a .forward to execute a program or write a file. You can disable this by putting "*" in /etc/shells. It also won't permit world-writable :include: files to reference programs or files (there's no way to disable this). These behaviours are only one level deep -- for example, it is legal for a world-writable :include: file to reference an alias that writes a file, on the assumption that the alias file is well controlled. Security fix: root was not treated suspiciously enough when looking into subdirectories. This would potentially allow a cracker to examine files that were publically readable but in a non-publically searchable directory. Fix a problem that causes an error on QUIT on a cached connection to create problems on the current job. These are typically unrelated, so errors occur in the wrong place. Reset CurrentLA in sendall() -- this makes sendmail queue runs more responsive to load average, and fixes a problem that ignored the load average in locally generated mail. From Eric Wassenaar. Fix possible core dump on aliases with null LHS. From John Orthoefer of BB&N. Revert to using flock() whenever possible -- there are just too many bugs in fcntl() locking, particularly over NFS, that cause sendmail to fail in perverse ways. Fix a bug that causes the connection cache to get confused when sending error messages. This resulted in "unexpected close" messages. It should fix itself on the following queue run. Problem noted by Liudvikas Bukys of the University of Rochester. Include $k in $=k as documented in the Install & Op Guide. This seems odd, but it was documented.... From Michael Corrigan of UCSD. Fix problem that caused :include:s from alias files to be forced to be owned by root instead of daemon (actually DefUid). From Tim Irvin. Diagnose unrecognized I option values -- from Mortin Forssen of the Chalmers University of Technology. Make "error" mailer work consistently when there is no error code associated with it -- previously it returned OK even though there was a real problem. Now it assumes EX_UNAVAILABLE. Fix bug that caused the last header line of messages that had no body and which were terminated with EOF instead of "." to be discarded. Problem noted by Liudvikas Bukys. Fix core dump on SMTP mail to programs that failed -- it tried to go to a "next MX host" when none existed, causing a core dump. From der Mouse at McGill University. Change IDENTPROTO from a defined/not defined to a 0/1 switch; this makes it easier to turn it off (using -DIDENTPROTO=0 in the Makefile). From der Mouse. Fix YP_MASTER_NAME store to use the unupdated result of gethostname() (instead of myhostname(), which tries to fully qualify the name) to be consistent with SunOS. If your hostname is unqualified, this fixes transfers to slave servers. Bug noted by Keith McMillan of Ameritech Services, Inc. Fix Ultrix problem: gethostbyname() can return a very large (> 500) h_length field, which causes the sockaddr to be trashed. Use the size of the sockaddr instead. Fix from Bob Manson of Ohio State. Don't assume "-a." on host lookups if NAMED_BIND is not defined -- this confuses gethostbyname on hosts file lookups, which doesn't understand the trailing dot convention. Log SMTP server subprocesses that die with a signal instead of from a clean exit. If you don't have option "I" set, don't assume that a DNS "host unknown" message is authoritative -- it might still be found in /etc/hosts. Fix a problem that would cause Deferred: messages to be sent as the subject of an error message, even though the actual cause of a message was more severe than that. Problem noted by Chris Seabrook of OSSI. Fix race condition in DBM alias file locking. From Kyle Jones of UUNET. Limit delivery syslog line length to avoid bugs in some versions of syslog(3). This adds a new compile time variable SYSLOG_BUFSIZE. From Jay Plett of Princeton University, which is in turn derived from IDA. Fix quotes inside of comments in addresses -- previously it insisted that they be balanced, but the 822 spec says that they should be ignored. Dump open file state to syslog upon receiving SIGUSR1 (for debugging). This also evaluates ruleset 89, if set (with the null input), and logs the result. This should be used sparingly, since the rewrite process is not reentrant. Change -qI, -qR, and -qS flags to be case-insensitive as documented in the Bat Book. If the mailer returned EX_IOERR or EX_OSERR, sendmail did not return an error message and did not requeue the message. Fix based on code from Roland Dirlewanger of Reseau Regional Aquarel, Bordeaux, France. Fix a problem that caused a seg fault if you got a 421 error code during some parts of connection initialization. I've only seen this when talking to buggy mailers on the other end, but it shouldn't give a seg fault in any case. From Amir Plivatsky. Fix core dump caused by a ruleset call that returns null. Fix from Bryan Costales of ICSI. Full-Name: field was being ignored. Fix from Motonori Nakamura of Kyoto University. Fix a possible problem with very long input lines in setproctitle. From P{r Emanuelsson. Avoid putting "This is a warning message" out on return receipts. Suggested by Douglas Anderson. Detect loops caused by recursive ruleset calls. Suggested by Bryan Costales. Initialize non-alias maps during alias rebuilds -- they may be needed for parsing. Problem noted by Douglas Anderson. Log sender address even if no message was collected in SMTP (e.g., if all RCPTs failed). Suggested by Motonori Nakamura. Don't reflect the owner-list contents into the envelope sender address if the value contains ", :, /, or | (to avoid illegal addresses appearing there). Efficiency hack for toktype macro -- from Craig Partridge of BB&N. Clean up DNS error printing so that a host name is always included. Remember to set $i during queue runs. Reported by Stephen Campbell of Dartmouth University. If the environment variable HOSTALIASES is set, use it during canonification as the name of a file with per-user host translations so that headers are properly mapped. Reported by Anne Bennett of Concordia University. Avoid printing misleading error message if SMTP mailer (not using [IPC]) should die on a core dump. Avoid incorrect diagnosis of "file 1 closed" when it is caused by the other end closing the connection. From Dave Morrison of Oracle. Improve several of the error messages printed by "mailq" to include a host name or other useful information. Add NetInfo preliminary support for NeXT systems. From Vince DeMarco. Fix a glitch that sometimes caused :include:s that pointed to NFS filesystems that were down to give an "aliasing/ forwarding loop broken" message instead of queueing the message for retry. Noted by William C Fenner of the NRL Connection Machine Facility. Fix a problem that could cause a core dump if the input sequence had (or somehow acquired) a \231 character. Make sure that route-addrs always have around them in non-SMTP envelopes (SMTP envelopes already do this properly). Avoid weird headers on unbalanced punctuation of the form: ``Joe User ; this has uucp-dom semantics but old UUCP syntax. This also permits "uucp-old" as an alias for "uucp" and "uucp-new" as a synonym for "suucp" for consistency. CONFIG: add POP mailer support (from Kimmo Suominen ). CONFIG: drop CSNET_RELAY support -- CSNET is long gone. CONFIG: fix bug caused with domain literal addresses (e.g., ``[128.32.131.12]'') when FEATURE(allmasquerade) was set; it would get an additional @masquerade.host added to the address. Problem noted by Peter Wan of Georgia Tech. CONFIG: make sure that the local UUCP name is in $=w. From Jim Murray of Stratus. CONFIG: changes to UUCP rewriting to simulate IDA-style "V" mailer flag. Briefly, if you are sending to host "foo", then it rewrites "foo!...!baz" to "...!baz", "foo!baz" remains "foo!baz", and anything else has the local name prepended. CONFIG: portability fixes for HP-UX. DOC: several minor problems fixed in the Install & Op Guide. MAKEMAP: fix core dump problem on lines that are too long or which lack newline. From Mark Delany. MAILSTATS: print sums of columns (total messages & kbytes in and out of the system). From Tom Ferrin of UC San Francisco Computer Graphics Lab. SIGNIFICANT USER- OR SYSAD-VISIBLE CHANGES: On HP-UX, /etc/sendmail.cf has been moved to /usr/lib/sendmail.cf to match HP sendmail. Permissions have been tightened up on world-writable :include: files and accounts that have shells that are not listed in /etc/shells. This may cause some .forward files that have worked before to start failing. SIGUSR1 dumps some state to the log. NEW FILES: src/Makefile.DGUX src/Makefile.Dynix src/Makefile.FreeBSD src/Makefile.Mach386 src/Makefile.NetBSD src/Makefile.RISCos src/Makefile.SCO src/Makefile.SVR4 src/Makefile.Titan cf/mailer/pop.m4 cf/ostype/bsdi1.0.m4 cf/ostype/dgux.m4 cf/ostype/dynix3.2.m4 cf/ostype/sco3.2.m4 makemap/Makefile.dist praliases/Makefile.dist 8.6.4/8.6.4 93/10/31 Repair core-dump problem (write to read-only memory segment) if you fall back to the return-to-Postmaster case in savemail. Problem reported by Richard Liu. Immediately diagnose bogus sender addresses in SMTP. This makes quite certain that crackers can't use this class of attack. Reliability Fix: check return value from fclose() and fsync() in a few critical places. Minor problem in initsys() that reversed a condition for redirecting the output channel on queue runs. It's not clear this code even does anything. From Eric Wassenaar of the Dutch National Institute for Nuclear and High-Energy Physics. Fix some problems that caused queue runs to do "too much work", such as double-reading the Errors-To: header. From Eric Wassenaar. Error messages on writing the temporary file (including the data file) were getting suppressed in SMTP -- this fix causes them to be properly reported. From Eric Wassenaar. Some changes to support AF_UNIX sockets -- this will only really become relevant in the next release, but some people need it for local patches. From Michael Corrigan of UC San Diego. Use dynamically allocated memory (instead of static buffers) for macros defined in initsys() and settime(); since these can have different values depending on which envelope they are in. From Eric Wassenaar. Improve logging to show ctladdr on to= logging; this tells you what uid/gid processes ran as. Fix a problem that caused error messages to be discarded if the sender address was unparseable for some reason; this was supposed to fall back to the "return to postmaster" case. Improve aliaswait backoff algorithm. Portability patches for Linux (8.6.3 required another header file) (from Karl London) and SCO UNIX. CONFIG: patch prog mailer to not strip host name off of envelope addresses (so that it matches local again). From Christopher Davis. CONFIG: change uucp-dom mailer so that "<>" translates to $n; this prevents uux from seeing lines with null names like ``From Sat Oct 30 14:55:31 1993''. From Motonori Nakamura of Kyoto University. CONFIG: handle syntax correctly. This isn't legal, but it shouldn't fail miserably. From Motonori Nakamura. 8.6.2/8.6.2 93/10/15 Put a "successful delivery" message in the transcript for addresses that get return-receipts. Put a prominent "this is only a warning" message in warning messages -- some people don't read carefully enough and end up sending the message several times. Include reason for temporary failure in the "warning" return message. Currently, it just says "cannot send for four hours". Fix the "Original message received" time generated for returntosender messages. It was previously listed as the current time. Bug reported by Eric Hagberg of Cornell University Medical College. If there is an error when writing the body of a message, don't send the trailing dot and wait for a response in sender SMTP, as this could cause the connection to hang up under some bizarre circumstances. From Eric Wassenaar. Fix some server SMTP synchronization problems caused when connections fail during message collection. From Eric Wassenaar. Fix a problem that can cause srvrsmtp to reject mail if the name server is down -- it accepts the RCPT but rejects the DATA command. Problem reported by Jim Murray of Stratus. Fix a problem that can cause core dumps if the config file incorrectly resolves to a null hostname. Reported by Allan Johannesen of WPI. Non-root use of -C flag, dangerous -f flags, and use of -oQ by non-root users were not put into X-Authentication-Warning:s as intended because the config file hadn't set the PrivacyFlags yet. Fix from Sven-Ove Westberg of the University of Lulea. Under very odd circumstances, the alias file rebuild code could get confused as to whether a database was open or not. Check "vendor code" on the end of V lines -- this is intended to provide a hook for vendor-specific configuration syntax. (This is a "new feature", but I've made an exception to my rule in a belief that this is a highly exceptional case.) Portability fixes for DG/UX (from Douglas Anderson of NCSC), SCO Unix (from Murray Kucherawy), A/UX, and OSF/1 (from Jon Forrest of UC Berkeley) CONFIG: fix ``mailer:host'' form of UUCP relay naming. 8.6.1/8.6 93/10/08 Portability fixes for A/UX and Encore UMAX V. Fix error message handling -- if you had a name server down causing an error during parsing, that message was never propogated to the queue file. 8.6/8.6 93/10/05 Configuration cleanup: make it easier to undo IDENTPROTO in conf.h (other systems have the same bug). If HASGETDTABLESIZE and _SC_OPEN_MAX are both defined, assume getdtablesize() instead of sysconf(); a disturbingly large number of systems defined _SC_OPEN_MAX in the header files but don't have the syscall. Another patch to really truly ignore MX records in getcanonname if trymx == FALSE. Fix problem that caused the "250 IAA25499 Message accepted for delivery" message to be omitted if there was an error in the header of the message (e.g., a bad Errors-To: line). Pointed out by Michael Corrigan of UCSD. Announce name of host we are chatting when we get errors; this is an IDA-ism suggested by Christophe Wolfhugel. Portability fixes for Alpha OSF/1 (from Anthony Baxter of the Australian Artificial Intelligence Institute), SCO Unix (from Murray Kucherawy of Hookup Communication Corp.), NeXT (from Vince DeMarco and myself), Linux (from Karl London ), BSDI (from Christophe Wolfhugel, and SVR4 on Dell (from Kimmo Suominen), AUX 3.0 on Macintosh, and ANSI C compilers. Some changes to get around gcc optimizer bugs. From Takahiro Kanbe. Fix error recovery in queueup if another tf file of the same name already exists. Problem stumbled over by Bill Wisner of The Well. Output YP_MASTER_NAME and YP_LAST_MODIFIED without null bytes. Problem noted by Keith McMillan of Ameritech Services. Deal with group permissions properly when opening .forward and :include: files. This relaxes the 8.1C restrictions slightly more. This includes proper setting of groups when reading :include: files, allowing you to read some files that you should be able to read but have previously been denied unless you owned them or they had "other" read permission. Make certain that $j is in $=w (after the .cf is read) so that if the user is forced to override some silly system, MX suppression will still work. Fix a couple of efficiency problems where newstr was double- calling expensive routines. In at least one case, it wasn't guaranteed that they would always return the same result. Problem noted by Christophe Wolfhugel. Fix null pointer dereference in putoutmsg -- only on an error condition from a non-SMTP mailer. From Motonori Nakamura. Macro expand "C" line class definitions before scanning so that "CX $Z" works. Fix problem that caused error message to be sent while still trying to send the original message if the connection is closed during a DATA command after getting an error on an RCPT command (pretty obscure). Problem reported by John Myers of CMU. Fix reply to NOOP to be 250 instead of 200 -- this is a long term bug. Fix a nasty bug causing core dumps when returning the "warning: cannot deliver for N hours -- will keep trying" message; it only occurred if you had PostMasterCopy set and only on some architectures. Although sendmail would keep trying, it would send error messages on each queue interval. This is an important fix. Allow u and g options to take user and group names respectively. Don't do a chdir into the queue directory in -bt mode to make ruleset testing a bit easier. Don't allow users to turn off logging (using -oL) on the command line -- command line can only raise, not lower, logging level. Set $u to the original recipient on the SMTP transaction or on the command line. This is only done if there is exactly one recipient. Technically, this does not meet the specs, because it does not guarantee a domain on the address. Fix a problem that dumped error messages on bad addresses if you used the -t flag. Problem noted by Josh Smith of Harvey Mudd College. Given an address such as `` '', auto-quote the first ``'' part, giving ``"" ''. This is to avoid the problem of people who use angle brackets in their full name information. Fix a null pointer dereference if you set option "l", have an Errors-To: header in the message, and have Errors-To: defined in the config file H lines. From J.R. Oldroyd. Put YPCOMPAT on #ifdef NIS instead -- it's one less thing to get wrong when compiling. Suggested by Rick McCarty of TI. Fix a problem that could pass negative SIZE parameter if the df file got lost; this would cause servers to always give a temporary failure, making the problem even worse. Problem noted by Allan Johannesen of WPI. Add "ident" timeout (one of the "r" option selectors) for IDENT protocol timeouts (30s default). Requested by Murray Kucherawy of HookUp Communication Corp. to handle bogus PC TCP/IP implementations. Change $w default definition to be just the first component of the domain name on config level 5. The $j macro defaults to the FQDN; $m remains as before. This lets well-behaved config files use any of the short, long, or subdomain names. Add makesendmail script in src to try to automate multi-architecture builds. I know, this is sub-optimal, but it is still helpful. Fix very obscure race condition that can cause a queue run to get a queue file for an already completed job. This problem has existed for years. Problem noted by the long suffering Allan Johannesen of WPI. Fix a problem that caused the raw sender name to be passed to udbsender instead of the canonified name -- this caused it to sometimes miss records that it should have found. Relax check of name on HELO packet so that a program using -bs that claims to be itself works properly. Restore rewriting of $: part of address through 2, R, 4 in buildaddr -- this requires passing a lot of flags to get it right. Unlike old versions, this ONLY rewrites recipient addresses, not sender addresses. Fix a bug that caused core dumps in config files that cannot resolve /file/name style addresses. Fix from Jonathan Kamens of OpenVision Technologies. Fix problem with fcntl locking that can cause error returns to be lost if the lock is lost; this required fully queueing everything, dropping the envelope (so errors would get returned), and then re-reading the queue from scratch. Fix a problem that caused aliases that redefine an otherwise true address to still send to the original address if and only if the alias failed in certain bizarre ways (e.g, if they pointed at a list:; syntax address). Problem pointed out by Jonathan Kamens. Remove support for frozen configuration files. They caused more trouble than it was worth. Fix problem that can cause error messages to get ignored when using both -odb and -t flags. Problem noted by Rob McNicholas at U.C. Berkeley. Include all "normal" variations on hostname in $=w. For example, if the host name is vangogh.cs.berkeley.edu, $=w will contain vangogh, vangogh.cs, and vangogh.cs.berkeley.edu. Add "restrictqrun" privacy flag -- without this, anyone can run the queue. Reset SmtpPhase global on initial connection creation so that messages don't come out with stale information. Pass an "ext" argument to lockfile so that error/log messages will properly reflect the true filename being locked. Put all [...] address forms into $=w -- this eliminates the need for MAXIPADDR in conf.h. Suggested by John Gardiner Myers of CMU. Fix a bug that can cause qf files to be left around even after an SMTP RSET command. Problem and fix from Michael Corrigan. Don't send a PostMasterCopy to errors when the Precedence: is negative. Error reports still go to the envelope sender address. Add LA_SHORT for load averages. Lock sendmail.st file when posting statistics. Add "SendBufSize" and "RcvBufSize" suboptions to "O" option to set the size of the TCP send and receive buffers; if you run over a slow slip line you may need to set these down (although it would be better to fix the SLIP implementation so that it's not necessary to recompile every program that does bulk data transfer). Allow null defaults on $( ... $) lookups. Problem reported by Amir Plivatsky. Diagnose crufty S and V config lines. This resulted from an observation that some people were using the SITE macro without the SITECONFIG macro first, which was causing bogus config files that were not caught. Fix makemap -f flag to turn off case folding (it was turning it on instead). THIS IS A USER VISIBLE CHANGE!!! Fix a problem that caused multiple error messages to be sent if you used "sendmail -t -oem -odb", your system uses fcntl locking, and one of the recipient addresses is unknown. Reset uid earlier in include() so that recursive .forwards or :include:s don't use the wrong uid. If file descriptor 0, 1, or 2 was closed when sendmail was called, the code to recover the descriptor was broken. This sometimes (only sometimes) caused problems with the alias file. Fix from Motonori Nakamura. Fix a problem that caused aliaswait to go into infinite recursion if the @:@ metasymbol wasn't found in the alias file. Improve error message on newaliases if database files cannot be opened or if running with no database format defined. Do a better estimation of the size of error messages when NoReturn is set. Problem noted by P{r (Pell) Emanuelsson. Fix a problem causing the "c" option (don't connect to expensive mailers) to be ignored in SMTP. Problem noted and the solution suggested by Robert Elz of The University of Melbourne. Improve connection caching algorithm by passing "[host]" to hostsignature, which strips the square brackets and returns the real name. This allows mailertable entries to match regular entries. Re-enable Return-Receipt-To: -- people seem to want this stupid feature, even if it doesn't work right. Catch and log attempts to try the "wiz" command in server SMTP. This also ups the log level from LOG_NOTICE to LOG_CRIT. Be more generous at assigning $z to the home directory -- do this for programs that are specified through a .forward file. Fix from Andrew Chang of Sun Microsystems. Always save a fatal error message in preference to a non-fatal error message so that the "subject" line of return messages is the best possible. CONFIG: reduce the number of quotes needed to quote configuration parameters with commas: two quotes should work now, e.g., define(ALIAS_FILE, ``/etc/aliases,/etc/aliases.local''). CONFIG: class $=Z is a set of UUCP hosts that use uucp-dom connections (domain-ized UUCP). CONFIG: fix bug in default maps (-o must be before database file name). Pointed out by Christophe Wolfhugel. CONFIG: add FEATURE(nodns) to state that we are not relying on DNS. This would presumably be used in UUCP islands. CONFIG: add OSTYPE(nextstep) and OSTYPE(linux). CONFIG: log $u in Received: line. This is in technical violation of the standards, since it doesn't guarantee a domain on the address. CONFIG: don't assume "m" in local mailer flags -- this means that if you redefine LOCAL_MAILER_FLAGS you will have to include the "m" flag should you want it. Apparently some Solaris 2.2 installations can't handle multiple local recipients. Problem noted by Josh Smith. CONFIG: add confDOMAIN_NAME to set $j (if undefined, $j defaults). CONFIG: change default version level from 4 to 5. CONFIG: add FEATURE(nullclient) to create a config file that forwards all mail to a hub without ever looking at the addresses in any detail. CONFIG: properly strip mailer: information off of relays when used to change .BITNET form into %-hack form. CONFIG: fix a problem that caused infinite loops if presented with an address such as "!foo". CONFIG: check for self literal (e.g., [128.32.131.12]) even if the reverse "PTR" mapping is broken. There's a better way to do this, but the change is fairly major and I want to hold it for another release. Problem noted by Bret Marquis. 8.5/8.5 93/07/23 Serious bug: if you used a command line recipient that was unknown sendmail would not send a return message (it was treating everything as though it had an SMTP-style client that would do the return itself). Problem noted by Josh Smith. Change "trymx" option in getcanonname() to ignore all MX data, even during a T_ANY query. This actually didn't break anything, because the only time you called getcanonname with !trymx was if you already knew there were no MX records, but it is somewhat cleaner. From Motonori Nakamura. Don't call getcanonname from getmxrr if you already know there are no DNS records matching the name. Fix a problem causing error messages to always include "The original message was received ... from localhost". The correct original host information is now included. Previous change to cf/sh/makeinfo.sh doesn't port to Ultrix (their version of "test" doesn't have the -x flag). Change it to use -f instead. From John Myers. CONFIG: 8.4 mistakenly set the default SMTP-style mailer to esmtp -- it should be smtp. CONFIG: send all relayed mail using confRELAY_MAILER (defaults to "relay" (a variant of "smtp") if MAILER(smtp) is used, else "suucp" if MAILER(uucp) is used, else "unknown"); this cleans up the configs somewhat. This fixes a serious problem that caused route-addrs to get mistaken as relays, pointed out by John Myers. WARNING: this also causes the default on SMART_HOST to change from "suucp" to "relay" if you have MAILER(smtp) specified. 8.4/8.4 93/07/22 Add option `w'. If you receive a message that comes to you because you are the best (lowest preference) target of an MX, and you haven't explicitly recognized the source MX host in your .cf file, this option will cause you to try the target host directly (as if there were no MX for it at all). If `w' is not set, this case is a configuration error. Beware: if `w' is set, senders may get bogus errors like "message timed out" or "host unknown" for problems that are really configuration errors. This option is disrecommended, provided only for compatibility with UIUC sendmail. Fix a problem that caused the incoming socket to be left open when sendmail forks after the DATA command. This caused calling systems to wait in FIN_WAIT_2 state until the entire list was processed and the child closed -- a potentially prodigious amount of time. Problem noted by Neil Rickert. Fix problem (created in 6.64) that caused mail sent to multiple addresses, one of which was a bad address, to completely suppress the sending of the message. This changes handling of EF_FATALERRS somewhat, and adds an EF_GLOBALERRS flag. This also fixes a potential problem with duplicate error messages if there is a syntax error in the header of a message that isn't noticed until late in processing. Original problem pointed out by Josh Smith of Harvey Mudd College. This release includes quite a bit of dickering with error handling (see below). Back out SMTP transaction if MAIL gets nested 501 error. This will only hurt already-broken software and should help humans. Fix a problem that broke aliases when neither NDBM nor NEWDB were compiled in. It would never read the alias file. Repair unbalanced `)' and `>' (the "open" versions are already repaired). Logging of "done" in dropenvelope() was incorrect: it would log this even when the queue file still existed. Change this to only log "done" (at log level 11) when the queue file is actually removed. From John Myers. Log "lost connection" in server SMTP at log level 20 if there is no pending transaction. Some senders just close the connection rather than sending QUIT. Fix a bug causing getmxrr to add a dot to the end of unqualified domains that do not have MX records -- this would cause the subsequent host name lookup to fail. The problem only occurred if you had FEATURE(nocanonify) set. Problem noted by Rick McCarty of Texas Instruments. Fix invocation of setvbuf when passed a -X flag -- I had unwittingly used an ANSI C extension, and this caused core dumps on some machines. Diagnose self-destructive alias loops on RCPT as well as EXPN. Previously it just gave an empty send queue, which then gave either "Need RCPT (recipient)" at the DATA (confusing, since you had given an RCPT command which returned 250) or just dropped the email, depending on whether you were running VERBose mode. Now it usually diagnoses this case as "aliasing/forwarding loop broken". Unfortunately, it still doesn't adequately diagnose some true error conditions. Add internal concept of "warning messages" using 6xx codes. These are not reported only to Postmaster. Unbalanced parens, brackets, and quotes are printed as 653 codes. They are always mapped to 5xx codes before use in SMTP. Clean up error messages to tell both the actual address that failed and the alias they arose from. This makes it somewhat easier to diagnose problems. Difficulty noted by Motonori Nakamura. Fix a problem that inappropriately added a ctladdr to addresses that shouldn't have had one during a queue run. This caused error messages to be handled differently during a queue run than a direct run. Don't print the qf name and line number if you get errors during the direct run of the queue from srvrsmtp -- this was just extra stuff for users to crawl through. Put command line flags on second line of pid file so you can auto-restart the daemon with all appropriate arguments. Use "kill `head -1 /etc/sendmail.pid`" to stop the daemon, and "eval `tail -1 /etc/sendmail.pid`" to restart it. Remove the ``setuid(getuid())'' in main -- this caused the IDENT daemon to screw up. This required that I change HASSETEUID to HASSETREUID and complicate the mode changing somewhat because both Ultrix and SunOS seem to have a bug causing seteuid() to set the saved uid as well as the effective. The program test/t_setreuid.c will test to see if your implementation of setreuid(2) is appropriately functional. The FallBackMX (option V) handling failed to properly identify fallback to yourself -- most of the code was there, but it wasn't being enabled. Problem noted by Murray Kucherawy of the University of Waterloo. Change :include: open timeout from ETIMEDOUT to an internal code EOPENTIMEOUT; this avoids adding "during SmtpPhase with CurHostName" in error messages, which can be confusing. Reported by Jonathan Kamens of OpenVision Technologies. Back out setpgrp (setpgid on POSIX systems) call to reset the process group id. The original fix was to get around some problems with recalcitrant MUAs, but it breaks any call from a shell that creates a process group id different from the process id. I could try to fix this by diddling the tty owner (using tcsetpgrp or equivalent) but this is too likely to break other things. Portability changes: Support -M as equivalent to -oM on Ultrix -- apparently DECnet calls sendmail with -MrDECnet -Ms -bs instead of using standard flags. Oh joy. This behaviour reported by Jon Giltner of University of Colorado. SGI IRIX -- this includes several changes that should help other strict ANSI compilers. SCO Unix -- from Murray Kucherawy of HookUp Communication Corporation. Solaris running the Sun C compiler (which despite the documentation apparently doesn't define __STDC__ by default). ConvexOS from Eric Schnoebelen of Convex. Sony NEWS workstations and Omron LUNA workstations from Motonori Nakamura. CONFIG: add confTRY_NULL_MX_LIST to set option `w'. CONFIG: delete `C' and `e' from default SMTP mailers flags; several people have made a good argument that this creates more problems than it solves (although this may prove painful in the short run). CONFIG: generalize all the relays to accept a "mailer:host" format. CONFIG: move local processing in ruleset 0 into a new ruleset 98 (8 on old sendmail). Domain literal [a.b.c.d] addresses are also passed through this ruleset. CONFIG: if neither SMART_HOST nor MAILER(smtp) were defined, internet-style addresses would "fall off the end" of ruleset zero and be interpreted as local -- however, the angle brackets confused the recursive call. These are now diagnosed as "Unrecognized host name". CONFIG: USENET rules weren't included in S0 because of a mistaken ifdef(`_MAILER_USENET_') instead of ifdef(`_MAILER_usenet_'). Problem found by Rein Tollevik of SINTEF RUNIT, Oslo. CONFIG: move up LOCAL_RULE_0 processing so that it happens very early in ruleset 0; this allows .mc authors to bypass things like the "short circuit" code for local addresses. Prompted by a comment by Bill Wisner of The Well. CONFIG: add confSMTP_MAILER to define the mailer used (smtp or esmtp) to send SMTP mail. This allows you to default to esmtp but use a mailertable or other override to deal with broken servers. This logic was pointed out to me by Bill Wisner. Ditto for confLOCAL_MAILER. Changes to cf/sh/makeinfo.sh to make it portable to SVR4 environments. Ugly as sin. 8.3/8.3 93/07/13 Fix setuid problems introduced in 8.2 that caused messages like "Cannot create qfXXXXXX: Invalid argument" or "Cannot reopen dfXXXXXX: Permission denied". This involved a new compile flag "HASSETEUID" that takes the place of the old _POSIX_SAVED_IDS -- it turns out that the POSIX interface is broken enough to break some systems badly. This includes some fixes for HP-UX. Also fixes problems where the real uid is not reset properly on startup (from Neil Rickert). Fix a problem that caused timed out messages to not report the addresses that timed out. Error messages are also more "user friendly". Drop required bandwidth on connections from 64 bytes/sec to 16 bytes/sec. Further Solaris portability changes -- doesn't require the BSD compatibility library. This also adds a new "HASGETDTABLESIZE" compile flag which can be used if you want to use getdtablesize(2) instead of sysconf(2). These are loosely based on changes from David Meyer at University of Oregon. This now seems to work, at least for quick test cases. Fix a problem that can cause duplicate error messages to be sent if you are in SMTP, you send to multiple addresses, and at least one of those addresses is good and points to an account that has a .forward file (whew!). Fix a problem causing messages to be discarded if checkcompat() returned EX_TEMPFAIL (because it didn't properly mark the "to" address). Problem noted by John Myers. Fix dfopen to return NULL if the open failed; I was depending on fdopen(-1) returning NULL, which isn't the case. This isn't serious, but does result in weird error diagnoses. From Michael Corrigan. CONFIG: add UUCP_MAX_SIZE M4 macro to set the maximum size of messages sent through UUCP-family mailers. Suggested by Bill Wisner of The Well. CONFIG: if both MAILER(uucp) and MAILER(smtp) are specified, include a "uucp-dom" mailer that uses domain-style addressing. Suggested by Bill Wisner. CONFIG: Add LOCAL_SHELL_FLAGS and LOCAL_SHELL_ARGS to match LOCAL_MAILER_FLAGS and LOCAL_MAILER_ARGS. Suggested by Christophe Wolfhugel. CONFIG: Add OSTYPE(aix3). From Christophe Wolfhugel. 8.2/8.2 93/07/11 Don't drop out on config file parse errors in -bt mode. On older configuration files, assume option "l" (use Errors-To header) for back compatibility. NOTE: this DOES NOT imply an endorsement of the Errors-To: header in any way. Accept -x flag on AIX-3 as well as OSF/1. Why, why, why??? Don't log errors on EHLO -- it isn't a "real" error for an old SMTP server to give an error on this command, and logging it in the transcript can be confusing. Fix from Bill Wisner. IRIX compatibility changes provided by Dan Rich . Solaris 2 compatibility changes. Provided by Bob Cunningham , John Oleynick Debugging: -d17 was overloaded (hostsignature and usersmtp.c); move usersmtp (smtpinit and smtpmailfrom) to -d18 to match the other flags in that file. Flush transcript before fork in mailfile(). From Eric Wassenaar. Save h_errno in mci struct and improve error message display. Changes from Eric Wassenaar. Open /dev/null for the transcript if the create of the xf file failed; this avoids at least one possible null pointer reference in very weird cases. From Eric Wassenaar. Clean up statistics gathering; it was over-reporting because of forks. From Eric Wassenaar. Fix problem that causes old Return-Path: line to override new Return-Path: line (conf.c needs H_FORCE to avoid re-using old value). From Motonori Nakamura. Fix broken -m flag in K definition -- even if -m (match only) was specified, it would still replace the key with the value. Noted by Rick McCarty of Texas Instruments. If the name server timed out over several days, no "timed out" message would ever be sent back. The timeout code has been moved from markfailure() to dropenvelope() so that all such failures should be diagnosted. Pointed out by Christophe Wolfhugel and others. Relax safefile() constraints: directories in an include or forward path must be readable by self if the controlling user owns the entry, readable by all otherwise (e.g., when reading your .forward file, you have to own and have X permssion in it; everyone needs X permission in the root and directories leading up to your home); include files must be readable by anyone, but need not be owned by you. If _POSIX_SAVED_IDS is defined, setuid to the owner before reading a .forward file; this gets around some problems on NFS mounts if root permission is not exported and the user's home directory isn't x'able. Additional NeXT portability enhancements from Axel Zinser. Additional HP-UX portability enhancements from Brian Bullen. Add a timeout around SMTP message writes; this assumes you can get throughput of at least 64 bytes/second. Note that this does not impact the "datafinal" default, which is separate; this is just intended to work around network clogs that will occur before the final dot is sent. From Eric Wassenaar. Change map code to set the "include null" flag adaptively -- it initially tries both, but if it finds anything matching without a null it never tries again with a null and vice versa. If -N is specified, it never tries without the null and creates new maps with a null byte. If -O is specified, it never tries with the null (for efficiency). If -N and -O are specified, you get -NO (get it?) lookup at all, so this would be a bad idea. If you don't specify either -N or -O, it adapts. Fix recognition of "same from address" so that MH submissions will insert the appropriate full name information; this used to work and got broken somewhere along the way. Some changes to eliminate some unnecessary SYSERRs in the log. For example, if you lost a connection, don't bother reporting that fact on the connection you lost. Add some "extended debugging" flags to try to track down why we get occassional problems with file descriptor one being closed when execing a mailer; it seems to only happen when there has been another error in the same transaction. This requires XDEBUG, defined by default in conf.h. Add "-X filename" command line flag, which logs both sides of all SMTP transactions. This is intended ONLY for debugging bad implementations of other mailers; start it up, send a message from a mailer that is failing, and then kill it off and examine the indicated log. This output is not intended to be particularly human readable. This also adds the HASSETVBUF compile flag, defaulted on if your compiler defines __STDC__. CONFIG: change SMART_HOST to override an SMTP mailer. If you have a local net that should get direct connects, you will need to use LOCAL_NET_CONFIG to catch these hosts. See cf/README for an example. CONFIG: add LOCAL_MAILER_ARGS (default: `mail -d $u') to handle sites that don't use the -d flag. CONFIG: hide recipient addresses as well as sender addresses behind $M if FEATURE(allmasquerade) is specified; this has been requested by several people, but can break local aliases. For example, if you mail to "localalias" this will be rewritten as "localalias@masqueradehost"; although initial delivery will work, replies will be broken. Use it sparingly. CONFIG: add FEATURE(domaintable). This maps unqualified domains to qualified domains in headers. I believe this is largely equivalent to the IDA feature of the same name. CONFIG: use $U as UUCP name instead of $k. This permits you to override the "system name" as your UUCP name -- in particular, to use domain-ized UUCP names. From Bill Wisner of The Well. CONFIG: create new mailer "esmtp" that always tries EHLO first. This is currently unused in the config files, but could be used in a mailertable entry. 8.1C/8.1B 93/06/27 Serious security bug fix: it was possible to read any file on the system, regardless of ownership and permissions. If a subroutine returns a fully qualified address, return it immediately instead of feeding it back into rewriting. This fixes a problem with mailertable lookups. CONFIG: fix some M4 frotz (concat => CONCAT) 8.1B/8.1A 93/06/12 Serious bug fix: pattern matching backup algorithm stepped by two tokens in classes instead of one. Found by Claus Assmann at University of Kiel, Germany. 8.1A/8.1A 93/06/08 Another mailertable fix.... 8.1/8.1 93/06/07 4.4BSD freeze. No semantic changes. 6.65/6.34 93/06/06 Fix some lintish problems. Fix some cases where server SMTP behaved poorly when handed bogus input, pointed out by Eric Wassenaar. CONFIG: fix some more (sigh) mailertable bugs -- thanks to Motonori Nakamura of Kyoto University (again). 6.64/6.33 93/06/05 Don't send 050 (-v) information after the 250 response to a QUIT command in srvrsmtp -- clients usually close the connection at this point, and it causes bogus error messages. Don't send messages that have errors on input (such as unbalanced parentheses) during SMTP transactions, since a return message has (probably) already been sent. Give better diagnostics on timeouts during network reads, including information similar to the SMTP phase. Fix bug that caused SMTP messages to deliver synchronously; this happened after the DATA 250, and hence caused reading the next command to be delayed. Ignore Errors-To: header unless 'l' (lower case el) header is specified. The Errors-To: header violates RFC 1123. Errors-To: was only needed to take the place of the envelope sender in the days when most Unix mailers didn't understand about the two kinds of senders. Don't send warning messages in response to automatically generated messages (that is, those From:<>). CONFIG: fix some rather stupid typos in the mailertable code pointed out by Motonori Nakamura of Kyoto University. CONFIG: add confUSE_ERRORS_TO configuration option. CONFIG: if ALWAYS_ADD_DOMAIN is selected, try to use $M (masquerade name) instead of $j. CONFIG: don't add dots to relay names (added in 6.29); it breaks several things, and can be simulated by dot terminating the names of relays. For example, use: DBbit.net.relay. (note the trailing dot). 6.63/6.32 93/06/01 Fix prototypes to eliminate chars in argument lists -- some compilers are pissy about this. Log protocol ($r) and body type if set so we can determine if the adaptive algorithms are working. Pessimize on locking of database files (particularly for NEWDB databases) during opens. There were problems with processes opening the file while it was rebuilt; since NEWDB caches heavily, the reader opened an empty file, which is an error. If your system has the ability to lock atomically on open, this works properly; otherwise, there are race conditions. Check mod time on .pag file instead of .dir in NDBM aliases because the .dir file doesn't get updated for small alias files. From John Gardiner Myers of CMU. More Solaris portability -- it now compiles on Solaris, but hangs up in gethostbyname(). Move setting of RES_DEBUG flag before first myhostname() call so we can see name server traffic on that call. Fsync() queue files. Fix a problem that causes -bi to try to rebuild maps other than the alias file(s). Fix a problem that caused udb to reject entries from any but the first database listed. Rearrange doc subdirectory for 4.4BSD release tape. CONFIG: put $r into the Received line. This was an oversight. CONFIG: fix typo (call to ruleset 99 should have been rulset 90). CONFIG: move "auxiliary" subroutines to be in ruleset 90-99 range -- in the long run, single digit rulesets may become reserved for builtin use by sendmail. CONFIG: fix major problem that causes host aliases (that is, anything in $=w != $j) to not be recognized. This has been around since 6.30. 6.62/6.31 93/05/28 BETA RELEASE Fix recursive syserr (if there is an error printing a syserr message). This makes the code much less eager to consider a write error as serious. This also includes some heuristics to be clever about closed connections. Lock NEWDB files during gets. This requires version 1.5 or later of the db library. If you have an older version, you can use -DOLD_NEWDB. This will go away in a few weeks. Fix problem causing aliases that use host maps to get overwritten. Do appropriate byte swapping on port numbers in ident protocol code. Fix from Allan Johannesen of WPI. Defer opening of map files to the same time as alias files so that the daemon will tend to pick up new versions more promptly. Prototype a bunch more functions. Some Solaris 2.1 changes (still doesn't link though). Try to simplify Makefiles by including more subordinate #defines in conf.h (based on OS type). CONFIG: check for domains if FEATURE(mailertable) is defined. For example, if the host name is "knecht.cs.berkeley.edu" it will search the following mailertable keys: knecht.cs.berkeley.edu .cs.berkeley.edu .berkeley.edu .edu This could be used to replace the special relays for bitnet and similar nets. 6.61/6.30 93/05/24 Fix problem that prevented appending dots on canonified host names. This breaks tons of config files -- very important fix. Fix improper pointer dereference in response to HELO command. Fix core dump if debugging set in map_rewrite. CONFIG: add FEATURE(always_add_domain) to always attach the local domain (only impacts local mail). CONFIG: try to avoid turning names into $j -- although technically a host can only have one "canonical name", it seems to be common practice to have several. 6.60/6.29 93/05/22 Major change: merge alias databases with maps. This expands and changes the map class interface but fixes a bunch of bugs. The important user-visible change is that the file name in a K line now does not include the ".db" extension; this is added automatically. Also, the -d (NIS domain) flag is missing from the K config line; use @domain instead. When compiling, the *_MAP names are gone -- just compile in NDBM, NEWDB, and/or NIS support. Announce mailer/host/user triple on -bv flag -- from Brian Bullen of Stirling University. Don't send more than one line in response to HELO -- it confuses Pony Express, which then behaves very badly. However, this change does send two line 220 greetings, with the second line reading "ESMTP spoken here". The usersmtp module recognizes this and goes into ESMTP mode regardless of the setting of the "a" mailer flag. Thus, "a" means "always try EHLO". AIX portability changes (thanks to Christophe Wolfhugel of Herve Schauer Consultants (Paris) for providing me with an INSA account for this purpose). Lightly tested. Use -D_AIX3. This probably breaks compatibility with some older systems (e.g., 4.2bsd) but still works on SunOS 4.1.2, Ultrix 4.2A, HP-UX 8.07, OSF/1 T1.3, and AIX 3.2.3. Fix a problem causing an error message loop if the output channel is hosed. Add the Makefiles that I use for various environments -- some are Berkeley make versions and some are old make versions. My makefile for the NeXT box has gotten lost, alas! PRALIASES: support for printing NEWDB databases. From Michael J. Corrigan of U.C. San Diego. CONFIG: don't pass pseudo-domains to $[ ... $] (if you have a wildcard MX it can have weird results). From Christophe Wolfhugel. CONFIG: dot terminate relay hostnames in S0. From Christophe Wolfhugel. 6.59/6.28 93/05/13 Log version with SMTP daemon startup message. Adjust setproctitle to work on NetBSD and BSD/386. Fix null pointer reference in MX fallback code. A bunch of minor fixes from Eric Wassenaar: If deliver cannot execv the mailer, return EX_OSERR instead of EX_TEMPFAIL (to give better error messages). Consistently malloc e_message. Catch degenerate case of calling returntosender() with an empty returnq. MIME reformatting. 6.58/6.28 93/05/13 Fix bug that can cause incorrect verbose display of user smtp messages. Disable SMTP VERB command if PRIV_NOEXPN is set (since this could reveal the same information. Allow failure when reading SMTP greeting message to go on to next MX host. Add "MIME-Version: 1.0" header if using MIME (this was NOT included in RFC 1344, but Bill King of Allan-Bradley Company forwarded me email from Nathaniel Borenstein claiming that it was an inadvertent omission). Don't use Content-Type: X-message-header. According to John Myers of CMU, many MIME readers will completely ignore the data if they don't recognize it. Instead, just add a blank line to make it a legal (empty) message. Fix problem causing dots to keep getting appended to cached hostnames. This can cause buffer overrun conditions. The problem was found by Erik Forsberg of Retix, although I used a different bug fix than he provided. Fix parsing of split header/envelope rewriting specs -- from Eric Forsberg. Fix from Eric Wassenaar to correct To: lists in error messages. 6.57/6.28 93/05/11 Fix minor glitch causing extra ctladdrs to be output to queue file. Just an annoyance. Cache results of name server canonification lookups to avoid backed up queue runs. Major rewrite of alias.c: considerable cleanup, plus sample (untested) support for NIS aliases. The "A" option can now be a comma separated list (or be repeated) -- that is, you can have multiple alias databases. Each database can have the syntax ``class:file''; if no class is specified, the "implicit" class is assumed. Implicit searches through a list of compiled in types -- hash, dbm, nis, and stab. Alias files are searched in the order they are listed. For example: OAhash:/etc/aliases.local,/etc/aliases OAnis:mail.aliases@my.nis.domain first searches the hash database /etc/aliases.local, then the regular /etc/aliases database, then the NIS map "mail.aliases" in the NIS domain "my.nis.domain". If in Verbose mode (probably from VERB command) run SMTP job in foreground and don't do RCPT optimizations. Add udb :mailsender as equivalent to owner- for regular aliases. Delete option 8; add option 7 that means the opposite. That is, default to 8-bit mode; a special option is needed to force sendmail into 7 bit mode. Send error messages in encapsulated MIME format. New compile flag "NIS" that turns on NIS alias and NIS map support. Add "j" option to send error messages in MIME (RFC 1341) encapsulated message format per RFC 1344. The syntax is pretty ugly if you don't have MIME-aware user agents. Clean up message handling (for display in mailq output). New setproctitle implementation for 4.4bsd. Create files (such as ~/dead.letter) using mode FileMode (the F option value) instead of 0666. Fix bug causing output of EXPN command to not be fully qualified. This may cause some problems with UUCP addresses that will require some config file assistance -- specifically, the $: part has to include the host name for this output to make sense. Fix a problem that sometimes diagnosed errors and still sent the message if the header syntax was bad. Fix a bug that caused an error message to be emailed when sendmail was operating in -bv mode. Add "ListenQueueSize" keyword to daemon options option (OO) to set the queue size parameter passed to listen(). You will normally have to tweak your kernel to up this. Strip spaces off of beginning of message-id before logging (in case it was folded across lines). Tweak compile flags in daemon.c -- there were some cases where it wouldn't work without NETINET. Change *file* mailer to output all the usual default headers (From, Date, Message-Id). It gets used when sending back error messages. CONFIG: explicitly catch and diagnose list:; syntax in ruleset zero -- this is not a valid recipient syntax according to RFC 821. CONFIG: add confMIME_FORMAT_ERRORS to send error messages in MIME format. Defaults to on. CONFIG: add SMTP_MAILER_FLAGS and UUCP_MAILER_FLAGS to augment the flags for those mailers. 6.56/6.27 93/05/01 Fix problem that causes the fallback mail to postmaster (case ESM_POSTMASTER in savemail()) to not look at aliases (ugh). Some more HPUX tweaking (compile flag hpux => __hpux so it still works in ANSI mode). Don't try to flock non-regular files when mailing to a file. In particular, this was a problem if you tried to send to /dev/null. Fix a weird bug that can cause senders to be queued as recipients if the name server is down when the mail is initially sent. This hack just ignores sender deletion (essentially, it sets the MeToo flag) if there is a TEMPFAIL during processing of the sender address. Obscure. Fix a dangling else problem -- from Brian Bullen from University of Stirling, UK. Add the "b" mailer flag to force a blank line on the end of messages. Some brilliant versions of /bin/mail insist on this but do not add it themselves. Add the "g" mailer flag to prevent user SMTP from sending "MAIL From:<>". This is only intended to be a transitional gesture, and should not be used if at all possible. It appears that Berkeley and IDA config files have always handled this properly; the UK config kit apparently does not. Don't lowercase and then capitalize header field names -- leave them with original capitalization. Fixes from Bill King of Allen-Bradley Company. Further cleanup and improved reporting of error messages, particularly conditions that cause messages to be requeued for future delivery. Tweak syslog priorities in some cases. CONFIG: clean up route-addr on UUCP addresses. 6.55/6.25 93/04/27 HPUX 8.07 compatibility changes in getla() -- I had to make these changes to get it to work at Berkeley, although others seem to have been working before (???). Various patches to XLA code. Fix problem that causes setuid bit on files to be ignored from SMTP or in queue runs. Problem noted by Jason Ornstein of Under The Wire, Inc. Fix problem that can cause CNAMEs to be ignored. Generalize getmxrr to match local host in $=w instead of a single name passed in. Some cleanup from Eric Wassenaar: Use FileMailer instead of ProgMailer in two places. Eliminate duplicate 8th-bit stripping in commaize. Fix a problem with mis-parsing of backslash escapes under some circumstances. NIS map fix (was always including trailing null character) from Mike Glendinning of Ingres UK. Add "a" mailer flag to try using ESMTP. It tries the EHLO command and if that fails falls back to regular SMTP. Also parses EHLO option keywords. If host supports SIZE extension, this is added to the MAIL FROM: command. Extend "b" option to include a second value which is the maximum message size this server is willing to accept. For example, a value of "10/1000000" says that there must be ten blocks free, and sendmail will reject any message larger than one megabyte. Some portability hooks for NeXT (this could be applicable to Mach in general). You have to create an empty file called "unistd.h" to get it to compile. Adjust config values (MAXLINE, MAXATOM, and PSBUFSIZE) to be more generous. Add X400-Received: to the list of headers tagged with H_TRACE in conf.c. From Bill King, Allen-Bradley Co. 6.54/6.25 93/04/19 Fix problem that caused redefinition of SMTP and QUEUE compile flags. Pointed out by Jon Forrest of the Sequoia 2000 project at Berkeley. Properly handle \! hack -- it was treating host\!user as one token (host!user) instead of three (host, !, user). Fix from Eric Wassenaar of NIKHEF-H. Fix compilation problem in getauthinfo() if IDENTPROTO is off. Turn off DEFNAMES and DNSRCH when getting the hostsignature (i.e., MX records) in level 1 configuration files; this matches the old behaviour. From Motonori Nakamura of Kyoto University. Improve error message printing -- if sent through an alias, error messages include the name of the alias in the message. Unfortunately, in order to make this work properly in queue runs, this changes the format of the C line in the qf file. The relatively uselessness of the previous information was pointed out to me by Allan E Johannesen of WPI. Add XLA compile flag to add hooks to Christophe Wolfhugel's extended load average code. This is still in very early form. For information regarding the guts of the xla code, contact Christophe.Wolfhugel@grasp.insa-lyon.fr. Additional hooks for detecting tempfails in rewriting rules (that is, in map lookups). 6.53/6.25 93/04/15 Properly diagnose ruleset zero returning null (instead of a mailer triple). From Motonori Nakamura of Kyoto University. More generalization of socket code for other protocols. Shorten timeouts on reverse name lookups -- since they are done during connection establishment, long timeouts here can cause higher level timeouts. This mainly serves to accept mail from hosts that do not have proper reverse (PTR) DNS records set up. Reset e_statmsg before each mailer invocation to avoid bogus messages in the log. Redefine $r, $s, and $_ in error envelopes so you don't get incorrect cruft in the error message. Problem noted by Motonori Nakamura of Kyoto University. Fix a problem that can cause failure to return errors to Postmaster in certain cases. From Motonori Nakamura. Fix a problem that can cause some systems to give duplicate error messages when a bad syntax address such as " $3 the input "user@a.b.c" failed instead of being properly rewritten as "user@a..c". Neil also convinced me that it was correct that $~ should match only one token -- the problem is that it's always possible to add another token, so $~ matches far too eagerly. 6.45/6.21 93/03/25 Implement multi-word classes (properly!). 6.44/6.21 93/03/25 Add X-Authentication-Warning: headers to clue users into possible attempts to forge mail. This is on the authwarnings privacy flag, but is the default. Suggested by Bryan Costales of ICSI. Pass default units for convtime in so they can be more reasonable. Allow config files to always add a new Comments: header (i.e., they will be added even if an old one already exists). Suggested by Bryan Costales of ICSI. Allow config files to delete an existing Return-Path: header. These should only be added at final delivery. Suggested by Bryan Costales of ICSI. Some debugging additions. Suggested by Bryan Costales of ICSI. Clean up logging of Family 0 addresses. Noted by David Muir Sharnoff and others. Add a "dequote" map class. This allows config files to strip quotes off of addresses. Note that this is not a builtin map, just a class -- so you have to define the map using the K line. Fix a bug in the queueup() loop getting a locked tf where in very odd cases it can fall off the bottom and core dump. Of course, it was P{r Emanuelsson who found it.... Open a new transcript when splitting an envelope. Problem found by Allan E Johannesen of WPI. Improved error output in endmailer if the mailer core dumps. CONFIG: Fix typo in UUCP mailer definition. CONFIG: Default several of the new options on: eight bit input, privacy flags set to "authwarnings", and message warning set to 4h. CONFIG: Use dequote map. 6.43/6.20 93/03/23 Fix problem with assumption of an sa_len field in a generic sockaddr -- it turns out that most vendors haven't picked up this (very important) fix. Change compilation flags for daemon code -- select one or both of NETINET or NETISO, but don't ever set DAEMON manually. CONFIG: add FEATURE(mailertable) to do IDA-style mailertables. 6.42/6.19 93/03/19 Use Postmaster as default fallback return address, not root. POSIX changes for file descriptor handling. Diagnose errors writing new queue file. If you change the owner using an owner- alias, also change the error mode to EM_MAIL so that errors don't get dropped into an inappropriate directory. Problem noted by Allan E Johannesen of WPI. If you are su'ed to root, send email as who you really are, not as root. From Brian Kantor of U.C. San Diego. Allow warning messages to be sent after a configurable interval has passed without delivery. The message is sent only once per envelope. This changes the format of the qf file to have an F line, and the format of the T option to accept take the format "return/warn" (both intervals). Don't force all local names to lower case -- this was left over from the weird handling of case mapping on aliases. It is now driven (as expected) by the "u" mailer flag. Problem noted by P{r Emanuelsson. Fix problem that caused headers on returned email to be trashed; they were getting freed, but are still accessible via BlankEnvelope. Fix problem that caused bogus ids to be created on returned mail. Add support for ISO and other non-INET networking. This is by no means finished yet. This does assume a lot of other system support, like a version of gethostbyname that returns non-AF_INET addresses. CONFIG: change default on prog mailer to keep upper case in user names (i.e., in the program command line). CONFIG: strip trailing dots off of hosts in uucp mailer before convert to bang format. CONFIG: create new "relay" mailer for $R (LOCAL_RELAY) and $H (MAIL_HUB) delivery that doesn't add local domain. Note that this violates 821, but is probably "more correct" for what we are trying to do. Problem pointed out by Michael Graff of Iowa State. 6.41/6.18 93/03/18 Clean up unnecessary creates of queue ids (i.e., empty qf files) when not needed, such as when starting up an SMTP connection. Fix problem where split envelopes aren't instantiated in the queue. This is quite a serious bug. Owner- aliases had problems with leading spaces causing a premature delimitation. 6.40/6.18 93/03/18 Have ending 250 (after DATA) include the id; suggested by Brian Kantor of UC San Diego. Add logging on envelope splitting. Change queue ids to have one more letter encoding the hour of the day so that during a single day there is a greater likelihood of uniqueness; requested by Brian Kantor. 6.39/6.18 93/03/18 Fix minor compile problem if LOCKF is defined. Define size of tobuf in conf.h. Observed by Toshinari Takahashi of Toshiba. Restore e_sender -- this is equivalent to e_from.q_paddr without decorations such as angle brackets and comments. OSF/1 on Alpha changes from Allan E Johannesen of WPI. CONFIG: fix typo in S3 for list syntax (;: => :;). Thanks to Christopher Hoover for noting the problem. 6.38/6.17 93/03/17 Pass envelope to disconnect to avoid another use of CurEnv, which can apparently end up being null at inopportune times. Log "received from" as "relay=" for consistency (suggested by John Gardiner Myers). Fix major bug in header handling: if no From: line existed in the header (so sendmail inserts one), and the sender is an alias that has an owner, the From: line shows the owner (as well as the envelope). Fixed by early binding the headers (which will change debugging output). HPUX portability patches from Michael J. Corrigan of UC San Diego. Some attempts to adapt better to out of open file conditions. Some changes to ctladdr handling in queue files. 6.37/6.17 93/03/16 MAJOR CHANGE: delete e_sender and e_returnpath (why are these different from e_from?) and $< macro. Log correct IP address in relay= field even if the connection times out. Log "received from [RESPONSE]" on EF_RESPONSE messages (from John Gardiner Myers). Fixes to SysExMsg logging (sometimes just got "message: %s" instead of "message: error message"), noted by Eric Wassenaar. Also reported by Motonori Nakamura. Improvements to MX piggybacking code, from Motonori Nakamura. Fix case where CurHostName points to an auto variable that has been deallocated (from Motonori Nakamura). Fix bug causing newlines to be included in aliases if option "n" (check alias RHS) is set; bug noted by David Muir Sharnoff. Fix problem causing user names that should be mapped to lower case to not be mapped if they are sent during a queue run. This greatly simplifies the case mapping code. Problem noted by Allan E Johannesen of WPI. Don't do recipient address rewriting in buildaddr. This improperly did recipient rewriting on sender addresses, and just seems bogus in general -- but the change could break some .cf files. Pass TZ envariable to child processes for System V. CONFIG: allow LOCAL_RULE_1 and LOCAL_RULE_2 if you want to define those rulesets. KNOWN PROBLEM: I have seen some problems on SunOS that causes the User Data Base to give errors on some addresses. I have tracked the problem back at least as far as 93.02.15 (version 6.22). Running with debugging on makes it go away, so I conclude that it is referencing uninitialized stack data. I haven't been able to track this down yet. 6.36/6.16 93/03/08 Allow local mailer to specify $@host -- this lets you assign the "foo" part of jgm+foo to $h for passing in to the local mailer. Additional debug printing in getcanonname (show query type). Don't add the e_fromdomain on sender addresses -- this interacts weirdly with the owner- code. Improve delivery logging to not log obvious or meaningless stuff. Include numeric IP address in Received: lines per RFC 1123 section 5.2.8. Fixed a bug in checking stat() return value if restrictmailq is set. Also, check the entire group set instead of just the primary group. Both from John Gardiner Myers. Don't have usrerr automatically print errno, since this is often misleading. Use transienterror() in makeconnection after connect() fails and in openmailer after execve() fails (from Eric Wassenaar). Also moved transienterror() from util.c to conf.c. Clean up from= logging on response messages. Undo patch allowing prescan to return a null vector -- it breaks too many things. Config: FEATURE(notsticky) lets you use UDB for everything coming in to the machine, even if it is specifically targetted to this machine. Without it, UDB is bypassed if the user name is fully qualified. Config: fix another minor botch with <> (local mailer wasn't mapping them properly). 6.35/6.15 93/03/05 Fix getrealhostname to return null if sinlen <= 0 -- this can occur if stdin is a pipe. Avoid infinite loop in getcanonname if name server return NO_DATA (for example). Config: avoid having C flag qualify list syntax and error syntax. 6.34/6.14 93/03/05 Fix logging in deliver to not pass too many parameters to Ultrix versions of syslog. Don't write the pid file until after the daemon has actually opened and conditioned the connection. Consider addresses "different" if their q_uids differ (so that two users forwarding to the same program will be seen as different, rather than the same). Fix problem with bad parameters in main() -- they set ExitStat but don't exit. Fix null pointer references through RealHostName -- painfully discovered by Allan E Johannesen of WPI. Fix bug causing user@@localhost to core dump (yuch). Config: don't put two @host.dom.ain on users in $=E in SMTP mailer. Also, catch user@ (no host) in ruleset 0. 6.33/6.13 93/03/03 Config: add confCW_FILE as the name of the cw configuration file (defaults to /etc/sendmail.cw). From P{r Emanuelsson. Allow prescan to return a pointer to an empty list -- this is not an error. Also, clean up error reporting to avoid double errors (prescan reports once, then the caller reports again). Changes to avoid trusting T_ANY queries -- run them, but if you don't get the info you expected, do T_A and T_MX queries anyhow. This also fixes an oversight where _res.options bits were being ignored. If PRIV_NOVRFY is set, use 252 response code instead of 502 per RFC 1123 section 5.2.3. It's not 100% clear that this is correct, but it probably works better with stupid mailers that do a VRFY and only check the first digit. 6.32/6.12 93/03/02 Fix uninitialized variable "protocol" in smtp code. Include in sendmail.h -- move towards POSIX/ANSI. Additional hooks for RFC 1427 (ESMTP SIZE extension). This includes requiring that enoughspace() know the system block size, which will undoubtedly break most ports. Trace flag 19 in use for srvrsmtp.c. Additional logging -- notably the sending mailer name. This also changes the delivery logging to strict field=value syntax. Fix some problems with messages getting sent even to addresses that had been marked bad -- from Eric Wassenaar. More WIDE changes: accept host name inside [...] as non-MXed host. This is intended ONLY for use inside firewalled environments, where the MX points at the gateway. Change .cf file conventions so that mapping for <> addresses don't have an @ in them (to avoid confusing the C mailer flag). Pointed out by Neil Rickert. Config extensions for Sam Leffler's FlexFAX software. 6.31/6.10 93/02/28 Fix some more bugs in alias owner code -- there were some weird cases where an error in a non-aliased name would override the return info in an aliased name with an owner. Changes from WIDE Project, forwarded to me by Motonori Nakamura: Log actual delivery host (after MX et al); from yasuhiro@dcl.co.jp. Log daemon startup. Deliver Postmaster copies without a body. Better logging of SMTP senders. Send all program email as daemon even when local. As requested in various forms from many people, accept -qIstring to limit queue runs to jobs with queue-id matching string. Similarly for -qRstring for recipients, -qSstring for senders. Initial hooks for ESMTP support (see RFC 1425). Fixed a syntax error in the UUCP mailer specification that caused core dumps on startup. Check for missing A= or P= arguments in mailer definitions. 6.30/6.10 93/02/27 Require FROZENCONFIG compilation flag to include frozen configuration code. Frozen configuration is really not a very good idea any more, particularly in shared library environments. Do better checking of errno after opens of :include: and .forward files to defer delivery on network and other transient errors. Suggestion from Craig Everhart. Fix minor botch in read timeout macro processing. Add FEATURE(nouucp) to config files for sites that know absolutely nothing about UUCP. Add built cf files to distribution tape and clarify how to build them if you don't have the Berkeley make. Some sizeof(long) portability changes for the Alpha, from Allan E Johannesen. Add "restrictmailq" privacy flag -- if set, only people in the same group as your queue directory can print the queue. If you set this, be sure you also restrict access to log files.... Fix another bug in owner-list stuff that can cause data files to be "lost". Fix a bug with queue runs that cause forwards to yourself to go into alias/forwarding loops. I'm still iffy about this fix. Fix from Eric Wassenaar for suppression of return message code. 6.29/6.9 93/02/24 Fix yet another problem in alias owner code -- put the wrong return address on the enclosed return-to-sender letter. 6.28/6.9 93/02/24 Fix botch in alias owner code that caused it to not operate if the error was detected locally. 6.27/6.9 93/02/24 M_LOCAL => M_LOCALMAILER to avoid conflict with Ultrix include file . Miscellaneous bug fixes from Eric Wassenaar: sendmail -bv -t logs the from line even though in verify mode only. sendmail -v can go into queue mode if shouldqueue returns TRUE. Add route-addr pruning per RFC 1123 section 5.3.3. This can be disabled using the "R" option. Delete (always undocumented) -R flag (save original recipients); there are ways to syslog(3) these now. Clean up SMTP reply codes -- specify them as needed in the code, instead of in conf.c -- this was needed during the NCP to TCP transition, but seems silly now. This also changes parameters to message and nmessage. Have mailstats read the .cf file to find the sendmail.st file and get text versions of mailer names. An initial version of this code was provided by Tuominen Keijo (although the comments indicate the good bits were written by "E.V."). Add yet more System V compatibility hacks. Fix bug in VRFY code (assumes everything must be a local user). Allow specification of any of the hard-wired pathnames in the Makefile. Delete concept of "trusted users" -- this really didn't provide any security anyway, and caused some problems. Delete last vestige of support for the word "at" as an equivalent to the character "@". Propagate owner-foo alias information into the envelope sender. Based on code from John Gardiner Myers. This is a major semantic change -- beware! Allow $@ on LHS to indicate "match zero" -- this is used to match the null expression. 6.26/6.8 93/02/21 Don't "lose" queue runs. Very important fix from (who else?) Eric Wassenaar. Completely reset state on RSET command -- from Eric Wassenaar. Send error messages and return receipts using an envelope sender of <> regardless of the setting of $n. Rewriting rules can undo this if they feel the necessity, as might be needed for networks that don't understand the syntax. This is permitted by RFC 821 section 3.6 and required by RFC 1123 section 5.3.3. THIS REQUIRES VERSION 4 CONFIG FILES because the rulesets must be able to parse <> properly. Don't ever send error messages to "<>" -- they will get sent to the local postmaster or dumped in /usr/tmp/dead.letter instead. Per RFC 1123 section 5.3.3. Explicitly check for email to yourself as a dotted quad. You have to call $[ [ ... ] $] to get this. Up the message timeout to five days per RFC 1123 section 5.3.1.1. Make all read timeouts individually configurable, as strongly recommended by RFC 1123 section 5.3.2. Use f_bavail (blocks available to regular users) instead of f_bfree (blocks available to superuser) in free block checks. Change $d macro to be the current time, not the origination time, since this is consistent with how it is used now. Generalization of enoughspace from Eric Wassenaar covering SGI, Apollo, HPUX, Ultrix, and SunOS. Ignore process group signals -- some front ends can do this if you kill a window too quickly. From Eric Wassenaar. Change umask to 022. 6.25/6.8 93/02/20 Close all cached connections before calling mailers and after forking for delivery (caused double closes which resulted in false errors). Add FEATURE(redirect) in config files -- this allows you to alias old addresses to a pointer to the new address that will give a 551 error message, but not deliver the mail. Some code changes to make the 551 errors look pretty. Names of M4 program paths in config files have changed -- they are all XXX_MAILER_PATH now, to match XXX_MAILER_FLAGS. Fix a bug in the QSELFREF code having to do with empty .forward files, reported by Eric Wassenaar. Add option "p" (privacy flags); this allows you to tune how picky the SMTP server will be. This also adds the confPRIVACY_FLAGS M4 macro in the config files. Add option "b" (minimum blocks free). If there are fewer than this number of blocks free on the filesystem containing the queue directory, the SMTP MAIL command will return a 452 response and ask you to try again later. This also adds the confMIN_FREE_BLOCKS M4 macro in the config files. Made VRFY just verify (doesn't expand aliases and .forward files); EXPN does full expansion. RCPT in queue-only mode also doesn't chase aliases and .forward. 6.24/6.7 93/02/19 Increase the number of domain search entries in domain.c to allow for the extra "" entry indicating the root domain. Reported by Motonori Nakamura of Kyoto U. Add a "SMART_HOST" in the configs for UUCP-connected sites that want to forward all mail with extra "@"s to that site. Also allows SMART_HOST, LOCAL_RELAY, and MAIL_HUB to be specified as ``mailer:hostname'' to use an alternate mailer. Clarified and updated some wording in the Operations Guide. Add the "c" mailer flag -- this suppresses all comment parts of addresses (requested by John Curran of NEARnet). Have -v print prompts in -bt mode even if stdin is not a terminal (default behaviour is to be silent if not reading from a terminal). Suggested by Bryan Costales, ICSI. Move the metacharacters from C0 space (\001-\037) into C1 space (\201-\237). This also fixes a bunch of potential bugs with G1 characters (\240-\276) in headers relating to negative numbers passed to isspace() et al. Add YP_LAST_MODIFIED and YP_MASTER_NAME to DBM version of alias database if YPCOMPAT is #defined. Enhancement from Takahiro Kanbe of Fuji Xerox Information Systems Co., Ltd. Add "list" Precedence (-30); this can be used with old sendmails which will map to precedence 0 (which will return error messages). Suggested by Stephen R. van den Berg. Many bug fixes from Eric Wassenaar of the National Institute for Nuclear and High-Energy Physics, Amsterdam: Clear timeouts properly on open failures in include(). Don't dereference through NULL if no home directory found. Re-establish SIGCHLD signal on System 5 in reapchild(). Avoid NULL pointer reference on -pFOO flag. Properly handle backslash escapes in comments. Correctly check reply status on SMTP NOOP command. Properly save SMTP error message if peer gives "Service Shutting Down" message. Avoid writing to the transcript if it couldn't be opened. Signal errors in SMTP children to parent properly. Handle self references in a list more globally (include a QSELFREF bit in the address flags). This enhancement was suggested by Eric Wassenaar. Use initgroups() in hpux, even though it's System-V based. The HASINITGROUPS compile flag can set this on other systems. This HPUX behaviour was pointed out by Eric Wassenaar. 6.23/6.6 93/02/16 Clean up handling of LogLevel to make it easier to figure out what's on what level. Change log levels to have some consistency: 1 serious system failures, security problems 2 lost communications, protocol failures 3 other serious failures 4 minor errors 5 message collection 6 vrfy logging, creation of return-to-sender 7 delivery failures 8 delivery successes 9 delivery tempfails (queue ups) 10 database expansion >64 debugging Allow IDA-style separated processing on S= and R= in Mailer definition lines. Note that rulesets 1 and 2 are still used for both addresses as before. Bruce Lilly gave a convincing argument that RFC976 insists on this behaviour. Added some time zones to arpatounix -- they may not be in the standards, but they are in use. However, I may delete arpatounix entirely -- there appears to be no reason for it to exist. Change to UUCP mailer (in cf directory) to try to do a saner job. I'm still not certain about this mailer in general. 6.22/6.5 93/02/15 Fix bug that prevents saving letters in ~/dead.letter. Don't add angle brackets in VRFY command if angle brackets already exist in the address. Fix bogus error message in udbexpand. Null terminate host buffers in buildaddr (broken in 6.21) -- IMPORTANT FIX!! 6.21/6.5 93/02/15 Fix another incorrect error message in alias.c, found by Azuma Okamoto. Fix a couple of problems in the more-configurable config files, found by Tom Ivar Helbekkmo. Fix problem with quoted :include: entries. Don't duplicate the filename on verbose printing of .forward and :include: contents. Extend size of prescan buffer (to allow bigger addresses). Also, detect some buffer overflows. Log user SMTP protocol errors (log level 4). 6.20/6.4 93/02/14 Fix another problem in the MCI state machine caused when there were errors generated from the other end to commands other than RCPT. 6.19/6.4 93/02/14 Include load average support for DEC Alpha running OSF/1. Fix multiple-response problem with errors in MAIL From: line. Fix SMTP reply codes for invalid address syntaxes (give 501; never give multiple error messages for a single message). Fix problem where a cached connection timeout rejects all later connects to that host. Fix incorrect error message if alias.c is compiled with DBM only. Additional changes to fix nested conditionals (from Bruce Lilly). Recover more gracefully from operating system failures, particularly NULL returns from openmailer (from Noritoshi Demizu, OMRON Corporation). Log forward, alias, and userdb expand operations on log level 10; concept suggested by P{r (Pell) Emanuelsson. Changes for HPUX 8.07 compatibility. 6.18/6.4 93/02/12 Allow any config option to be set using an M4 define. Change UNAME compile flag to HASUNAME for IDA compatibility (besides, it's a better name). Note in README that on SunOS it must be linked -Bstatic. Fairly major change in domain.c to handle wildcard MX records more rationally. NOTE: the "w" option (no wildcard MX records match local domain) has been eliminated. Fix some unset variable references pointed out by Bruce Lilly. Fix host name in process titles when using cached connection. 6.17/6.3 93/01/28 Fix System 5 compatibility changes to be compatible with the rest of the world. 6.16/6.3 93/01/28 Experimental fix for problem handling errors in the SMTP protocol in conjunction with connection caching. System 5 compatibility changes. 6.15/6.3 93/01/26 Fix a bug that causes local mail delivered using -odq to be eliminated as a duplicate (because it matched the ctladdr, now passed in as a C line). These changes are pretty tricky...... 6.14/6.3 93/01/25 Add debugging for some MCI errors. 6.13/6.3 93/01/22 Fix -e compatibility flag to take a value. Fix a couple of minor compilation warnings on Sun cc. Improve error messages in a few cases to be more self-explanatory. 6.12/6.3 93/01/21 Fix yet-another problem with environment handling, pointed out by Yoshitaka Tokugawa and Tom Ivar Helbekkmo. Some heuristics to try to limit resource exhaustion problems if a downstream host has been down for a long time. Fix problem with incorrect host name being logged in "Connection timed out" messages (from Tom Ivar Helbekkmo). Fix some ANSI C problems (from Takahiro Kanbe). Properly log message sender on returned mail during queue run. Count number of recipients properly. Fix a problem in yp map code. Diagnose "message timed out" (from Motonori Nakamura). 6.11/6.3 93/01/20 Fix problem with address delimitor inside quotes. Define $k and $=k to be the UUCP name (from the uname call) based on code from Bruce Lilly. 6.10/6.2 93/01/18 Implement arpatounix (largely code from Bruce Lilly). Log more info (suggested by John Myers). Allow nested $?...$|...$. (inspired by code from Bruce Lilly of Sony US). POSIX compatibility (noted by Keith Bostic). Handle SMTP MAIL command errors properly (urged by several people, notably John Myers of CMU). Do early diagnosis of .cf errors (notably referencing a RHS substitution that isn't on the LHS). Adjust checkpointing to better handle batched recipients, suggested by John Myers. Fix miscellaneous bugs. (config files:) Implement MAIL_HUB for all local mail (to handle NFS-mounted directories) as urged by Tom Ivar Helbekkmo of the Norwegian School of Economics. 6.9/6.1 93/01/13 Environment handling simplification/bug fix -- child processes get a minimal, fixed environment. This avoids different behaviour in queue runs. Handle commas inside comments properly. Properly limit large messages submitted in -obq mode. 6.8/6.1 93/01/10 Check mtime of thaw file against .cf and sendmail binary, based on code from John Myers. 6.7/6.1 93/01/10 MX piggybacking, based on code from John Myers@CMU. Allow checkcompat to return -1 to mean tempfail. Bug fix in m_mno computation. 6.6/6.1 93/01/09 Tuning of queueing functions as recommended by John Gardiner Myers. Return mail headers (no body) on messages with negative precedence. Minor other bug fixes. 6.5/6.1 93/01/03 Fix botch causing queued headers to have ?XX? prefixes. 6.4/6.1 93/01/02 Changes to recognize special mailer types (e.g., file) early. 6.3/6.1 93/01/01 Pass timeouts to sfgets. Check for control characters in addresses. Fixed deferred error reporting. Report duplicate aliases. Handle mixed case recursive aliases. Misc bug fixes. 6.2/6.1 92/12/30 Put return-receipt-to on a conf.c flag (but don't set it). Fix minor syslog problem. Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/README =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/README (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/README (revision 26986) @@ -1,1961 +1,1984 @@ NEW SENDMAIL CONFIGURATION FILES Eric Allman - @(#)README 8.111 (Berkeley) 1/16/97 + @(#)README 8.120 (Berkeley) 6/14/97 This document describes the sendmail configuration files being used at Berkeley. These use features in the new (R8) sendmail; they will not work on other versions. These configuration files are probably not as general as previous versions, and don't handle as many of the weird cases automagically. I was able to simplify them for two reasons. First, the network has become more consistent -- for example, at this point, everyone on the internet is supposed to be running a name server, so hacks to handle NIC-registered hosts can go away. Second, I assumed that a subdomain would be running SMTP internally -- UUCP is presumed to be a long-haul protocol. I realize that this is not universal, but it does describe the vast majority of sites with which I am familiar, including those outside the US. Of course, the downside of this is that if you do live in a weird world, things are going to get weirder for you. I'm sorry about that, but at the time we at Berkeley had a problem, and it seemed like the right thing to do. This package requires a post-V7 version of m4; if you are running the 4.2bsd, SysV.2, or 7th Edition version, I suggest finding a friend with a newer version. You can m4-expand on their system, then run locally. SunOS's /usr/5bin/m4 or BSD-Net/2's m4 both work. GNU m4 version 1.1 or later also works. Unfortunately, I'm told that the M4 on BSDI 1.0 doesn't work -- you'll have to use a Net/2 or GNU version. GNU m4 is available from ftp://prep.ai.mit.edu/pub/gnu/m4-1.4.tar.gz (check for -the latest version). +the latest version). EXCEPTIONS: DEC's m4 on Digital UNIX 4.x is broken +(3.x is fine). Use GNU m4 on this platform. IF YOU DON'T HAVE A BERKELEY MAKE, don't despair! Just run "m4 ../m4/cf.m4 foo.mc > foo.cf" -- that should be all you need. There is also a fairly crude (but functional) Makefile.dist that works on the old version of make. To get started, you may want to look at tcpproto.mc (for TCP-only sites), uucpproto.mc (for UUCP-only sites), and clientproto.mc (for clusters of clients using a single mail host). Others are versions that we use at Berkeley, although not all are in current use. For example, ucbarpa has gone away, but I've left ucbarpa.mc in because it demonstrates some interesting techniques. I'm not pretending that this README describes everything that these configuration files can do; clever people can probably tweak them to great effect. But it should get you started. ******************************************************************* *** BE SURE YOU CUSTOMIZE THESE FILES! They have some *** *** Berkeley-specific assumptions built in, such as the name *** *** of our UUCP-relay. You'll want to create your own domain *** *** description, and use that in place of domain/Berkeley.m4. *** ******************************************************************* +--------------------------+ | INTRODUCTION AND EXAMPLE | +--------------------------+ Configuration files are contained in the subdirectory "cf", with a suffix ".mc". They must be run through "m4" to produce a ".cf" file. You must pre-load "cf.m4": m4 ${CFDIR}/m4/cf.m4 config.mc > config.cf where ${CFDIR} is the root of the cf directory and config.mc is the name of your configuration file. If you are running a version of M4 that understands the __file__ builtin (versions of GNU m4 >= 0.75 do this, but the versions distributed with 4.4BSD and derivatives do not) or the -I flag (ditto), then ${CFDIR} can be in an arbitrary directory. For "traditional" versions, ${CFDIR} ***MUST*** be "..", or you MUST use -D_CF_DIR_=/path/to/cf/dir/ -- note the trailing slash! For example: m4 -D_CF_DIR_=${CFDIR}/ ${CFDIR}/m4/cf.m4 config.mc > config.cf Let's examine a typical .mc file: divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # # # This is a Berkeley-specific configuration file for HP-UX 9.x. - # It applies only the the Computer Science Division at Berkeley, + # It applies only to the Computer Science Division at Berkeley, # and should not be used elsewhere. It is provided on the sendmail # distribution as a sample only. To create your own configuration # file, create an appropriate domain file in ../domain, change the # `DOMAIN' macro below to reference that file, and copy the result # to a name of your own choosing. # divert(0) The divert(-1) will delete the crud in the resulting output file. The copyright notice can be replaced by whatever your lawyers require; our lawyers require the one that I've included in my files. A copyleft is a copyright by another name. The divert(0) restores regular output. VERSIONID(`') VERSIONID is a macro that stuffs the version information into the resulting file. We use SCCS; you could use RCS, something else, or omit it completely. This is not the same as the version id included in SMTP greeting messages -- this is defined in m4/version.m4. OSTYPE(hpux9)dnl You must specify an OSTYPE to properly configure things such as the pathname of the help and status files, the flags needed for the local mailer, and other important things. If you omit it, you will get an error when you try to build the configuration. Look at the ostype directory for the list of known operating system types. DOMAIN(CS.Berkeley.EDU)dnl This example is specific to the Computer Science Division at Berkeley. You can use "DOMAIN(generic)" to get a sufficiently bland definition that may well work for you, or you can create a customized domain definition appropriate for your environment. MAILER(local) MAILER(smtp) These describe the mailers used at the default CS site site. The local mailer is always included automatically. Beware: MAILER declarations should always be at the end of the configuration file, and MAILER(smtp) should always precede MAILER(uucp). The general rules are that the order should be: VERSIONID OSTYPE DOMAIN FEATURE local macro definitions MAILER LOCAL_RULESET_* +----------------------------+ | A BRIEF INTRODUCTION TO M4 | +----------------------------+ Sendmail uses the M4 macro processor to ``compile'' the configuration files. The most important thing to know is that M4 is stream-based, that is, it doesn't understand about lines. For this reason, in some places you may see the word ``dnl'', which standards for ``delete through newline''; essentially, it deletes all characters starting at the ``dnl'' up to and including the next newline character. In most cases sendmail uses this only to avoid lots of unnecessary blank lines in the output. Other important directives are define(A, B) which defines the macro ``A'' to have value ``B''. Macros are expanded as they are read, so one normally quotes both values to prevent expansion. For example, define(`SMART_HOST', `smart.foo.com') One word of warning: M4 macros are expanded even in lines that appear to be comments. For example, if you have # See FEATURE(foo) above it will not do what you expect, because the FEATURE(foo) will be expanded. This also applies to # And then define the $X macro to be the return address because ``define'' is an M4 keyword. If you want to use them, surround them with directed quotes, `like this'. +--------+ | OSTYPE | +--------+ You MUST define an operating system environment, or the configuration file build will puke. There are several environments available; look at the "ostype" directory for the current list. This macro changes things like the location of the alias file and queue directory. Some of these files are identical to one another. It is IMPERATIVE that the OSTYPE occur before any MAILER definitions. In general, the OSTYPE macro should go immediately after any version information, and MAILER definitions should always go last. Operating system definitions are usually easy to write. They may define the following variables (everything defaults, so an ostype file may be empty). Unfortunately, the list of configuration-supported systems is not as broad as the list of source-supported systems, since many of the source contributors do not include corresponding ostype files. ALIAS_FILE [/etc/aliases] The location of the text version of the alias file(s). It can be a comma-separated list of names (but be sure you quote values with commas in them -- for example, use define(`ALIAS_FILE', `a,b') to get "a" and "b" both listed as alias files; otherwise the define() primitive only sees "a"). HELP_FILE [/usr/lib/sendmail.hf] The name of the file containing information printed in response to the SMTP HELP command. QUEUE_DIR [/var/spool/mqueue] The directory containing queue files. STATUS_FILE [/etc/sendmail.st] The file containing status information. LOCAL_MAILER_PATH [/bin/mail] The program used to deliver local mail. LOCAL_MAILER_FLAGS [rmn] The flags used by the local mailer. The flags lsDFM are always included. LOCAL_MAILER_ARGS [mail -d $u] The arguments passed to deliver local mail. LOCAL_MAILER_MAX [undefined] If defined, the maximum size of local mail that you are willing to accept. LOCAL_MAILER_CHARSET [undefined] If defined, messages containing 8-bit data that ARRIVE from an address that resolves to the local mailer and which are converted to MIME will be labelled with this character set. LOCAL_SHELL_PATH [/bin/sh] The shell used to deliver piped email. LOCAL_SHELL_FLAGS [eu] The flags used by the shell mailer. The flags lsDFM are always included. LOCAL_SHELL_ARGS [sh -c $u] The arguments passed to deliver "prog" mail. LOCAL_SHELL_DIR [$z:/] The directory search path in which the shell should run. USENET_MAILER_PATH [/usr/lib/news/inews] The name of the program used to submit news. USENET_MAILER_FLAGS [rlsDFMmn] The mailer flags for the usenet mailer. USENET_MAILER_ARGS [-m -h -n] The command line arguments for the usenet mailer. USENET_MAILER_MAX [100000] The maximum size of messages that will be accepted by the usenet mailer. SMTP_MAILER_FLAGS [undefined] Flags added to SMTP mailer. Default flags are `mDFMUX' for all SMTP-based mailers; the "esmtp" mailer adds `a' and "smtp8" adds `8'. SMTP_MAILER_MAX [undefined] The maximum size of messages that will be transported using the smtp, smtp8, or esmtp mailers. SMTP_MAILER_ARGS [IPC $h] The arguments passed to the smtp mailer. About the only reason you would want to change this would be to change the default port. ESMTP_MAILER_ARGS [IPC $h] The arguments passed to the esmtp mailer. SMTP8_MAILER_ARGS [IPC $h] The arguments passed to the smtp8 mailer. RELAY_MAILER_ARGS [IPC $h] The arguments passed to the relay mailer. SMTP_MAILER_CHARSET [undefined] If defined, messages containing 8-bit data that ARRIVE from an address that resolves to one of the SMTP mailers and which are converted to MIME will be labelled with this character set. UUCP_MAILER_PATH [/usr/bin/uux] The program used to send UUCP mail. UUCP_MAILER_FLAGS [undefined] Flags added to UUCP mailer. Default flags are `DFMhuU' (and `m' for uucp-new mailer, minus `U' for uucp-dom mailer). UUCP_MAILER_ARGS [uux - -r -z -a$g -gC $h!rmail ($u)] The arguments passed to the UUCP mailer. UUCP_MAILER_MAX [100000] The maximum size message accepted for transmission by the UUCP mailers. UUCP_MAILER_CHARSET [undefined] If defined, messages containing 8-bit data that ARRIVE from an address that resolves to one of the UUCP mailers and which are converted to MIME will be labelled with this character set. FAX_MAILER_PATH [/usr/local/lib/fax/mailfax] The program used to submit FAX messages. FAX_MAILER_ARGS [mailfax $u $h $f] The arguments passed to the FAX mailer. FAX_MAILER_MAX [100000] The maximum size message accepted for transmission by FAX. POP_MAILER_PATH [/usr/lib/mh/spop] The pathname of the POP mailer. POP_MAILER_FLAGS [Penu] Flags added to POP mailer. Flags "lsDFM" are always added. POP_MAILER_ARGS [pop $u] The arguments passed to the POP mailer. PROCMAIL_MAILER_PATH [/usr/local/bin/procmail] The path to the procmail program. This is also used by FEATURE(local_procmail). -PROCMAIL_MAILER_FLAGS [Shu] Flags added to Procmail mailer. Flags - ``DFMmn'' are always set. This is NOT used by +PROCMAIL_MAILER_FLAGS [SPhnu9] Flags added to Procmail mailer. Flags + ``DFM'' are always set. This is NOT used by FEATURE(local_procmail); tweak LOCAL_MAILER_FLAGS instead. -PROCMAIL_MAILER_ARGS [procmail -m $h $f $u] The arguments passed to +PROCMAIL_MAILER_ARGS [procmail -Y -m $h $f $u] The arguments passed to the Procmail mailer. This is NOT used by FEATURE(local_procmail); tweak LOCAL_MAILER_ARGS instead. PROCMAIL_MAILER_MAX [undefined] If set, the maximum size message that will be accepted by the procmail mailer. MAIL11_MAILER_PATH [/usr/etc/mail11] The path to the mail11 mailer. MAIL11_MAILER_FLAGS [nsFx] Flags for the mail11 mailer. MAIL11_MAILER_ARGS [mail11 $g $x $h $u] Arguments passed to the mail11 mailer. PH_MAILER_PATH [/usr/local/etc/phquery] The path to the phquery program. PH_MAILER_FLAGS [ehmu] Flags for the phquery mailer. PH_MAILER_ARGS [phquery -- $u] -- arguments to the phquery mailer. CYRUS_MAILER_FLAGS [A5@] The flags used by the cyrus mailer. The flags lsDFMnP are always included. CYRUS_MAILER_PATH [/usr/cyrus/bin/deliver] The progam used to deliver cyrus mail. CYRUS_MAILER_ARGS [deliver -e -m $h -- $u] The arguments passed to deliver cyrus mail. CYRUS_MAILER_MAX [undefined] If set, the maximum size message that will be accepted by the cyrus mailer. CYRUS_MAILER_USER [cyrus:mail] The user and group to become when running the cyrus mailer. CYRUS_BB_MAILER_FLAGS [undefined] The flags used by the cyrusbb mailer. The flags lsDFMnP are always included. CYRUS_BB_MAILER_ARGS [deliver -e -m $u] The arguments passed to deliver cyrusbb mail. +---------+ | DOMAINS | +---------+ You will probably want to collect domain-dependent defines into one file, referenced by the DOMAIN macro. For example, our Berkeley domain file includes definitions for several internal distinguished hosts: UUCP_RELAY The host that will accept UUCP-addressed email. If not defined, all UUCP sites must be directly connected. BITNET_RELAY The host that will accept BITNET-addressed email. If not defined, the .BITNET pseudo-domain won't work. DECNET_RELAY The host that will accept DECNET-addressed email. If not defined, the .DECNET pseudo-domain and addresses of the form node::user will not work. FAX_RELAY The host that will accept mail to the .FAX pseudo-domain. The "fax" mailer overrides this value. LOCAL_RELAY DEPRECATED. The site that will handle unqualified names -- that is, names with out an @domain extension. If not set, they are assumed to belong on this machine. This allows you to have a central site to store a company- or department-wide alias database. This only works at small sites, and only with some user agents. LUSER_RELAY The site that will handle lusers -- that is, apparently local names that aren't local accounts or aliases. Any of these can be either ``mailer:hostname'' (in which case the mailer is the internal mailer name, such as ``uucp-new'' and the hostname is the name of the host as appropriate for that mailer) or just a ``hostname'', in which case a default mailer type (usually ``relay'', a variant on SMTP) is used. WARNING: if you have a wildcard MX record matching your domain, you probably want to define these to have a trailing dot so that you won't get the mail diverted back to yourself. The domain file can also be used to define a domain name, if needed (using "DD") and set certain site-wide features. If all hosts at your site masquerade behind one email name, you could also use MASQUERADE_AS here. You do not have to define a domain -- in particular, if you are a single machine sitting off somewhere, it is probably more work than it's worth. This is just a mechanism for combining "domain dependent knowledge" into one place. +---------+ | MAILERS | +---------+ There are fewer mailers supported in this version than the previous version, owing mostly to a simpler world. As a general rule, put the MAILER definitions last in your .mc file, and always put MAILER(smtp) before MAILER(uucp) -- several features and definitions will modify the definition of mailers, and the smtp mailer modifies the UUCP mailer. local The local and prog mailers. You will almost always need these; the only exception is if you relay ALL your mail to another site. This mailer is included automatically. smtp The Simple Mail Transport Protocol mailer. This does not hide hosts behind a gateway or another other such hack; it assumes a world where everyone is running the name server. This file actually defines four mailers: "smtp" for regular (old-style) SMTP to other servers, "esmtp" for extended SMTP to other servers, "smtp8" to do SMTP to other servers without converting 8-bit data to MIME (essentially, this is your statement that you know the other end is 8-bit clean even if it doesn't say so), and "relay" for transmission to our RELAY_HOST, LUSER_RELAY, or MAILER_HUB. uucp The Unix-to-Unix Copy Program mailer. Actually, this defines two mailers, "uucp-old" (a.k.a. "uucp") and "uucp-new" (a.k.a. "suucp"). The latter is for when you know that the UUCP mailer at the other end can handle multiple recipients in one transfer. If the smtp mailer is also included in your configuration, two other mailers ("uucp-dom" and "uucp-uudom") are also defined [warning: you MUST specify MAILER(smtp) before MAILER(uucp)]. When you include the uucp mailer, sendmail looks for all names in the $=U class and sends them to the uucp-old mailer; all names in the $=Y class are sent to uucp-new; and all names in the $=Z class are sent to uucp-uudom. Note that this is a function of what version of rmail runs on the receiving end, and hence may be out of your control. See the section below describing UUCP mailers in more detail. usenet Usenet (network news) delivery. If this is specified, an extra rule is added to ruleset 0 that forwards all local email for users named ``group.usenet'' to the ``inews'' program. Note that this works for all groups, and may be considered a security problem. fax Facsimile transmission. This is experimental and based on Sam Leffler's FlexFAX software. For more information, see below. pop Post Office Protocol. procmail An interface to procmail (does not come with sendmail). This is designed to be used in mailertables. For example, a common question is "how do I forward all mail for a given domain to a single person?". If you have this mailer defined, you could set up a mailertable reading: host.com procmail:/etc/procmailrcs/host.com with the file /etc/procmailrcs/host.com reading: :0 # forward mail for host.com ! -oi -f $1 person@other.host This would arrange for (anything)@host.com to be sent to person@other.host. Within the procmail script, $1 is the name of the sender and $2 is the name of the recipient. If you use this with FEATURE(local_procmail), the FEATURE should be listed first. mail11 The DECnet mail11 mailer, useful only if you have the mail11 program from gatekeeper.dec.com:/pub/DEC/gwtools (and DECnet, of course). This is for Phase IV DECnet support; if you have Phase V at your site you may have additional problems. phquery The phquery program. This is somewhat counterintuitively referenced as the "ph" mailer internally. It can be used to do CCSO name server lookups. The phquery program, which this mailer uses, is distributed with the ph client. cyrus The cyrus and cyrusbb mailers. The cyrus mailer delivers to a local cyrus user. this mailer can make use of the "user+detail@local.host" syntax; it will deliver the mail to the user's "detail" mailbox if the mailbox's ACL permits. The cyrusbb mailer delivers to a system-wide cyrus mailbox if the mailbox's ACL permits. The local mailer accepts addresses of the form "user+detail", where the "+detail" is not used for mailbox matching but is available to certain local mail programs (in particular, see FEATURE(local_procmail)). For example, "eric", "eric+sendmail", and "eric+sww" all indicate the same user, but additional arguments , "sendmail", and "sww" may be provided for use in sorting mail. +----------+ | FEATURES | +----------+ Special features can be requested using the "FEATURE" macro. For example, the .mc line: FEATURE(use_cw_file) tells sendmail that you want to have it read an /etc/sendmail.cw file to get values for class $=w. The FEATURE may contain a single optional parameter -- for example: FEATURE(mailertable, dbm /usr/lib/mailertable) Available features are: use_cw_file Read the file /etc/sendmail.cw file to get alternate names for this host. This might be used if you were on a host that MXed for a dynamic set of other hosts. If the set is static, just including the line "Cw ..." is probably superior. The actual filename can be overridden by redefining confCW_FILE. use_ct_file Read the file /etc/sendmail.ct file to get the names of users that will be ``trusted'', that is, able to set their envelope from address using -f without generating a warning message. The actual filename can be overridden by redefining confCT_FILE. redirect Reject all mail addressed to "address.REDIRECT" with a ``551 User not local; please try
'' message. If this is set, you can alias people who have left to their new address with ".REDIRECT" appended. nouucp Don't do anything special with UUCP addresses at all. nocanonify Don't pass addresses to $[ ... $] for canonification. This would generally only be used by sites that only act as mail gateways or which have user agents that do full canonification themselves. You may also want to use "define(`confBIND_OPTS',`-DNSRCH -DEFNAMES')" to turn off the usual resolver options that do a similar thing. stickyhost If set, email sent to "user@local.host" are marked as "sticky" -- that is, the local addresses aren't matched against UDB and don't go through ruleset 5. This is used if you want a set up where "user" is not necessarily the same as "user@local.host", e.g., to make a distinct domain-wide namespace. Prior to 8.7 this was the default, and notsticky was used to turn this off. mailertable Include a "mailer table" which can be used to override routing for particular domains. The argument of the FEATURE may be the key definition. If none is specified, the definition used is: hash -o /etc/mailertable Keys in this database are fully qualified domain names or partial domains preceded by a dot -- for example, "vangogh.CS.Berkeley.EDU" or ".CS.Berkeley.EDU". Values must be of the form: mailer:domain where "mailer" is the internal mailer name, and "domain" is where to send the message. These maps are not reflected into the message header. domaintable Include a "domain table" which can be used to provide domain name mapping. Use of this should really be limited to your own domains. It may be useful if you change names (e.g., your company changes names from oldname.com to newname.com). The argument of the FEATURE may be the key definition. If none is specified, the definition used is: hash -o /etc/domaintable The key in this table is the domain name; the value is the new (fully qualified) domain. Anything in the domaintable is reflected into headers; that is, this is done in ruleset 3. bitdomain Look up bitnet hosts in a table to try to turn them into internet addresses. The table can be built using the bitdomain program contributed by John Gardiner Myers. The argument of the FEATURE may be the key definition; if none is specified, the definition used is: hash -o /etc/bitdomain.db Keys are the bitnet hostname; values are the corresponding internet hostname. uucpdomain Similar feature for UUCP hosts. The default map definition is: hash -o /etc/uudomain.db At the moment there is no automagic tool to build this database. always_add_domain Include the local host domain even on locally delivered mail. Normally it is not added on unqualified names. However, if you use a shared message store but do not use the same user name space everywhere, you may need the host name on local names. allmasquerade If masquerading is enabled (using MASQUERADE_AS), this feature will cause recipient addresses to also masquerade as being from the masquerade host. Normally they get the local hostname. Although this may be right for ordinary users, it can break local aliases. For example, if you send to "localalias", the originating sendmail will find that alias and send to all members, but send the message with "To: localalias@masqueradehost". Since that alias likely does not exist, replies will fail. Use this feature ONLY if you can guarantee that the ENTIRE namespace on your masquerade host supersets all the local entries. limited_masquerade Normally, any hosts listed in $=w are masqueraded. If this feature is given, only the hosts listed in $=M are masqueraded. This is useful if you have several domains with disjoint namespaces hosted on the same machine. masquerade_entire_domain If masquerading is enabled (using MASQUERADE_AS) and MASQUERADE_DOMAIN (see below) is set, this feature will cause addresses to be rewritten such that the masquerading domains are actually entire domains to be hidden. All hosts within the masquerading domains will be rewritten to the masquerade name (used in MASQUERADE_AS). For example, if you have: MASQUERADE_AS(masq.com) MASQUERADE_DOMAIN(foo.org) MASQUERADE_DOMAIN(bar.com) then *foo.org and *bar.com are converted to masq.com. Without this feature, only foo.org and bar.com are masqueraded. NOTE: only domains within your jurisdiction and current hierarchy should be masqueraded using this. genericstable This feature will cause certain addresses originating in the local domain or a domain listed in $=G to be looked up in a map and turned into another ("generic") form, which can change both the domain name and the user name. This is similar to the userdb functionality. The same types of addresses as for masquerading are looked up, i.e. only header sender addresses unless the allmasquerade and/or masquerade_envelope features are given. The addresses must be in the list of names given by the macros GENERICS_DOMAIN or GENERICS_DOMAIN_FILE (analogously to MASQUERADE_DOMAIN and MASQUERADE_DOMAIN_FILE, see below). The argument of FEATURE(genericstable) may be the map defintion; the default map definition is: hash -o /etc/genericstable The key for this table is either the full address or the unqualified username (the former is tried first); the value is the new user address. If the new user address does - not include a domain, $j is used. + not include a domain, $j is used. Note that the address must + being looked up must be fully qualified. For local mail, it + is necessary to use FEATURE(always_add_domain) for the + addresses to be qualified. virtusertable A domain-specific form of aliasing, allowing multiple virtual domains to be hosted on one machine. For example, if the virtuser table contained: info@foo.com foo-info info@bar.com bar-info @baz.org jane@elsewhere.net then mail addressed to info@foo.com will be sent to the address foo-info, mail addressed to info@bar.com will be delivered to bar-info, and mail addressed to anyone at - baz.org will be sent to jane@elsewhere.net. All the host - names on the left hand side (foo.com, bar.com, and baz.org) - must be in $=w. The default map definition is: + baz.org will be sent to jane@elsewhere.net. The username + from the original address is passed as %1 allowing: + @foo.org %1@elsewhere.com + + meaning someone@foo.org will be sent to someone@elsewhere.com. + + All the host names on the left hand side (foo.com, bar.com, + and baz.org) must be in $=w. The default map definition is: + hash -o /etc/virtusertable A new definition can be specified as the second argument of - the FEATURE macro. + the FEATURE macro, such as + FEATURE(virtusertable, dbm -o /etc/mail/virtusers) + nodns We aren't running DNS at our site (for example, we are UUCP-only connected). It's hard to consider this a "feature", but hey, it had to go somewhere. + Actually, as of 8.7 this is a no-op -- remove "dns" from + the hosts service switch entry instead. nullclient This is a special case -- it creates a stripped down configuration file containing nothing but support for forwarding all mail to a central hub via a local SMTP-based network. The argument is the name of that hub. The only other feature that should be used in conjunction with this one is "nocanonify" (this causes addresses to be sent unqualified via the SMTP connection; normally they are qualifed with the masquerade name, which defaults to the name of the hub machine). No mailers should be defined. No aliasing or forwarding is done. local_procmail Use procmail as the local mailer. This mailer can make use of the "user+indicator@local.host" syntax; normally the +indicator is just tossed, but by default it is passed as the -a argument to procmail. The argument to this feature is the pathname of procmail, which defaults to PROCMAIL_MAILER_PATH. Note that this does NOT use PROCMAIL_MAILER_FLAGS or PROCMAIL_MAILER_ARGS for the local mailer; tweak LOCAL_MAILER_FLAGS and LOCAL_MAILER_ARGS instead. bestmx_is_local Accept mail as though locally addressed for any host that lists us as the best possible MX record. This generates additional DNS traffic, but should be OK for low to medium traffic hosts. THIS FEATURE IS FUNDAMENTALLY INCOMPATIBLE WITH WILDCARD MX RECORDS!!! If you have a wildcard MX record that matches your domain, you cannot use this feature. smrsh Use the SendMail Restricted SHell (smrsh) provided with the distribution instead of /bin/sh for mailing to programs. This improves the ability of the local system administrator to control what gets run via e-mail. If an argument is provided it is used as the pathname to smrsh; otherwise, /usr/local/etc/smrsh is assumed. +-------+ | HACKS | +-------+ Some things just can't be called features. To make this clear, they go in the hack subdirectory and are referenced using the HACK macro. These will tend to be site-dependent. The release includes the Berkeley-dependent "cssubdomain" hack (that makes sendmail accept local names in either Berkeley.EDU or CS.Berkeley.EDU; this is intended as a short-term aid while we move hosts into subdomains. +--------------------+ | SITE CONFIGURATION | +--------------------+ ***************************************************** * This section is really obsolete, and is preserved * * only for back compatibility. You should plan on * * using mailertables for new installations. In * * particular, it doesn't work for the newer forms * * of UUCP mailers, such as uucp-uudom. * ***************************************************** Complex sites will need more local configuration information, such as lists of UUCP hosts they speak with directly. This can get a bit more tricky. For an example of a "complex" site, see cf/ucbvax.mc. If your host is known by several different names, you need to augment the $=w class. This is a list of names by which you are known, and anything sent to an address using a host name in this list will be treated as local mail. You can do this in two ways: either create the file /etc/sendmail.cw containing a list of your aliases (one per line), and use ``FEATURE(use_cw_file)'' in the .mc file, or add the line: Cw alias.host.name at the end of that file. See the ``vangogh.mc'' file for an example. Be sure you use the fully-qualified name of the host, rather than a short name. The SITECONFIG macro allows you to indirectly reference site-dependent configuration information stored in the siteconfig subdirectory. For example, the line SITECONFIG(uucp.ucbvax, ucbvax, U) reads the file uucp.ucbvax for local connection information. The second parameter is the local name (in this case just "ucbvax" since it is locally connected, and hence a UUCP hostname). The third parameter is the name of both a macro to store the local name (in this case, $U) and the name of the class (e.g., $=U) in which to store the host information read from the file. Another SITECONFIG line reads SITECONFIG(uucp.ucbarpa, ucbarpa.Berkeley.EDU, W) This says that the file uucp.ucbarpa contains the list of UUCP sites connected to ucbarpa.Berkeley.EDU. The $=W class will be used to store this list, and $W is defined to be ucbarpa.Berkeley.EDU, that is, the name of the relay to which the hosts listed in uucp.ucbarpa are connected. [The machine ucbarpa is gone now, but I've left this out-of-date configuration file around to demonstrate how you might do this.] Note that the case of SITECONFIG with a third parameter of ``U'' is special; the second parameter is assumed to be the UUCP name of the local site, rather than the name of a remote site, and the UUCP name is entered into $=w (the list of local hostnames) as $U.UUCP. The siteconfig file (e.g., siteconfig/uucp.ucbvax.m4) contains nothing more than a sequence of SITE macros describing connectivity. For example: SITE(cnmat) SITE(sgi olympus) The second example demonstrates that you can use two names on the same line; these are usually aliases for the same host (or are at least in the same company). +--------------------+ | USING UUCP MAILERS | +--------------------+ It's hard to get UUCP mailers right because of the extremely ad hoc nature of UUCP addressing. These config files are really designed for domain-based addressing, even for UUCP sites. There are four UUCP mailers available. The choice of which one to use is partly a matter of local preferences and what is running at the other end of your UUCP connection. Unlike good protocols that define what will go over the wire, UUCP uses the policy that you should do what is right for the other end; if they change, you have to change. This makes it hard to do the right thing, and discourages people from updating their software. In general, if you can avoid UUCP, please do. The major choice is whether to go for a domainized scheme or a non-domainized scheme. This depends entirely on what the other end will recognize. If at all possible, you should encourage the other end to go to a domain-based system -- non-domainized addresses don't work entirely properly. The four mailers are: uucp-old (obsolete name: "uucp") This is the oldest, the worst (but the closest to UUCP) way of sending messages accros UUCP connections. It does bangify everything and prepends $U (your UUCP name) to the sender's address (which can already be a bang path itself). It can only send to one address at a time, so it spends a lot of time copying duplicates of messages. Avoid this if at all possible. uucp-new (obsolete name: "suucp") The same as above, except that it assumes that in one rmail command you can specify several recipients. It still has a lot of other problems. uucp-dom This UUCP mailer keeps everything as domain addresses. Basically, it uses the SMTP mailer rewriting rules. This mailer is only included if MAILER(smtp) is also specified. Unfortunately, a lot of UUCP mailer transport agents require bangified addresses in the envelope, although you can use domain-based addresses in the message header. (The envelope shows up as the From_ line on UNIX mail.) So.... uucp-uudom This is a cross between uucp-new (for the envelope addresses) and uucp-dom (for the header addresses). It bangifies the envelope sender (From_ line in messages) without adding the local hostname, unless there is no host name on the address at all (e.g., "wolf") or the host component is a UUCP host name instead of a domain name ("somehost!wolf" instead of "some.dom.ain!wolf"). This is also included only if MAILER(smtp) is also specified. Examples: We are on host grasp.insa-lyon.fr (UUCP host name "grasp"). The following summarizes the sender rewriting for various mailers. Mailer sender rewriting in the envelope ------ ------ ------------------------- uucp-{old,new} wolf grasp!wolf uucp-dom wolf wolf@grasp.insa-lyon.fr uucp-uudom wolf grasp.insa-lyon.fr!wolf uucp-{old,new} wolf@fr.net grasp!fr.net!wolf uucp-dom wolf@fr.net wolf@fr.net uucp-uudom wolf@fr.net fr.net!wolf uucp-{old,new} somehost!wolf grasp!somehost!wolf uucp-dom somehost!wolf somehost!wolf@grasp.insa-lyon.fr uucp-uudom somehost!wolf grasp.insa-lyon.fr!somehost!wolf If you are using one of the domainized UUCP mailers, you really want to convert all UUCP addresses to domain format -- otherwise, it will do it for you (and probably not the way you expected). For example, if you have the address foo!bar!baz (and you are not sending to foo), the heuristics will add the @uucp.relay.name or @local.host.name to this address. However, if you map foo to foo.host.name first, it will not add the local hostname. You can do this using the uucpdomain feature. +-------------------+ | TWEAKING RULESETS | +-------------------+ For more complex configurations, you can define special rules. The macro LOCAL_RULE_3 introduces rules that are used in canonicalizing the names. Any modifications made here are reflected in the header. A common use is to convert old UUCP addreses to SMTP addresses using the UUCPSMTP macro. For example: LOCAL_RULE_3 UUCPSMTP(decvax, decvax.dec.com) UUCPSMTP(research, research.att.com) will cause addresses of the form "decvax!user" and "research!user" to be converted to "user@decvax.dec.com" and "user@research.att.com" respectively. This could also be used to look up hosts in a database map: LOCAL_RULE_3 R$* < @ $+ > $* $: $1 < @ $(hostmap $2 $) > $3 This map would be defined in the LOCAL_CONFIG portion, as shown below. Similarly, LOCAL_RULE_0 can be used to introduce new parsing rules. For example, new rules are needed to parse hostnames that you accept via MX records. For example, you might have: LOCAL_RULE_0 R$+ <@ host.dom.ain.> $#uucp $@ cnmat $: $1 < @ host.dom.ain.> You would use this if you had installed an MX record for cnmat.Berkeley.EDU pointing at this host; this rule catches the message and forwards it on using UUCP. You can also tweak rulesets 1 and 2 using LOCAL_RULE_1 and LOCAL_RULE_2. These rulesets are normally empty. A similar macro is LOCAL_CONFIG. This introduces lines added after the boilerplate option setting but before rulesets, and can be used to declare local database maps or whatever. For example: LOCAL_CONFIG Khostmap hash /etc/hostmap.db Kyplocal nis -m hosts.byname +---------------------------+ | MASQUERADING AND RELAYING | +---------------------------+ You can have your host masquerade as another using MASQUERADE_AS(host.domain) This causes mail being sent to be labeled as coming from the indicated host.domain, rather than $j. One normally masquerades as one of one's own subdomains (for example, it's unlikely that I would choose to masquerade as an MIT site). This behaviour is modified by a plethora of FEATUREs; in particular, see masquerade_envelope, allmasquerade, limited_masquerade, and masquerade_entire_domain. The masquerade name is not normally canonified, so it is important that it be your One True Name, that is, fully qualified and not a CNAME. However, if you use a CNAME, the receiving side may canonify it for you, so don't think you can cheat CNAME mapping this way. Normally the only addresses that are masqueraded are those that come from this host (that is, are either unqualified or in $=w, the list of local domain names). You can augment this list using MASQUERADE_DOMAIN(otherhost.domain) The effect of this is that although mail to user@otherhost.domain will not be delivered locally, any mail including any user@otherhost.domain will, when relayed, be rewritten to have the MASQUERADE_AS address. This can be a space-separated list of names. If these names are in a file, you can use MASQUERADE_DOMAIN_FILE(filename) to read the list of names from the indicated file. Normally only header addresses are masqueraded. If you want to masquerade the envelope as well, use FEATURE(masquerade_envelope) There are always users that need to be "exposed" -- that is, their internal site name should be displayed instead of the masquerade name. Root is an example. You can add users to this list using EXPOSED_USER(usernames) This adds users to class E; you could also use something like FE/etc/sendmail.cE You can also arrange to relay all unqualified names (that is, names without @host) to a relay host. For example, if you have a central email server, you might relay to that host so that users don't have to have .forward files or aliases. You can do this using define(`LOCAL_RELAY', mailer:hostname) The ``mailer:'' can be omitted, in which case the mailer defaults to "relay". There are some user names that you don't want relayed, perhaps because of local aliases. A common example is root, which may be locally aliased. You can add entries to this list using LOCAL_USER(usernames) This adds users to class L; you could also use something like FL/etc/sendmail.cL If you want all incoming mail sent to a centralized hub, as for a shared /var/spool/mail scheme, use define(`MAIL_HUB', mailer:hostname) Again, ``mailer:'' defaults to "relay". If you define both LOCAL_RELAY and MAIL_HUB _AND_ you have FEATURE(stickyhost), unqualified names will be sent to the LOCAL_RELAY and other local names will be sent to MAIL_HUB. Names in $=L will be delivered locally, so you MUST have aliases or .forward files for them. For example, if you are on machine mastodon.CS.Berkeley.EDU and you have FEATURE(stickyhost), the following combinations of settings will have the indicated effects: email sent to.... eric eric@mastodon.CS.Berkeley.EDU LOCAL_RELAY set to mail.CS.Berkeley.EDU (delivered locally) mail.CS.Berkeley.EDU (no local aliasing) (aliasing done) MAIL_HUB set to mammoth.CS.Berkeley.EDU mammoth.CS.Berkeley.EDU mammoth.CS.Berkeley.EDU (aliasing done) (aliasing done) Both LOCAL_RELAY and mail.CS.Berkeley.EDU mammoth.CS.Berkeley.EDU MAIL_HUB set as above (no local aliasing) (aliasing done) If you do not have FEATURE(stickyhost) set, then LOCAL_RELAY and MAIL_HUB act identically, with MAIL_HUB taking precedence. If you want all outgoing mail to go to a central relay site, define SMART_HOST as well. Briefly: LOCAL_RELAY applies to unqualifed names (e.g., "eric"). MAIL_HUB applies to names qualified with the name of the local host (e.g., "eric@mastodon.CS.Berkeley.EDU"). SMART_HOST applies to names qualified with other hosts. However, beware that other relays (e.g., UUCP_RELAY, BITNET_RELAY, DECNET_RELAY, and FAX_RELAY) take precedence over SMART_HOST, so if you really want absolutely everything to go to a single central site you will need to unset all the other relays -- or better yet, find or build a minimal config file that does this. For duplicate suppression to work properly, the host name is best specified with a terminal dot: define(`MAIL_HUB', `host.domain.') note the trailing dot ---^ ++--------------------------------+ +| ADDING NEW MAILERS OR RULESETS | ++--------------------------------+ + +Sometimes you may need to add entirely new mailers or rulesets. They +should be introduced with the constructs MAILER_DEFINITIONS and +LOCAL_RULESETS respectively. For example: + + MAILER_DEFINITIONS + Mmymailer, ... + ... + + LOCAL_RULESETS + Scheck_relay + ... + + +-------------------------------+ | NON-SMTP BASED CONFIGURATIONS | +-------------------------------+ These configuration files are designed primarily for use by SMTP-based sites. I don't pretend that they are well tuned for UUCP-only or UUCP-primarily nodes (the latter is defined as a small local net connected to the rest of the world via UUCP). However, there is one hook to handle some special cases. You can define a ``smart host'' that understands a richer address syntax using: define(`SMART_HOST', mailer:hostname) In this case, the ``mailer:'' defaults to "relay". Any messages that can't be handled using the usual UUCP rules are passed to this host. If you are on a local SMTP-based net that connects to the outside world via UUCP, you can use LOCAL_NET_CONFIG to add appropriate rules. For example: define(`SMART_HOST', suucp:uunet) LOCAL_NET_CONFIG R$* < @ $* .$m. > $* $#smtp $@ $2.$m. $: $1 < @ $2.$m. > $3 This will cause all names that end in your domain name ($m) via SMTP; anything else will be sent via suucp (smart UUCP) to uunet. If you have FEATURE(nocanonify), you may need to omit the dots after the $m. If you are running a local DNS inside your domain which is not otherwise connected to the outside world, you probably want to use: define(`SMART_HOST', smtp:fire.wall.com) LOCAL_NET_CONFIG R$* < @ $* . > $* $#smtp $@ $2. $: $1 < @ $2. > $3 That is, send directly only to things you found in your DNS lookup; anything else goes through SMART_HOST. -If you are not running DNS at all, it is important to use -FEATURE(nodns) to avoid having sendmail queue everything waiting -for the name server to come up. - +-----------+ | WHO AM I? | +-----------+ Normally, the $j macro is automatically defined to be your fully qualified domain name (FQDN). Sendmail does this by getting your host name using gethostname and then calling gethostbyname on the result. For example, in some environments gethostname returns only the root of the host name (such as "foo"); gethostbyname is supposed to return the FQDN ("foo.bar.com"). In some (fairly rare) cases, gethostbyname may fail to return the FQDN. In this case you MUST define confDOMAIN_NAME to be your fully qualified domain name. This is usually done using: Dmbar.com define(`confDOMAIN_NAME', `$w.$m')dnl +--------------------+ | USING MAILERTABLES | +--------------------+ To use FEATURE(mailertable), you will have to create an external database containing the routing information for various domains. For example, a mailertable file in text format might be: .my.domain xnet:%1.my.domain uuhost1.my.domain suucp:uuhost1 .bitnet smtp:relay.bit.net This should normally be stored in /etc/mailertable. The actual database version of the mailertable is built using: makemap hash /etc/mailertable.db < /etc/mailertable The semantics are simple. Any LHS entry that does not begin with a dot matches the full host name indicated. LHS entries beginning with a dot match anything ending with that domain name -- that is, they can be thought of as having a leading "*" wildcard. Matching is done in order of most-to-least qualified -- for example, even though ".my.domain" is listed first in the above example, an entry of "uuhost1.my.domain" will match the second entry since it is more explicit. The RHS should always be a "mailer:host" pair. The mailer is the configuration name of a mailer (that is, an `M' line in the sendmail.cf file). The "host" will be the hostname passed to that mailer. In domain-based matches (that is, those with leading dots) the "%1" may be used to interpolate the wildcarded part of the host name. For example, the first line above sends everything addressed to "anything.my.domain" to that same host name, but using the (presumably experimental) xnet mailer. In some cases you may want to temporarily turn off MX records, particularly on gateways. For example, you may want to MX everything in a domain to one machine that then forwards it directly. To do this, you might use the DNS configuration: *.domain. IN MX 0 relay.machine and on relay.machine use the mailertable: .domain smtp:[gateway.domain] The [square brackets] turn off MX records for this host only. If you didn't do this, the mailertable would use the MX record again, which would give you an MX loop. +--------------------------------+ | USING USERDB TO MAP FULL NAMES | +--------------------------------+ The user database was not originally intended for mapping full names to login names (e.g., Eric.Allman => eric), but some people are using it that way. (I would recommend that you set up aliases for this purpose instead -- since you can specify multiple alias files, this is fairly easy.) The intent was to locate the default maildrop at a site, but allow you to override this by sending to a specific host. If you decide to set up the user database in this fashion, it is imperative that you not use FEATURE(stickyhost) -- otherwise, e-mail sent to Full.Name@local.host.name will be rejected. To build the internal form of the user database, use: makemap btree /usr/data/base.db < /usr/data/base.txt As a general rule, I am adamantly opposed to using full names as e-mail addresses, since they are not in any sense unique. For example, the Unix software-development community has two Andy Tannenbaums, at least two well-known Peter Deutsches, and at one time Bell Labs had two Stephen R. Bournes with offices along the same hallway. Which one will be forced to suffer the indignity of being Stephen_R_Bourne_2? The less famous of the two, or the one that was hired later? Finger should handle full names (and be fuzzy). Mail should use handles, and not be fuzzy. [Not that I expect anyone to pay any attention to my opinions.] +--------------------------------+ | MISCELLANEOUS SPECIAL FEATURES | +--------------------------------+ Plussed users Sometimes it is convenient to merge configuration on a centralized mail machine, for example, to forward all root mail to a mail server. In this case it might be useful to be able to treat the root addresses as a class of addresses with subtle differences. You can do this using plussed users. For example, a client might include the alias: root: root+client1@server On the server, this will match an alias for "root+client1". If that is not found, the alias "root+*" will be tried, then "root". LDAP For notes on use LDAP in sendmail, see http://www-leland.stanford.edu/~bbense/Inst.html +----------------+ | SECURITY NOTES | +----------------+ A lot of sendmail security comes down to you. Sendmail 8 is much more careful about checking for security problems than previous versions, but there are some things that you still need to watch for. In particular: * Make sure the aliases file isn't writable except by trusted system personnel. This includes both the text and database version. * Make sure that other files that sendmail reads, such as the mailertable, are only writable by trusted system personnel. * The queue directory should not be world writable PARTICULARLY if your system allows "file giveaways" (that is, if a non-root user can chown any file they own to any other user). * If your system allows file giveaways, DO NOT create a publically writable directory for forward files. This will allow anyone to steal anyone else's e-mail. Instead, create a script that copies the .forward file from users' home directories once a night (if you want the non-NFS-mounted forward directory). * If your system allows file giveaways, you'll find that sendmail is much less trusting of :include: files -- in particular, you'll have to have /SENDMAIL/ANY/SHELL/ in /etc/shells before they will be trusted (that is, before files and programs listed in them will be honored). In general, file giveaways are a mistake -- if you can turn them off I recommend you do so. +------------------+ | FlexFAX SOFTWARE | +------------------+ Sam Leffler's FlexFAX software is still in beta test -- but he expects a public version out "later this week" [as of 3/1/93]. The following blurb is direct from Sam: $Header: /usr/people/sam/fax/RCS/HOWTO,v 1.14 93/05/24 11:42:16 sam Exp $ How To Obtain This Software (in case all you get is this file) -------------------------------------------------------------- The source code is available for public ftp on sgi.com sgi/fax/v2.1.src.tar.Z (192.48.153.1) You can also obtain inst'able images for Silicon Graphics machines from sgi.com sgi/fax/v2.1.inst.tar (192.48.153.1) For example, % ftp -n sgi.com .... ftp> user anonymous ... ftp> cd sgi/fax ftp> binary ftp> get v2.1.src.tar.Z In general, the latest version of the 2.1 release of the software is always available as "v2.1.src.tar.Z" or "v2.1.inst.tar" in the ftp directory. This file is a link to the appropriate released version (so don't waste your time retrieving the linked file as well!) Any files of the form v2.1.*.patch are shell scripts that can be used to patch older versions of the source code. For example, the file v2.1.0.patch would contain patches to update v2.1.0.tar.Z. (Note to beta testers: this is different than the naming conventions used during beta testing.) Patch files only work to go between consecutive versions, so if you are multiple versions behind the latest release, you will need to apply each patch file between your current version and the latest. Obtaining the Software by Electronic Mail ----------------------------------------- Do not send me requests for the software; they will be ignored (without response). If you cannot use FTP at all, there is a service called "ftpmail" available from gatekeeper.dec.com: you can send e-mail to this machine and it will use FTP to retrieve files for you and send you the files back again via e-mail. To find out more about the ftpmail service, send a message to "ftpmail@gatekeeper.dec.com" whose body consists of the single line "help". Obtaining the Software Within Silicon Graphics ---------------------------------------------- Internal to Silicon Graphics there are inst'able images on the host flake.asd in the directory /usr/dist. Thus you can do something like: % inst -f flake.asd.sgi.com:/usr/dist/flexfax to install the latest version of the software on your machine. What to do Once You've Retrieved Stuff -------------------------------------- The external distributions come in a compressed or uncompressed tar file. To extract the source distribution: % zcat v2.1.src.tar.Z | tar xf - (uncompress and extract individual files in current directory). To unpack and install the client portion of the inst'able distribution: % mkdir dist % cd dist; tar xf ../v2.1.inst.tar; cd .. % inst -f dist/flexfax ... inst> go (Note, the dist subdirectory is because some versions of inst fail if the files are in the current directory.) Server binaries are also included in the inst'able images as flexfax.server.*. They are not installed by default, so to get them also you need to do: % inst -f flexfax ... inst> install flexfax.server.* inst> go The SGI binaries were built for Version 4.0.5H of the IRIX operating system. They should work w/o problem on earlier versions of the system, but I have not fully tested this. Also, note that to install a server on an SGI machine, you need to have installed the Display PostScript execution environment product (dps_eoe). Otherwise, the fax server will not be able to convert PostScript to facsimile for transmission. If you are working from the source distribution, look at the file README in the top of the source tree. If you are working from the inst images, the subsystem flexfax.man.readme contains the README file and other useful pieces of information--the installed files are placed in the directory /usr/local/doc/flexfax). Basically you will need to run the faxaddmodem script to setup and configure your fax modem. Consult the README file and the manual page for faxaddmodem for information. FlexFAX Mail List ----------------- A mailing list for users of this software is located on sgi.com. If you want to join this mailing list or have a list-related request such as getting your name removed from it, send a request to majordomo@whizzer.wpd.sgi.com For example, to subscribe, send the line "subscribe flexfax" in the body of your message. The line "help" will return a list of the commands understood by the mailing list management software. Submissions (including bug reports) should be directed to: flexfax@sgi.com When corresponding about this software please always specify what version you have, what system you're running on, and, if the problem is specific to your modem, identify the modem and firmware revision. +--------------------------------+ | TWEAKING CONFIGURATION OPTIONS | +--------------------------------+ There are a large number of configuration options that don't normally need to be changed. However, if you feel you need to tweak them, you can define the following M4 variables. This list is shown in four columns: the name you define, the default value for that definition, the option or macro that is affected (either Ox for an option or Dx for a macro), and a brief description. Greater detail of the semantics can be found in the Installation and Operations Guide. Some options are likely to be deprecated in future versions -- that is, the option is only included to provide back-compatibility. These are marked with "*". Remember that these options are M4 variables, and hence may need to be quoted. In particular, arguments with commas will usually have to be ``double quoted, like this phrase'' to avoid having the comma confuse things. This is common for alias file definitions and for the read timeout. M4 Variable Name Configuration Description & [Default] ================ ============= ======================= confMAILER_NAME $n macro [MAILER-DAEMON] The sender name used for internally generated outgoing messages. confDOMAIN_NAME $j macro If defined, sets $j. This should only be done if your system cannot determine your local domain name, and then it should be set to $w.Foo.COM, where Foo.COM is your domain name. confCF_VERSION $Z macro If defined, this is appended to the configuration version name. confFROM_HEADER From: [$?x$x <$g>$|$g$.] The format of an internally generated From: address. confRECEIVED_HEADER Received: [$?sfrom $s .$?_($?s$|from $.$_) $.by $j ($v/$Z)$?r with $r$. id $i$?u for $u$.; $b] The format of the Received: header in messages passed through this host. It is unwise to try to change this. confCW_FILE Fw class [/etc/sendmail.cw] Name of file used to get the local additions to the $=w (local host names) class. confCT_FILE Ft class [/etc/sendmail.ct] Name of file used to get the local additions to the $=t (trusted users) class. confTRUSTED_USERS Ct class [no default] Names of users to add to the list of trusted users. This list always includes root, uucp, and daemon. See also FEATURE(use_ct_file). confSMTP_MAILER - [esmtp] The mailer name used when SMTP connectivity is required. One of "smtp", "smtp8", or "esmtp". confUUCP_MAILER - [uucp-old] The mailer to be used by default for bang-format recipient addresses. See also discussion of $=U, $=Y, and $=Z in the MAILER(uucp) section. confLOCAL_MAILER - [local] The mailer name used when local connectivity is required. Almost always "local". confRELAY_MAILER - [relay] The default mailer name used for relaying any mail (e.g., to a BITNET_RELAY, a SMART_HOST, or whatever). This can reasonably be "uucp-new" if you are on a UUCP-connected site. confSEVEN_BIT_INPUT SevenBitInput [False] Force input to seven bits? confEIGHT_BIT_HANDLING EightBitMode [pass8] 8-bit data handling confALIAS_WAIT AliasWait [10m] Time to wait for alias file rebuild until you get bored and decide that the apparently pending rebuild failed. confMIN_FREE_BLOCKS MinFreeBlocks [100] Minimum number of free blocks on queue filesystem to accept SMTP mail. (Prior to 8.7 this was minfree/maxsize, where minfree was the number of free blocks and maxsize was the maximum message size. Use confMAX_MESSAGE_SIZE for the second value now.) confMAX_MESSAGE_SIZE MaxMessageSize [infinite] The maximum size of messages that will be accepted (in bytes). confBLANK_SUB BlankSub [.] Blank (space) substitution character. confCON_EXPENSIVE HoldExpensive [False] Avoid connecting immediately to mailers marked expensive? confCHECKPOINT_INTERVAL CheckpointInterval [10] Checkpoint queue files every N recipients. confDELIVERY_MODE DeliveryMode [background] Default delivery mode. confAUTO_REBUILD AutoRebuildAliases [False] Automatically rebuild alias file if needed. confERROR_MODE ErrorMode [print] Error message mode. confERROR_MESSAGE ErrorHeader [undefined] Error message header/file. confSAVE_FROM_LINES SafeFromLine Save extra leading From_ lines. confTEMP_FILE_MODE TempFileMode [0600] Temporary file mode. confMATCH_GECOS MatchGECOS [True] Match GECOS field. confMAX_HOP MaxHopCount [25] Maximum hop count. confIGNORE_DOTS* IgnoreDots [False; always False in -bs or -bd mode] Ignore dot as terminator for incoming messages? confBIND_OPTS ResolverOptions [undefined] Default options for DNS resolver. confMIME_FORMAT_ERRORS* SendMimeErrors [True] Send error messages as MIME- encapsulated messages per RFC 1344. confFORWARD_PATH ForwardPath [$z/.forward.$w:$z/.forward] The colon-separated list of places to search for .forward files. N.B.: see the Security Notes section. confMCI_CACHE_SIZE ConnectionCacheSize [2] Size of open connection cache. confMCI_CACHE_TIMEOUT ConnectionCacheTimeout [5m] Open connection cache timeout. confHOST_STATUS_DIRECTORY HostStatusDirectory [undefined] If set, host status is kept on disk between sendmail runs in the named directory tree. This need not be a full pathname, in which case it is interpreted relative to the queue - directory. This option also - single-threads connections to each - host, i.e., prevents multiple - connections to a single server from - this client. + directory. +confSINGLE_THREAD_DELIVERY SingleThreadDelivery + [False] If this option and the + HostStatusDirectory option are both + set, single thread deliveries to other + hosts. That is, don't allow any two + sendmails on this host to connect + simultaneously to any other single + host. This can slow down delivery in + some cases, in particular since a + cached but otherwise idle connection + to a host will prevent other sendmails + from connecting to the other host. confUSE_ERRORS_TO* UserErrorsTo [False] Use the Errors-To: header to deliver error messages. This should not be necessary because of general acceptance of the envelope/header distinction. confLOG_LEVEL LogLevel [9] Log level. confME_TOO MeToo [False] Include sender in group expansions. confCHECK_ALIASES CheckAliases [False] Check RHS of aliases when running newaliases. Since this does DNS lookups on every address, it can slow down the alias rebuild process considerably on large alias files. confOLD_STYLE_HEADERS* OldStyleHeaders [True] Assume that headers without special chars are old style. confDAEMON_OPTIONS DaemonPortOptions [none] SMTP daemon options. confPRIVACY_FLAGS PrivacyOptions [authwarnings] Privacy flags. confCOPY_ERRORS_TO PostmasterCopy [undefined] Address for additional copies of all error messages. confQUEUE_FACTOR QueueFactor [600000] Slope of queue-only function. confDONT_PRUNE_ROUTES DontPruneRoutes [False] Don't prune down route-addr syntax addresses to the minimum possible. confSAFE_QUEUE* SuperSafe [True] Commit all messages to disk before forking. confTO_INITIAL Timeout.initial [5m] The timeout waiting for a response on the initial connect. confTO_CONNECT Timeout.connect [0] The timeout waiting for an initial connect() to complete. This can only shorten connection timeouts; the kernel silently enforces an absolute maximum (which varies depending on the system). confTO_ICONNECT Timeout.iconnect [undefined] Like Timeout.connect, but applies only to the very first attempt to connect to a host in a message. This allows a single very fast pass followed by more careful delivery attempts in the future. confTO_HELO Timeout.helo [5m] The timeout waiting for a response to a HELO or EHLO command. confTO_MAIL Timeout.mail [10m] The timeout waiting for a response to the MAIL command. confTO_RCPT Timeout.rcpt [1h] The timeout waiting for a response to the RCPT command. confTO_DATAINIT Timeout.datainit [5m] The timeout waiting for a 354 response from the DATA command. confTO_DATABLOCK Timeout.datablock [1h] The timeout waiting for a block during DATA phase. confTO_DATAFINAL Timeout.datafinal [1h] The timeout waiting for a response to the final "." that terminates a message. confTO_RSET Timeout.rset [5m] The timeout waiting for a response to the RSET command. confTO_QUIT Timeout.quit [2m] The timeout waiting for a response to the QUIT command. confTO_MISC Timeout.misc [2m] The timeout waiting for a response to other SMTP commands. confTO_COMMAND Timeout.command [1h] In server SMTP, the timeout waiting for a command to be issued. confTO_IDENT Timeout.ident [30s] The timeout waiting for a response to an IDENT query. confTO_FILEOPEN Timeout.fileopen [60s] The timeout waiting for a file (e.g., :include: file) to be opened. confTO_QUEUERETURN Timeout.queuereturn [5d] The timeout before a message is returned as undeliverable. confTO_QUEUERETURN_NORMAL Timeout.queuereturn.normal [undefined] As above, for normal priority messages. confTO_QUEUERETURN_URGENT Timeout.queuereturn.urgent [undefined] As above, for urgent priority messages. confTO_QUEUERETURN_NONURGENT Timeout.queuereturn.non-urgent [undefined] As above, for non-urgent (low) priority messages. confTO_QUEUEWARN Timeout.queuewarn [4h] The timeout before a warning message is sent to the sender telling them that the message has been deferred. confTO_QUEUEWARN_NORMAL Timeout.queuewarn.normal [undefined] As above, for normal priority messages. confTO_QUEUEWARN_URGENT Timeout.queuewarn.urgent [undefined] As above, for urgent priority messages. confTO_QUEUEWARN_NONURGENT Timeout.queuewarn.non-urgent [undefined] As above, for non-urgent (low) priority messages. confTO_HOSTSTATUS Timeout.hoststatus [30m] How long information about host statuses will be maintained before it is considered stale and the host should be retried. This applies both within a single queue run and to persistent information (see below). confTIME_ZONE TimeZoneSpec [USE_SYSTEM] Time zone info -- can be USE_SYSTEM to use the system's idea, USE_TZ to use the user's TZ envariable, or something else to force that value. confDEF_USER_ID DefaultUser [1:1] Default user id. confUSERDB_SPEC UserDatabaseSpec [undefined] User database specification. confFALLBACK_MX FallbackMXhost [undefined] Fallback MX host. confTRY_NULL_MX_LIST TryNullMXList [False] If we are the best MX for a host and haven't made other arrangements, try connecting to the host directly; normally this would be a config error. confQUEUE_LA QueueLA [8] Load average at which queue-only function kicks in. confREFUSE_LA RefuseLA [12] Load average at which incoming SMTP connections are refused. confMAX_DAEMON_CHILDREN MaxDaemonChildren [undefined] The maximum number of children the daemon will permit. After this number, connections will be rejected. If not set or <= 0, there is no limit. confCONNECTION_RATE_THROTTLE ConnectionRateThrottle [undefined] The maximum number of connections permitted per second. After this many connections are accepted, further connections will be delayed. If not set or <= 0, there is no limit. confWORK_RECIPIENT_FACTOR RecipientFactor [30000] Cost of each recipient. confSEPARATE_PROC ForkEachJob [False] Run all deliveries in a separate process. confWORK_CLASS_FACTOR ClassFactor [1800] Priority multiplier for class. confWORK_TIME_FACTOR RetryFactor [90000] Cost of each delivery attempt. confQUEUE_SORT_ORDER QueueSortOrder [Priority] Queue sort algorithm: - Priority or Host. + Priority, Host, or Time. confMIN_QUEUE_AGE MinQueueAge [0] The minimum amount of time a job must sit in the queue between queue runs. This allows you to set the queue run interval low for better resposiveness without trying all jobs in each run. confDEF_CHAR_SET DefaultCharSet [unknown-8bit] When converting unlabelled 8 bit input to MIME, the character set to use by default. confSERVICE_SWITCH_FILE ServiceSwitchFile [/etc/service.switch] The file to use for the service switch on systems that do not have a system-defined switch. confHOSTS_FILE HostsFile [/etc/hosts] The file to use when doing "file" type access of hosts names. confDIAL_DELAY DialDelay [0s] If a connection fails, wait this long and try again. Zero means "don't retry". This is to allow "dial on demand" connections to have enough time to complete a connection. confNO_RCPT_ACTION NoRecipientAction [none] What to do if there are no legal recipient fields (To:, Cc: or Bcc:) in the message. Legal values can be "none" to just leave the nonconforming message as is, "add-to" to add a To: header with all the known recipients (which may expose blind recipients), "add-apparently-to" to do the same but use Apparently-To: instead of To:, "add-bcc" to add an empty Bcc: header, or "add-to-undisclosed" to add the header ``To: undisclosed-recipients:;''. confSAFE_FILE_ENV SafeFileEnvironment [undefined] If set, sendmail will do a chroot() into this directory before writing files. confCOLON_OK_IN_ADDR ColonOkInAddr [True unless Configuration Level > 6] If set, colons are treated as a regular character in addresses. If not set, they are treated as the introducer to the RFC 822 "group" syntax. Colons are handled properly in route-addrs. This option defaults on for V5 and lower configuration files. confMAX_QUEUE_RUN_SIZE MaxQueueRunSize [0] If set, limit the maximum size of any given queue run to this number of entries. Essentially, this will stop reading the queue directory after this number of entries are reached; it does _not_ pick the highest priority jobs, so this should be as large as your system can tolerate. If not set, there is no limit. confDONT_EXPAND_CNAMES DontExpandCnames [False] If set, $[ ... $] lookups that do DNS based lookups do not expand CNAME records. This currently violates the published standards, but the IETF seems to be moving toward legalizing this. For example, if "FTP.Foo.ORG" is a CNAME for "Cruft.Foo.ORG", then with this option set a lookup of "FTP" will return "FTP.Foo.ORG"; if clear it returns "Cruft.FOO.ORG". N.B. you may not see any effect until your downstream neighbors stop doing CNAME lookups as well. confFROM_LINE UnixFromLine [From $g $d] The From_ line used when sending to files or programs. confOPERATORS OperatorChars [.:%@!^/[]+] Address operator characters. confSMTP_LOGIN_MSG SmtpGreetingMessage [$j Sendmail $v/$Z; $b] The initial (spontaneous) SMTP greeting message. The word "ESMTP" will be inserted between the first and second words to convince other sendmails to try to speak ESMTP. confDONT_INIT_GROUPS DontInitGroups [False] If set, the initgroups(3) routine will never be invoked. You might want to do this if you are running NIS and you have a large group map, since this call does a sequential scan of the map; in a large site this can cause your ypserv to run essentially full time. If you set this, agents run on behalf of users will only have their primary (/etc/passwd) group permissions. confUNSAFE_GROUP_WRITES UnsafeGroupWrites [False] If set, group-writable :include: and .forward files are considered "unsafe", that is, programs and files cannot be directly referenced from such files. World-writable files are always considered unsafe. confDOUBLE_BOUNCE_ADDRESS DoubleBounceAddress [postmaster] If an error occurs when sending an error message, send that "double bounce" error message to this address. confRUN_AS_USER RunAsUser [undefined] If set, become this user when reading and delivering mail. Causes all file reads (e.g., .forward and :include: files) to be done as this user. Also, all programs will be run as this user, and all output files will be written as this user. Intended for use only on firewalls where users do not have accounts. -confSINGLE_THREAD_DELIVERY SingleThreadDelivery - [False] If this option and the - HostStatusDirectory option are both - set, single thread deliveries to other - hosts. That is, don't allow any two - sendmails on this host to connect - simultaneously to any other single - host. This can slow down delivery in - some cases, in particular since a - cached but otherwise idle connection - to a host will prevent other sendmails - from connecting to the other host. See also the description of OSTYPE for some parameters that can be tweaked (generally pathnames to mailers). +-----------+ | HIERARCHY | +-----------+ Within this directory are several subdirectories, to wit: m4 General support routines. These are typically very important and should not be changed without very careful consideration. cf The configuration files themselves. They have ".mc" suffixes, and must be run through m4 to become complete. The resulting output should have a ".cf" suffix. ostype Definitions describing a particular operating system type. These should always be referenced using the OSTYPE macro in the .mc file. Examples include "bsd4.3", "bsd4.4", "sunos3.5", and "sunos4.1". domain Definitions describing a particular domain, referenced using the DOMAIN macro in the .mc file. These are site dependent; for example, "CS.Berkeley.EDU.m4" describes hosts in the CS.Berkeley.EDU subdomain. mailer Descriptions of mailers. These are referenced using the MAILER macro in the .mc file. sh Shell files used when building the .cf file from the .mc file in the cf subdirectory. feature These hold special orthogonal features that you might want to include. They should be referenced using the FEATURE macro. hack Local hacks. These can be referenced using the HACK macro. They shouldn't be of more than voyeuristic interest outside the .Berkeley.EDU domain, but who knows? We've all got our own peccadillos. siteconfig Site configuration -- e.g., tables of locally connected UUCP sites. +------------------------+ | ADMINISTRATIVE DETAILS | +------------------------+ The following sections detail usage of certain internal parts of the sendmail.cf file. Read them carefully if you are trying to modify the current model. If you find the above descriptions adequate, these should be {boring, confusing, tedious, ridiculous} (pick one or more). RULESETS (* means built in to sendmail) 0 * Parsing 1 * Sender rewriting 2 * Recipient rewriting 3 * Canonicalization 4 * Post cleanup 5 * Local address rewrite (after aliasing) 1x mailer rules (sender qualification) 2x mailer rules (recipient qualification) 3x mailer rules (sender header qualification) 4x mailer rules (recipient header qualification) 5x mailer subroutines (general) 6x mailer subroutines (general) 7x mailer subroutines (general) 8x reserved 90 Mailertable host stripping 96 Bottom half of Ruleset 3 (ruleset 6 in old sendmail) 97 Hook for recursive ruleset 0 call (ruleset 7 in old sendmail) 98 Local part of ruleset 0 (ruleset 8 in old sendmail) 99 Guaranteed null (for debugging) MAILERS 0 local, prog local and program mailers 1 [e]smtp, relay SMTP channel 2 uucp-* UNIX-to-UNIX Copy Program 3 netnews Network News delivery 4 fax Sam Leffler's FlexFAX software 5 mail11 DECnet mailer MACROS A B Bitnet Relay C DECnet Relay D The local domain -- usually not needed E reserved for X.400 Relay F FAX Relay G H mail Hub (for mail clusters) I J K L Luser Relay M Masquerade (who I claim to be) N O P Q R Relay (for unqualified names) S Smart Host T U my UUCP name (if I have a UUCP connection) V UUCP Relay (class V hosts) W UUCP Relay (class W hosts) X UUCP Relay (class X hosts) Y UUCP Relay (all other hosts) Z Version number CLASSES A B domains that are candidates for bestmx lookup C D E addresses that should not seem to come from $M F hosts we forward for G domains that should be looked up in genericstable H I J K L addresses that should not be forwarded to $R M domains that should be mapped to $M N O operators that indicate network operations (cannot be in local names) P top level pseudo-domains: BITNET, DECNET, FAX, UUCP, etc. Q - R + R domains we are willing to relay (pass anti-spam filters) S T U locally connected UUCP hosts V UUCP hosts connected to relay $V W UUCP hosts connected to relay $W X UUCP hosts connected to relay $X Y locally connected smart UUCP hosts Z locally connected domain-ized UUCP hosts . the class containing only a dot [ the class containing only a left bracket M4 DIVERSIONS 1 Local host detection and resolution 2 Local Ruleset 3 additions 3 Local Ruleset 0 additions 4 UUCP Ruleset 0 additions 5 locally interpreted names (overrides $R) 6 local configuration (at top of file) 7 mailer definitions 8 9 special local rulesets (1 and 2) Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-hpux10.mc =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-hpux10.mc (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-hpux10.mc (revision 26986) @@ -1,52 +1,52 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # # # This is a Berkeley-specific configuration file for HP-UX 9.x. -# It applies only the the Computer Science Division at Berkeley, +# It applies only to the Computer Science Division at Berkeley, # and should not be used elsewhere. It is provided on the sendmail # distribution as a sample only. To create your own configuration # file, create an appropriate domain file in ../domain, change the # `DOMAIN' macro below to reference that file, and copy the result # to a name of your own choosing. # divert(0)dnl -VERSIONID(`@(#)cs-hpux10.mc 8.4 (Berkeley) 3/23/96') +VERSIONID(`@(#)cs-hpux10.mc 8.5 (Berkeley) 6/3/97') OSTYPE(hpux10)dnl DOMAIN(CS.Berkeley.EDU)dnl define(`MAIL_HUB', mailspool.CS.Berkeley.EDU)dnl MAILER(local)dnl MAILER(smtp)dnl Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-hpux9.mc =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-hpux9.mc (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-hpux9.mc (revision 26986) @@ -1,52 +1,52 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # # # This is a Berkeley-specific configuration file for HP-UX 9.x. -# It applies only the the Computer Science Division at Berkeley, +# It applies only to the Computer Science Division at Berkeley, # and should not be used elsewhere. It is provided on the sendmail # distribution as a sample only. To create your own configuration # file, create an appropriate domain file in ../domain, change the # `DOMAIN' macro below to reference that file, and copy the result # to a name of your own choosing. # divert(0)dnl -VERSIONID(`@(#)cs-hpux9.mc 8.5 (Berkeley) 3/23/96') +VERSIONID(`@(#)cs-hpux9.mc 8.6 (Berkeley) 6/3/97') OSTYPE(hpux9)dnl DOMAIN(CS.Berkeley.EDU)dnl define(`MAIL_HUB', mailspool.CS.Berkeley.EDU)dnl MAILER(local)dnl MAILER(smtp)dnl Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-osf1.mc =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-osf1.mc (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-osf1.mc (revision 26986) @@ -1,51 +1,51 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # # # This is a Berkeley-specific configuration file for OSF/1. -# It applies only the the Computer Science Division at Berkeley, +# It applies only to the Computer Science Division at Berkeley, # and should not be used elsewhere. It is provided on the sendmail # distribution as a sample only. To create your own configuration # file, create an appropriate domain file in ../domain, change the # `DOMAIN' macro below to reference that file, and copy the result # to a name of your own choosing. # divert(0)dnl -VERSIONID(`@(#)cs-osf1.mc 8.4 (Berkeley) 3/23/96') +VERSIONID(`@(#)cs-osf1.mc 8.5 (Berkeley) 6/3/97') OSTYPE(osf1)dnl DOMAIN(CS.Berkeley.EDU)dnl MAILER(local)dnl MAILER(smtp)dnl Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-solaris2.mc =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-solaris2.mc (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-solaris2.mc (revision 26986) @@ -1,51 +1,51 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # # # This is a Berkeley-specific configuration file for Solaris 2.x. -# It applies only the the Computer Science Division at Berkeley, +# It applies only to the Computer Science Division at Berkeley, # and should not be used elsewhere. It is provided on the sendmail # distribution as a sample only. To create your own configuration # file, create an appropriate domain file in ../domain, change the # `DOMAIN' macro below to reference that file, and copy the result # to a name of your own choosing. # divert(0)dnl -VERSIONID(`@(#)cs-solaris2.mc 8.3 (Berkeley) 3/23/96') +VERSIONID(`@(#)cs-solaris2.mc 8.4 (Berkeley) 6/3/97') OSTYPE(solaris2)dnl DOMAIN(CS.Berkeley.EDU)dnl MAILER(local)dnl MAILER(smtp)dnl Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-sunos4.1.mc =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-sunos4.1.mc (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-sunos4.1.mc (revision 26986) @@ -1,51 +1,51 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # # # This is a Berkeley-specific configuration file for SunOS 4.1.x. -# It applies only the the Computer Science Division at Berkeley, +# It applies only to the Computer Science Division at Berkeley, # and should not be used elsewhere. It is provided on the sendmail # distribution as a sample only. To create your own configuration # file, create an appropriate domain file in ../domain, change the # `DOMAIN' macro below to reference that file, and copy the result # to a name of your own choosing. # divert(0)dnl -VERSIONID(`@(#)cs-sunos4.1.mc 8.4 (Berkeley) 3/23/96') +VERSIONID(`@(#)cs-sunos4.1.mc 8.5 (Berkeley) 6/3/97') OSTYPE(sunos4.1)dnl DOMAIN(CS.Berkeley.EDU)dnl MAILER(local)dnl MAILER(smtp)dnl Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-ultrix4.mc =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-ultrix4.mc (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/cs-ultrix4.mc (revision 26986) @@ -1,51 +1,51 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # # # This is a Berkeley-specific configuration file for Ultrix 4.x. -# It applies only the the Computer Science Division at Berkeley, +# It applies only to the Computer Science Division at Berkeley, # and should not be used elsewhere. It is provided on the sendmail # distribution as a sample only. To create your own configuration # file, create an appropriate domain file in ../domain, change the # `DOMAIN' macro below to reference that file, and copy the result # to a name of your own choosing. # divert(0)dnl -VERSIONID(`@(#)cs-ultrix4.mc 8.4 (Berkeley) 3/23/96') +VERSIONID(`@(#)cs-ultrix4.mc 8.5 (Berkeley) 6/3/97') OSTYPE(ultrix4)dnl DOMAIN(CS.Berkeley.EDU)dnl MAILER(local)dnl MAILER(smtp)dnl Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/knecht.mc =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/knecht.mc (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/knecht.mc (revision 26986) @@ -1,51 +1,94 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # # # This is specific to Eric's home machine. # divert(0)dnl -VERSIONID(`@(#)knecht.mc 8.4 (Berkeley) 11/24/96') +VERSIONID(`@(#)knecht.mc 8.11 (Berkeley) 6/12/97') OSTYPE(bsd4.4)dnl DOMAIN(generic)dnl define(`confDEF_USER_ID', `mailnull')dnl define(`confHOST_STATUS_DIRECTORY', `.hoststat')dnl define(`confTO_ICONNECT', `10s')dnl define(`confCOPY_ERRORS_TO', `Postmaster')dnl define(`confTO_QUEUEWARN', `8h')dnl +define(`confPRIVACY_FLAGS', ``authwarnings,noexpn,novrfy'')dnl FEATURE(virtusertable)dnl MAILER(local)dnl MAILER(smtp)dnl + +LOCAL_CONFIG +# domains that are not us but which we will relay +FR-o /etc/sendmail.cR + +# domain override table to accept unresolvable/reject resolvable domains +Kdomaincheck hash -o /etc/domaincheck + + +LOCAL_RULESETS + +# reject bogus return addresses +Scheck_mail +R<> $@ +R$* $: $>Parse0 $>3 $1 make domain canonical +R $* < @ $+ . > $* $: < $( domaincheck $2 $: OK $) > $1 < @ $2 . > $3 + tag resolved names +R $* < @ $+ > $* $: < $( domaincheck $2 $: ? $) > $1 < @ $2 > $3 + check for overrides +R $* $@ +R $* < @ $+ > $* $#error $: 451 Sender domain must resolve +R $* $: < ? $&{client_name} > $1 no @domain on address... +R $* $@ ...local unqualed ok +R $* $#error $: 551 Domain name required + ...remote is not +R<$+> $* $#error $: $1 error from domaincheck + +# disallow relaying +Scheck_rcpt +# anything terminating locally is ok +R$* $: $>Parse0 $>3 $1 strip local crud +R$+ < @ $=w . > $@ OK +R$+ < @ $* $=R . > $@ OK + +# anything originating locally is ok +R$* $: $(dequote "" $&{client_name} $) +R$=w $@ OK +R$=R $@ OK +R$@ $@ OK + +# anything else is bogus +R$* $#error $: "550 Relaying Denied" Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/s2k-osf1.mc =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/s2k-osf1.mc (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/s2k-osf1.mc (revision 26986) @@ -1,51 +1,51 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # # # This is a Berkeley-specific configuration file for OSF/1. -# It applies only the the Sequoia 2000 Project at Berkeley, +# It applies only to the Sequoia 2000 Project at Berkeley, # and should not be used elsewhere. It is provided on the sendmail # distribution as a sample only. To create your own configuration # file, create an appropriate domain file in ../domain, change the # `DOMAIN' macro below to reference that file, and copy the result # to a name of your own choosing. # divert(0)dnl -VERSIONID(`@(#)s2k-osf1.mc 8.4 (Berkeley) 3/23/96') +VERSIONID(`@(#)s2k-osf1.mc 8.5 (Berkeley) 6/3/97') OSTYPE(osf1)dnl DOMAIN(S2K.Berkeley.EDU)dnl MAILER(local)dnl MAILER(smtp)dnl Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/s2k-ultrix4.mc =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/s2k-ultrix4.mc (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/cf/s2k-ultrix4.mc (revision 26986) @@ -1,51 +1,51 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # # # This is a Berkeley-specific configuration file for Ultrix 4.x. -# It applies only the the Sequoia 2000 Project at Berkeley, +# It applies only to the Sequoia 2000 Project at Berkeley, # and should not be used elsewhere. It is provided on the sendmail # distribution as a sample only. To create your own configuration # file, create an appropriate domain file in ../domain, change the # `DOMAIN' macro below to reference that file, and copy the result # to a name of your own choosing. # divert(0)dnl -VERSIONID(`@(#)s2k-ultrix4.mc 8.4 (Berkeley) 3/23/96') +VERSIONID(`@(#)s2k-ultrix4.mc 8.5 (Berkeley) 6/3/97') OSTYPE(ultrix4)dnl DOMAIN(S2K.Berkeley.EDU)dnl MAILER(local)dnl MAILER(smtp)dnl Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/feature/bestmx_is_local.m4 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/feature/bestmx_is_local.m4 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/feature/bestmx_is_local.m4 (revision 26986) @@ -1,66 +1,66 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # divert(0) -VERSIONID(`@(#)bestmx_is_local.m4 8.4 (Berkeley) 10/23/96') +VERSIONID(`@(#)bestmx_is_local.m4 8.5 (Berkeley) 3/28/97') divert(-1) LOCAL_CONFIG # turn on bestMX lookup table Kbestmx bestmx # limit bestmx to these domains CB`'_ARG_ LOCAL_NET_CONFIG # If we are the best MX for a site, then we want to accept # its mail as local. We assume we've already weeded out mail to # UUCP sites which are connected to us, which should also have # listed us as their best MX. # # Warning: this may generate a lot of extra DNS traffic -- a # lower cost method is to list all the expected best MX hosts # in $=w. This should be fine (and easier to administer) for # low to medium traffic hosts. If you use the limited bestmx # by passing in a set of possible domains it will improve things. ifelse(_ARG_, `', `', `#')dnl unlimited bestmx R$* < @ $* > $* $: $1 < @ $2 @@ $(bestmx $2 $) > $3 ifelse(_ARG_, `', `#', `')dnl limit bestmx to $=B -R$* < @ $* $=B > $* $: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4 +R$* < @ $* $=B . > $* $: $1 < @ $2 $3 . @@ $(bestmx $2 $3 . $) > $4 R$* $=O $* < @ $* @@ $=w . > $* $@ $>97 $1 $2 $3 R$* < @ $* @@ $=w . > $* $#local $: $1 R$* < @ $* @@ $* > $* $: $1 < @ $2 > $4 Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/m4/nullrelay.m4 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/m4/nullrelay.m4 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/m4/nullrelay.m4 (revision 26986) @@ -1,134 +1,135 @@ divert(-1) # # Copyright (c) 1983, 1995 Eric P. Allman # Copyright (c) 1988, 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. # divert(0) -VERSIONID(`@(#)nullrelay.m4 8.12 (Berkeley) 10/12/96') +VERSIONID(`@(#)nullrelay.m4 8.13 (Berkeley) 4/30/97') # # This configuration applies only to relay-only hosts. They send # all mail to a hub without consideration of the address syntax # or semantics, except for adding the hub qualification to the # addresses. # # This is based on a prototype done by Bryan Costales of ICSI. # ###################################################################### ###################################################################### ##### ##### REWRITING RULES ##### ###################################################################### ###################################################################### ########################################### ### Rulset 3 -- Name Canonicalization ### ########################################### S3 # handle null input R$@ $@ <@> # strip group: syntax (not inside angle brackets!) and trailing semicolon R$* $: $1 <@> mark addresses R$* < $* > $* <@> $: $1 < $2 > $3 unmark R$* :: $* <@> $: $1 :: $2 unmark node::addr R:`include': $* <@> $: :`include': $1 unmark :`include':... R$* : $* <@> $: $2 strip colon if marked R$* <@> $: $1 unmark -R$* ; $: $1 strip trailing semi +R$* ; $1 strip trailing semi +R$* < $* ; > $1 < $2 > bogus bracketed semi # null input now results from list:; syntax R$@ $@ :; <@> # basic textual canonicalization -- note RFC733 heuristic here R$* $: < $1 > housekeeping <> R$+ < $* > < $2 > strip excess on left R< $* > $+ < $1 > strip excess on right R<> $@ < @ > MAIL FROM:<> case R< $+ > $: $1 remove housekeeping <> ifdef(`_NO_CANONIFY_', `dnl', `# eliminate local host if present R@ $=w $=: $+ $@ @ $M $2 $3 @thishost ... R@ $+ $@ @ $1 @somewhere ... R$=E @ $=w $@ $1 @ $2 leave exposed R$+ @ $=w $@ $1 @ $M ...@thishost R$+ @ $+ $@ $1 @ $2 ...@somewhere R$=w ! $=E $@ $2 @ $1 leave exposed R$=w ! $+ $@ $2 @ $M thishost!... R$+ ! $+ $@ $1 ! $2 @ $M somewhere ! ... R$=E % $=w $@ $1 @ $2 leave exposed R$+ % $=w $@ $1 @ $M ...%thishost R$+ % $+ $@ $1 @ $2 ...%somewhere R$=E $@ $1 @ $j leave exposed R$+ $@ $1 @ $M unadorned user') ###################################### ### Ruleset 0 -- Parse Address ### ###################################### S0 R$*:;<@> $#error $@ USAGE $: "list:; syntax illegal for recipient addresses" # pass everything else to a relay host R$* $#_RELAY_ $@ $H $: $1 ################################################## ### Ruleset 4 -- Final Output Post-rewriting ### ################################################## S4 R$* <@> $@ handle <> and list:; # strip trailing dot off before passing to nullclient relay R$* @ $+ . $1 @ $2 # ###################################################################### ###################################################################### ##### `##### MAILER DEFINITIONS' ##### ###################################################################### ###################################################################### undivert(7)dnl Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/m4/proto.m4 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/m4/proto.m4 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/m4/proto.m4 (revision 26986) @@ -1,913 +1,926 @@ divert(-1) # # Copyright (c) 1983, 1995 Eric P. Allman # Copyright (c) 1988, 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. # divert(0) -VERSIONID(`@(#)proto.m4 8.139 (Berkeley) 12/31/96') +VERSIONID(`@(#)proto.m4 8.149 (Berkeley) 4/30/97') MAILER(local)dnl # level 7 config file format V7/Berkeley divert(-1) # do some sanity checking ifdef(`__OSTYPE__',, `errprint(`*** ERROR: No system type defined (use OSTYPE macro)')') # pick our default mailers ifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `esmtp')') ifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')') ifdef(`confRELAY_MAILER',, `define(`confRELAY_MAILER', `ifdef(`_MAILER_smtp_', `relay', `ifdef(`_MAILER_uucp', `uucp-new', `unknown')')')') ifdef(`confUUCP_MAILER',, `define(`confUUCP_MAILER', `uucp-old')') define(`_SMTP_', `confSMTP_MAILER')dnl for readability only define(`_LOCAL_', `confLOCAL_MAILER')dnl for readability only define(`_RELAY_', `confRELAY_MAILER')dnl for readability only define(`_UUCP_', `confUUCP_MAILER')dnl for readability only # back compatibility with old config files ifdef(`confDEF_GROUP_ID', `errprint(`*** confDEF_GROUP_ID is obsolete.') errprint(` Use confDEF_USER_ID with a colon in the value instead.')') ifdef(`confREAD_TIMEOUT', `errprint(`*** confREAD_TIMEOUT is obsolete.') errprint(` Use individual confTO_ parameters instead.')') ifdef(`confMESSAGE_TIMEOUT', `define(`_ARG_', index(confMESSAGE_TIMEOUT, /)) ifelse(_ARG_, -1, `define(`confTO_QUEUERETURN', confMESSAGE_TIMEOUT)', `define(`confTO_QUEUERETURN', substr(confMESSAGE_TIMEOUT, 0, _ARG_)) define(`confTO_QUEUEWARN', substr(confMESSAGE_TIMEOUT, eval(_ARG_+1)))')') ifdef(`confMIN_FREE_BLOCKS', `ifelse(index(confMIN_FREE_BLOCKS, /), -1,, `errprint(`*** compound confMIN_FREE_BLOCKS is obsolete.') errprint(` Use confMAX_MESSAGE_SIZE for the second part of the value.')')') # clean option definitions below.... define(`_OPTION', `ifdef(`$2', `O $1=$2', `#O $1`'ifelse($3, `',, `=$3')')')dnl divert(0)dnl ################## # local info # ################## Cwlocalhost ifdef(`USE_CW_FILE', `# file containing names of hosts for which we receive email Fw`'confCW_FILE', `dnl') # my official domain name # ... `define' this only if sendmail cannot automatically determine your domain ifdef(`confDOMAIN_NAME', `Dj`'confDOMAIN_NAME', `#Dj$w.Foo.COM') ifdef(`_NULL_CLIENT_ONLY_', `divert(-1)')dnl CP. ifdef(`UUCP_RELAY', `# UUCP relay host DY`'UUCP_RELAY CPUUCP ')dnl ifdef(`BITNET_RELAY', `# BITNET relay host DB`'BITNET_RELAY CPBITNET ')dnl ifdef(`DECNET_RELAY', `define(`_USE_DECNET_SYNTAX_', 1)dnl # DECnet relay host DC`'DECNET_RELAY CPDECNET ')dnl ifdef(`FAX_RELAY', `# FAX relay host DF`'FAX_RELAY CPFAX ')dnl # "Smart" relay host (may be null) DS`'ifdef(`SMART_HOST', SMART_HOST) # place to which unknown users should be forwarded ifdef(`LUSER_RELAY', `', `#')dnl Kuser user -m -a<> ifdef(`LUSER_RELAY', `DL`'LUSER_RELAY', `#DLname_of_luser_relay') # operators that cannot be in local usernames (i.e., network indicators) CO @ % ifdef(`_NO_UUCP_', `', `!') # a class with just dot (for identifying canonical names) C.. # a class with just a left bracket (for identifying domain literals) C[[ # Mailer table (overriding domains) ifdef(`MAILER_TABLE', `Kmailertable MAILER_TABLE', `#Kmailertable dbm /etc/mailertable') # Domain table (adding domains) ifdef(`DOMAIN_TABLE', `Kdomaintable DOMAIN_TABLE', `#Kdomaintable dbm /etc/domaintable') # Generics table (mapping outgoing addresses) ifdef(`GENERICS_TABLE', `Kgenerics GENERICS_TABLE', `#Kgenerics dbm /etc/genericstable') # Virtual user table (maps incoming users) ifdef(`VIRTUSER_TABLE', `Kvirtuser VIRTUSER_TABLE', `#Kvirtuser dbm /etc/virtusertable') # who I send unqualified names to (null means deliver locally) DR`'ifdef(`LOCAL_RELAY', LOCAL_RELAY) # who gets all local email traffic ($R has precedence for unqualified names) DH`'ifdef(`MAIL_HUB', MAIL_HUB) # dequoting map Kdequote dequote divert(0)dnl # end of nullclient diversion # class E: names that should be exposed as from this host, even if we masquerade ifdef(`_NULL_CLIENT_ONLY_', `#', `# class L: names that should be delivered locally, even if we have a relay # class M: domains that should be converted to $M #CL root ')CE root undivert(5)dnl # who I masquerade as (null for no masquerading) (see also $=M) DM`'ifdef(`MASQUERADE_NAME', MASQUERADE_NAME) # my name for error messages ifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON') undivert(6)dnl include(_CF_DIR_`m4/version.m4') ############### # Options # ############### # strip message body to 7 bits on input? _OPTION(SevenBitInput, `confSEVEN_BIT_INPUT') # 8-bit data handling _OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', adaptive) ifdef(`_NULL_CLIENT_ONLY_', `dnl', ` # wait for alias file rebuild (default units: minutes) _OPTION(AliasWait, `confALIAS_WAIT', 5m) # location of alias file _OPTION(AliasFile, `ALIAS_FILE', /etc/aliases) ') # minimum number of free blocks on filesystem _OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', 100) # maximum message size _OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', 1000000) # substitution for space (blank) characters _OPTION(BlankSub, `confBLANK_SUB', _) # avoid connecting to "expensive" mailers on initial submission? _OPTION(HoldExpensive, `confCON_EXPENSIVE') # checkpoint queue runs after every N successful deliveries _OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', 10) # default delivery mode _OPTION(DeliveryMode, `confDELIVERY_MODE', background) # automatically rebuild the alias database? _OPTION(AutoRebuildAliases, `confAUTO_REBUILD') # error message header/file _OPTION(ErrorHeader, `confERROR_MESSAGE', /etc/sendmail.oE) # error mode _OPTION(ErrorMode, `confERROR_MODE', print) # save Unix-style "From_" lines at top of header? _OPTION(SaveFromLine, `confSAVE_FROM_LINES') # temporary file mode _OPTION(TempFileMode, `confTEMP_FILE_MODE', 0600) # match recipients against GECOS field? _OPTION(MatchGECOS, `confMATCH_GECOS') # maximum hop count _OPTION(MaxHopCount, `confMAX_HOP', 17) # location of help file O HelpFile=ifdef(`HELP_FILE', HELP_FILE, /usr/lib/sendmail.hf) # ignore dots as terminators in incoming messages? _OPTION(IgnoreDots, `confIGNORE_DOTS') # name resolver options _OPTION(ResolverOptions, `confBIND_OPTS', +AAONLY) # deliver MIME-encapsulated error messages? _OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS') # Forward file search path _OPTION(ForwardPath, `confFORWARD_PATH', /var/forward/$u:$z/.forward.$w:$z/.forward) # open connection cache size _OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', 2) # open connection cache timeout _OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', 5m) # persistent host status directory _OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', .hoststat) # single thread deliveries (requires HostStatusDirectory)? _OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY') # use Errors-To: header? _OPTION(UseErrorsTo, `confUSE_ERRORS_TO') # log level _OPTION(LogLevel, `confLOG_LEVEL', 10) # send to me too, even in an alias expansion? _OPTION(MeToo, `confME_TOO') # verify RHS in newaliases? _OPTION(CheckAliases, `confCHECK_ALIASES') # default messages to old style headers if no special punctuation? _OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS') # SMTP daemon options _OPTION(DaemonPortOptions, `confDAEMON_OPTIONS', Port=esmtp) # privacy flags _OPTION(PrivacyOptions, `confPRIVACY_FLAGS', authwarnings) # who (if anyone) should get extra copies of error messages _OPTION(PostMasterCopy, `confCOPY_ERRORS_TO', Postmaster) # slope of queue-only function _OPTION(QueueFactor, `confQUEUE_FACTOR', 600000) # queue directory O QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, /var/spool/mqueue) # timeouts (many of these) _OPTION(Timeout.initial, `confTO_INITIAL', 5m) _OPTION(Timeout.connect, `confTO_CONNECT', 5m) _OPTION(Timeout.iconnect, `confTO_ICONNECT', 5m) _OPTION(Timeout.helo, `confTO_HELO', 5m) _OPTION(Timeout.mail, `confTO_MAIL', 10m) _OPTION(Timeout.rcpt, `confTO_RCPT', 1h) _OPTION(Timeout.datainit, `confTO_DATAINIT', 5m) _OPTION(Timeout.datablock, `confTO_DATABLOCK', 1h) _OPTION(Timeout.datafinal, `confTO_DATAFINAL', 1h) _OPTION(Timeout.rset, `confTO_RSET', 5m) _OPTION(Timeout.quit, `confTO_QUIT', 2m) _OPTION(Timeout.misc, `confTO_MISC', 2m) _OPTION(Timeout.command, `confTO_COMMAND', 1h) _OPTION(Timeout.ident, `confTO_IDENT', 30s) _OPTION(Timeout.fileopen, `confTO_FILEOPEN', 60s) _OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', 5d) _OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', 5d) _OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', 2d) _OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', 7d) _OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', 4h) _OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', 4h) _OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', 1h) _OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', 12h) _OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', 30m) # should we not prune routes in route-addr syntax addresses? _OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES') # queue up everything before forking? _OPTION(SuperSafe, `confSAFE_QUEUE') # status file O StatusFile=ifdef(`STATUS_FILE', `STATUS_FILE', /etc/sendmail.st) # time zone handling: # if undefined, use system default # if defined but null, use TZ envariable passed in # if defined and non-null, use that info ifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=', confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=', `O TimeZoneSpec=confTIME_ZONE') # default UID (can be username or userid:groupid) _OPTION(DefaultUser, `confDEF_USER_ID', nobody) # list of locations of user database file (null means no lookup) _OPTION(UserDatabaseSpec, `confUSERDB_SPEC', /etc/userdb) # fallback MX host _OPTION(FallbackMXhost, `confFALLBACK_MX', fall.back.host.net) # if we are the best MX host for a site, try it directly instead of config err _OPTION(TryNullMXList, `confTRY_NULL_MX_LIST') # load average at which we just queue messages _OPTION(QueueLA, `confQUEUE_LA', 8) # load average at which we refuse connections _OPTION(RefuseLA, `confREFUSE_LA', 12) # maximum number of children we allow at one time _OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', 12) # maximum number of new connections per second _OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', 3) # work recipient factor _OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', 30000) # deliver each queued job in a separate process? _OPTION(ForkEachJob, `confSEPARATE_PROC') # work class factor _OPTION(ClassFactor, `confWORK_CLASS_FACTOR', 1800) # work time factor _OPTION(RetryFactor, `confWORK_TIME_FACTOR', 90000) # shall we sort the queue by hostname first? _OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', priority) # minimum time in queue before retry _OPTION(MinQueueAge, `confMIN_QUEUE_AGE', 30m) # default character set _OPTION(DefaultCharSet, `confDEF_CHAR_SET', iso-8859-1) # service switch file (ignored on Solaris, Ultrix, OSF/1, others) _OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', /etc/service.switch) # hosts file (normally /etc/hosts) _OPTION(HostsFile, `confHOSTS_FILE', /etc/hosts) # dialup line delay on connection failure _OPTION(DialDelay, `confDIAL_DELAY', 10s) # action to take if there are no recipients in the message _OPTION(NoRecipientAction, `confNO_RCPT_ACTION', add-to-undisclosed) # chrooted environment for writing to files _OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', /arch) # are colons OK in addresses? _OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR') # how many jobs can you process in the queue? _OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', 10000) # shall I avoid expanding CNAMEs (violates protocols)? _OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES') # SMTP initial login message (old $e macro) _OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG') # UNIX initial From header format (old $l macro) _OPTION(UnixFromLine, `confFROM_LINE') # delimiter (operator) characters (old $o macro) _OPTION(OperatorChars, `confOPERATORS') # shall I avoid calling initgroups(3) because of high NIS costs? _OPTION(DontInitGroups, `confDONT_INIT_GROUPS') # are group-writable `:include:' and .forward files (un)trustworthy? _OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES') # where do errors that occur when sending errors get sent? _OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS') # what user id do we assume for the majority of the processing? _OPTION(RunAsUser, `confRUN_AS_USER', sendmail) ########################### # Message precedences # ########################### Pfirst-class=0 Pspecial-delivery=100 Plist=-30 Pbulk=-60 Pjunk=-100 ##################### # Trusted users # ##################### # this is equivalent to setting class "t" ifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `/etc/sendmail.ct') Troot Tdaemon ifdef(`_NO_UUCP_', `dnl', `Tuucp') ifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl') ######################### # Format of headers # ######################### ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl H?P?Return-Path: <$g> HReceived: confRECEIVED_HEADER H?D?Resent-Date: $a H?D?Date: $a H?F?Resent-From: confFROM_HEADER H?F?From: confFROM_HEADER H?x?Full-Name: $x # HPosted-Date: $a # H?l?Received-Date: $b H?M?Resent-Message-Id: <$t.$i@$j> H?M?Message-Id: <$t.$i@$j> ifdef(`_NULL_CLIENT_ONLY_', `include(_CF_DIR_`'m4/nullrelay.m4)m4exit', `dnl') # ###################################################################### ###################################################################### ##### ##### REWRITING RULES ##### ###################################################################### ###################################################################### ############################################ ### Ruleset 3 -- Name Canonicalization ### ############################################ S3 # handle null input (translate to <@> special case) R$@ $@ <@> # strip group: syntax (not inside angle brackets!) and trailing semicolon R$* $: $1 <@> mark addresses R$* < $* > $* <@> $: $1 < $2 > $3 unmark R@ $* <@> $: @ $1 unmark @host:... R$* :: $* <@> $: $1 :: $2 unmark node::addr R:`include': $* <@> $: :`include': $1 unmark :`include':... R$* [ $* : $* ] <@> $: $1 [ $2 : $3 ] unmark IPv6 addrs R$* : $* [ $* ] $: $1 : $2 [ $3 ] <@> remark if leading colon R$* : $* <@> $: $2 strip colon if marked R$* <@> $: $1 unmark -R$* ; $: $1 strip trailing semi +R$* ; $1 strip trailing semi +R$* < $* ; > $1 < $2 > bogus bracketed semi # null input now results from list:; syntax R$@ $@ :; <@> # strip angle brackets -- note RFC733 heuristic to get innermost item R$* $: < $1 > housekeeping <> R$+ < $* > < $2 > strip excess on left R< $* > $+ < $1 > strip excess on right R<> $@ < @ > MAIL FROM:<> case R< $+ > $: $1 remove housekeeping <> # make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later R@ $+ , $+ @ $1 : $2 change all "," to ":" # localize and dispose of route-based addresses R@ $+ : $+ $@ $>96 < @$1 > : $2 handle # find focus for list syntax R $+ : $* ; @ $+ $@ $>96 $1 : $2 ; < @ $3 > list syntax R $+ : $* ; $@ $1 : $2; list syntax # find focus for @ syntax addresses R$+ @ $+ $: $1 < @ $2 > focus on domain R$+ < $+ @ $+ > $1 $2 < @ $3 > move gaze right R$+ < @ $+ > $@ $>96 $1 < @ $2 > already canonical # do some sanity checking R$* < @ $* : $* > $* $1 < @ $2 $3 > $4 nix colons in addrs ifdef(`_NO_UUCP_', `dnl', `# convert old-style addresses to a domain-based address R$- ! $+ $@ $>96 $2 < @ $1 .UUCP > resolve uucp names R$+ . $- ! $+ $@ $>96 $3 < @ $1 . $2 > domain uucps R$+ ! $+ $@ $>96 $2 < @ $1 .UUCP > uucp subdomains ') ifdef(`_USE_DECNET_SYNTAX_', `# convert node::user addresses into a domain-based address R$- :: $+ $@ $>96 $2 < @ $1 .DECNET > resolve DECnet names R$- . $- :: $+ $@ $>96 $3 < @ $1.$2 .DECNET > numeric DECnet addr ', `dnl') # if we have % signs, take the rightmost one R$* % $* $1 @ $2 First make them all @s. R$* @ $* @ $* $1 % $2 @ $3 Undo all but the last. R$* @ $* $@ $>96 $1 < @ $2 > Insert < > and finish # else we must be a local name R$* $@ $>96 $1 ################################################ ### Ruleset 96 -- bottom half of ruleset 3 ### ################################################ S96 # handle special cases for local names R$* < @ localhost > $* $: $1 < @ $j . > $2 no domain at all R$* < @ localhost . $m > $* $: $1 < @ $j . > $2 local domain ifdef(`_NO_UUCP_', `dnl', `R$* < @ localhost . UUCP > $* $: $1 < @ $j . > $2 .UUCP domain') R$* < @ [ $+ ] > $* $: $1 < @@ [ $2 ] > $3 mark [a.b.c.d] R$* < @@ $=w > $* $: $1 < @ $j . > $3 self-literal R$* < @@ $+ > $* $@ $1 < @ $2 > $3 canon IP addr # look up domains in the domain table ifdef(`DOMAIN_TABLE', `', `#')dnl R$* < @ $+ > $* $: $1 < @ $(domaintable $2 $) > $3 undivert(2)dnl ifdef(`_NO_UUCP_', `dnl', `ifdef(`UUCP_RELAY', `# pass UUCP addresses straight through R$* < @ $+ . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `# if really UUCP, handle it immediately ifdef(`_CLASS_U_', `R$* < @ $=U . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl') ifdef(`_CLASS_V_', `R$* < @ $=V . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl') ifdef(`_CLASS_W_', `R$* < @ $=W . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl') ifdef(`_CLASS_X_', `R$* < @ $=X . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl') ifdef(`_CLASS_Y_', `R$* < @ $=Y . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl') +define(`X', ifdef(`_NO_CANONIFY_', `#', `'))dnl # try UUCP traffic as a local address -R$* < @ $+ . UUCP > $* $: $1 < @ $[ $2 $] . UUCP . > $3 -R$* < @ $+ . . UUCP . > $* $@ $1 < @ $2 . > $3') +X`'R$* < @ $+ . UUCP > $* $: $1 < @ $[ $2 $] . UUCP . > $3 +X`'R$* < @ $+ . . UUCP . > $* $@ $1 < @ $2 . > $3') +undefine(`X')dnl ') # pass to name server to make hostname canonical ifdef(`_NO_CANONIFY_', `#')dnl R$* < @ $* $~P > $* $: $1 < @ $[ $2 $3 $] > $4 # local host aliases and pseudo-domains are always canonical R$* < @ $=w > $* $: $1 < @ $2 . > $3 R$* < @ $j > $* $: $1 < @ $j . > $2 -R$* < @ $* $=M > $* $: $1 < @ $2 $3 . > $4 +ifdef(`_MASQUERADE_ENTIRE_DOMAIN_', +`R$* < @ $* $=M > $* $: $1 < @ $2 $3 . > $4', +`R$* < @ $=M > $* $: $1 < @ $2 . > $3') R$* < @ $* $=P > $* $: $1 < @ $2 $3 . > $4 R$* < @ $* . . > $* $1 < @ $2 . > $3 ################################################## ### Ruleset 4 -- Final Output Post-rewriting ### ################################################## S4 R$* <@> $@ handle <> and list:; # strip trailing dot off possibly canonical name R$* < @ $+ . > $* $1 < @ $2 > $3 # eliminate internal code -- should never get this far! R$* < @ *LOCAL* > $* $1 < @ $j > $2 # externalize local domain info R$* < $+ > $* $1 $2 $3 defocus R@ $+ : @ $+ : $+ @ $1 , @ $2 : $3 canonical R@ $* $@ @ $1 ... and exit ifdef(`_NO_UUCP_', `dnl', `# UUCP must always be presented in old form R$+ @ $- . UUCP $2!$1 u@h.UUCP => h!u') ifdef(`_USE_DECNET_SYNTAX_', `# put DECnet back in :: form R$+ @ $+ . DECNET $2 :: $1 u@h.DECNET => h::u', `dnl') # delete duplicate local names R$+ % $=w @ $=w $1 @ $2 u%host@host => u@host ############################################################## ### Ruleset 97 -- recanonicalize and call ruleset zero ### ### (used for recursive calls) ### ############################################################## S`'97 R$* $: $>3 $1 R$* $@ $>0 $1 ###################################### ### Ruleset 0 -- Parse Address ### ###################################### S0 +R$* $: $>Parse0 $1 initial parsing +R$* $: $>98 $1 handle local hacks +R$* $: $>Parse1 $1 final parsing + +SParse0 R<@> $#_LOCAL_ $: <@> special case error msgs R$* : $* ; <@> $#error $@ 5.1.3 $: "list:; syntax illegal for recipient addresses" R<@ $+> $#error $@ 5.1.1 $: "user address required" R$* $: <> $1 R<> $* < @ [ $+ ] > $* $1 < @ [ $2 ] > $3 R<> $* <$* : $* > $* $#error $@ 5.1.1 $: "colon illegal in host name part" R<> $* $1 R$* < @ . $* > $* $#error $@ 5.1.2 $: "invalid host name" +R$* < @ $* .. $* > $* $#error $@ 5.1.2 $: "invalid host name" ifdef(`_MAILER_smtp_', `# handle numeric address spec R$* < @ [ $+ ] > $* $: $>98 $1 < @ [ $2 ] > $3 numeric internet spec R$* < @ [ $+ ] > $* $#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3 still numeric: send', `dnl') # now delete the local info -- note $=O to find characters that cause forwarding -R$* < @ > $* $@ $>97 $1 user@ => user -R< @ $=w . > : $* $@ $>97 $2 @here:... -> ... +R$* < @ > $* $@ $>Parse0 $>3 $1 user@ => user +R< @ $=w . > : $* $@ $>Parse0 $>3 $2 @here:... -> ... R$- < @ $=w . > $: $(dequote $1 $) < @ $2 . > dequote "foo"@here R< @ $+ > $#error $@ 5.1.1 $: "user address required" -R$* $=O $* < @ $=w . > $@ $>97 $1 $2 $3 ...@here -> ... +R$* $=O $* < @ $=w . > $@ $>Parse0 $>3 $1 $2 $3 ...@here -> ... -# handle local hacks -R$* $: $>98 $1 - +SParse1 # handle virtual users define(`X', ifdef(`VIRTUSER_TABLE', `', `#'))dnl X`'R$+ < @ $=w . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . > -X`'R< @ > $+ < @ $+ . > $: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . > -X`'R< @ > $+ $: $1 +X`'R<@> $+ + $* < @ $* . > + $: < $(virtuser $1 + * @ $3 $@ $1 $: @ $) > $1 + $2 < @ $3 . > +X`'R<@> $+ + $* < @ $* . > + $: < $(virtuser $1 @ $3 $@ $1 $: @ $) > $1 + $2 < @ $3 . > +X`'R<@> $+ < @ $+ . > $: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . > +X`'R<@> $+ $: $1 X`'R< error : $- $+ > $* $#error $@ $( dequote $1 $) $: $2 X`'R< $+ > $+ < @ $+ > $: $>97 $1 undefine(`X')dnl # short circuit local delivery so forwarded email works ifdef(`_MAILER_usenet_', `', `#')dnl R$+ . USENET < @ $=w . > $#usenet $: $1 handle usenet specially ifdef(`_STICKY_LOCAL_DOMAIN_', `R$+ < @ $=w . > $: < $H > $1 < @ $2 . > first try hub R< $+ > $+ < $+ > $>95 < $1 > $2 < $3 > yep .... R< > $+ + $* < $+ > $#_LOCAL_ $: $1 + $2 plussed name? R< > $+ < $+ > $#_LOCAL_ $: @ $1 nope, local address', `R$=L < @ $=w . > $#_LOCAL_ $: @ $1 special local names R$+ < @ $=w . > $#_LOCAL_ $: $1 regular local name') define(`X', ifdef(`MAILER_TABLE', `', `#'))dnl # not local -- try mailer table lookup X`'R$* <@ $+ > $* $: < $2 > $1 < @ $2 > $3 extract host name X`'R< $+ . > $* $: < $1 > $2 strip trailing dot X`'R< $+ > $* $: < $(mailertable $1 $) > $2 lookup X`'R< $~[ : $+ > $* $>95 < $1 : $2 > $3 check -- resolved? X`'R< $+ > $* $: $>90 <$1> $2 try domain undefine(`X')dnl undivert(4)dnl ifdef(`_NO_UUCP_', `dnl', `# resolve remotely connected UUCP links (if any) ifdef(`_CLASS_V_', `R$* < @ $=V . UUCP . > $* $: $>95 < $V > $1 <@$2.UUCP.> $3', `dnl') ifdef(`_CLASS_W_', `R$* < @ $=W . UUCP . > $* $: $>95 < $W > $1 <@$2.UUCP.> $3', `dnl') ifdef(`_CLASS_X_', `R$* < @ $=X . UUCP . > $* $: $>95 < $X > $1 <@$2.UUCP.> $3', `dnl')') # resolve fake top level domains by forwarding to other hosts ifdef(`BITNET_RELAY', `R$*<@$+.BITNET.>$* $: $>95 < $B > $1 <@$2.BITNET.> $3 user@host.BITNET', `dnl') ifdef(`DECNET_RELAY', `R$*<@$+.DECNET.>$* $: $>95 < $C > $1 <@$2.DECNET.> $3 user@host.DECNET', `dnl') ifdef(`_MAILER_pop_', `R$+ < @ POP. > $#pop $: $1 user@POP', `dnl') ifdef(`_MAILER_fax_', `R$+ < @ $+ .FAX. > $#fax $@ $2 $: $1 user@host.FAX', `ifdef(`FAX_RELAY', `R$*<@$+.FAX.>$* $: $>95 < $F > $1 <@$2.FAX.> $3 user@host.FAX', `dnl')') ifdef(`UUCP_RELAY', `# forward non-local UUCP traffic to our UUCP relay R$*<@$*.UUCP.>$* $: $>95 < $Y > $1 <@$2.UUCP.> $3 uucp mail', `ifdef(`_MAILER_uucp_', `# forward other UUCP traffic straight to UUCP R$* < @ $+ .UUCP. > $* $#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3 user@host.UUCP', `dnl')') ifdef(`_MAILER_usenet_', ` # addresses sent to net.group.USENET will get forwarded to a newsgroup R$+ . USENET $#usenet $: $1', `dnl') ifdef(`_LOCAL_RULES_', `# figure out what should stay in our local mail system undivert(1)', `dnl') # pass names that still have a host to a smarthost (if defined) R$* < @ $* > $* $: $>95 < $S > $1 < @ $2 > $3 glue on smarthost name # deal with other remote names ifdef(`_MAILER_smtp_', `R$* < @$* > $* $#_SMTP_ $@ $2 $: $1 < @ $2 > $3 user@host.domain', `R$* < @$* > $* $#error $@ 5.1.2 $: Unrecognized host name $2') # if this is quoted, strip the quotes and try again R$+ $: $(dequote $1 $) strip quotes R$+ $=O $+ $@ $>97 $1 $2 $3 try again # handle locally delivered names R$=L $#_LOCAL_ $: @ $1 special local names R$+ $#_LOCAL_ $: $1 regular local names ########################################################################### ### Ruleset 5 -- special rewriting after aliases have been expanded ### ########################################################################### S5 # deal with plussed users so aliases work nicely R$+ + * $#_LOCAL_ $@ $&h $: $1 -R$+ + $* $#_LOCAL_ $@ $2 $: $1 + * +R$+ + $* $#_LOCAL_ $@ + $2 $: $1 + * # prepend an empty "forward host" on the front R$+ $: <> $1 define(`X', ifdef(`LUSER_RELAY', `', `#'))dnl # send unrecognized local users to a relay host -X`'R< > $+ + $* $: < $L . > $( user $1 $) + $2 X`'R< > $+ $: < $L . > $( user $1 $) look up user X`'R< $* > $+ <> $* $: < > $2 $3 found; strip $L X`'R< $* . > $+ $: < $1 > $2 strip extra dot undefine(`X')dnl -# handle plussed local names -R< > $+ + $* $#_LOCAL_ $@ $2 $: $1 - # see if we have a relay or a hub R< > $+ $: < $H > $1 try hub R< > $+ $: < $R > $1 try relay -R< > $+ $@ $1 nope, give up +R< > $+ $: < > < $1 $(dequote "" $&h $) > nope, restore +detail +R< > < $+ + $* > $* < > < $1 > + $2 $3 find the user part +R< > < $+ > + $* $#_LOCAL_ $@ $2 $: @ $1 strip the extra + +R< > < $+ > $@ $1 no +detail +R$+ $: $1 $(dequote "" $&h $) add +detail back in R< local : $* > $* $: $>95 < local : $1 > $2 no host extension R< error : $* > $* $: $>95 < error : $1 > $2 no host extension R< $- : $+ > $+ $: $>95 < $1 : $2 > $3 < @ $2 > R< $+ > $+ $@ $>95 < $1 > $2 < @ $1 > ################################################################### ### Ruleset 90 -- try domain part of mailertable entry ### ################################################################### define(`X', ifdef(`MAILER_TABLE', `', `#'))dnl S90 X`'R$* <$- . $+ > $* $: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4 X`'R$* <$~[ : $+ > $* $>95 < $2 : $3 > $4 check -- resolved? X`'R$* < . $+ > $* $@ $>90 $1 . <$2> $3 no -- strip & try again X`'R$* < $* > $* $: < $(mailertable . $@ $1$2 $) > $3 try "." X`'R< $~[ : $+ > $* $>95 < $1 : $2 > $3 "." found? X`'R< $* > $* $@ $2 no mailertable match undefine(`X')dnl ################################################################### ### Ruleset 95 -- canonify mailer:[user@]host syntax to triple ### ################################################################### S95 R< > $* $@ $1 strip off null relay R< error : $- $+ > $* $#error $@ $( dequote $1 $) $: $2 R< local : $* > $* $>CanonLocal < $1 > $2 R< $- : $+ @ $+ > $*<$*>$* $# $1 $@ $3 $: $2<@$3> use literal user R< $- : $+ > $* $# $1 $@ $2 $: $3 try qualified mailer R< $=w > $* $@ $2 delete local host R< $+ > $* $#_RELAY_ $@ $1 $: $2 use unqualified mailer ################################################################### ### Ruleset CanonLocal -- canonify local: syntax ### ################################################################### SCanonLocal # strip trailing dot from any host name that may appear R< $* > $* < @ $* . > $: < $1 > $2 < @ $3 > # handle local: syntax -- use old user, either with or without host -R< > $* < @ $* > $* $#local $@ $1@$2 $: $1 -R< > $+ $#local $@ $1 $: $1 +R< > $* < @ $* > $* $#_LOCAL_ $@ $1@$2 $: $1 +R< > $+ $#_LOCAL_ $@ $1 $: $1 # handle local:user@host syntax -- ignore host part -R< $+ @ $+ > $* $: < $1 > $3 +R< $+ @ $+ > $* < @ $* > $: < $1 > $3 < @ $4 > # handle local:user syntax -R< $+ > $* <@ $* > $* $#local $@ $2@$3 $: $1 -R< $+ > $* $#local $@ $2 $: $1 +R< $+ > $* <@ $* > $* $#_LOCAL_ $@ $2@$3 $: $1 +R< $+ > $* $#_LOCAL_ $@ $2 $: $1 ################################################################### ### Ruleset 93 -- convert header names to masqueraded form ### ################################################################### S93 # handle generics database define(`X', ifdef(`GENERICS_TABLE', `', `#'))dnl X`'R$+ < @ $=G . > $: < $1@$2 > $1 < @ $2 . > @ mark X`'R$+ < @ *LOCAL* > $: < $1@$j > $1 < @ *LOCAL* > @ mark X`'R< $+ > $+ < $* > @ $: < $(generics $1 $: $) > $2 < $3 > X`'R< > $+ < @ $+ > $: < $(generics $1 $: $) > $1 < @ $2 > X`'R< $* @ $* > $* < $* > $@ $>3 $1 @ $2 found qualified X`'R< $+ > $* < $* > $: $>3 $1 @ *LOCAL* found unqualified X`'R< > $* $: $1 not found undefine(`X')dnl # special case the users that should be exposed R$=E < @ *LOCAL* > $@ $1 < @ $j . > leave exposed ifdef(`_MASQUERADE_ENTIRE_DOMAIN_', `R$=E < @ $* $=M . > $@ $1 < @ $2 $3 . >', `R$=E < @ $=M . > $@ $1 < @ $2 . >') ifdef(`_LIMITED_MASQUERADE_', `#')dnl R$=E < @ $=w . > $@ $1 < @ $2 . > # handle domain-specific masquerading ifdef(`_MASQUERADE_ENTIRE_DOMAIN_', `R$* < @ $* $=M . > $* $: $1 < @ $2 $3 . @ $M > $4 convert masqueraded doms', `R$* < @ $=M . > $* $: $1 < @ $2 . @ $M > $3 convert masqueraded doms') ifdef(`_LIMITED_MASQUERADE_', `#')dnl R$* < @ $=w . > $* $: $1 < @ $2 . @ $M > $3 R$* < @ *LOCAL* > $* $: $1 < @ $j . @ $M > $2 R$* < @ $+ @ > $* $: $1 < @ $2 > $3 $M is null R$* < @ $+ @ $+ > $* $: $1 < @ $3 . > $4 $M is not null ################################################################### ### Ruleset 94 -- convert envelope names to masqueraded form ### ################################################################### S94 ifdef(`_MASQUERADE_ENVELOPE_', `', `#')dnl R$+ $@ $>93 $1 ifdef(`_MASQUERADE_ENVELOPE_', `#', `')dnl R$* < @ *LOCAL* > $* $: $1 < @ $j . > $2 ################################################################### ### Ruleset 98 -- local part of ruleset zero (can be null) ### ################################################################### S98 undivert(3)dnl undivert(9)dnl # ###################################################################### ###################################################################### ##### `##### MAILER DEFINITIONS' ##### ###################################################################### ###################################################################### undivert(7)dnl Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/m4/version.m4 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/m4/version.m4 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/m4/version.m4 (revision 26986) @@ -1,39 +1,39 @@ divert(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # -VERSIONID(`@(#)version.m4 8.8.5.3 (Berkeley) 1/21/97') +VERSIONID(`@(#)version.m4 8.8.6.1 (Berkeley) 6/14/97') # divert(0) # Configuration version number -DZ8.8.5`'ifdef(`confCF_VERSION', `/confCF_VERSION') +DZ8.8.6`'ifdef(`confCF_VERSION', `/confCF_VERSION') Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/mailer/mail11.m4 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/mailer/mail11.m4 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/mailer/mail11.m4 (revision 26986) @@ -1,50 +1,51 @@ PUSHDIVERT(-1) # # Not exciting enough to bother with copyrights and most of the # rulesets are based from those provided by DEC. # Barb Dijker, Labyrinth Computer Services, barb@labyrinth.com # # This mailer is only useful if you have DECNET and the # mail11 program - gatekeeper.dec.com:/pub/DEC/gwtools. # # For local delivery of DECNET style addresses to the local # DECNET node, you will need feature(use_cw_file) and put # your DECNET nodename in in the cw file. # ifdef(`MAIL11_MAILER_PATH',, `define(`MAIL11_MAILER_PATH', /usr/etc/mail11)') ifdef(`MAIL11_MAILER_FLAGS',, `define(`MAIL11_MAILER_FLAGS', nsFx)') ifdef(`MAIL11_MAILER_ARGS',, `define(`MAIL11_MAILER_ARGS', mail11 $g $x $h $u)') define(`_USE_DECNET_SYNTAX_') +define(`_LOCAL_', ifdef(`confLOCAL_MAILER', confLOCAL_MAILER, `local')) POPDIVERT PUSHDIVERT(3) # DECNET delivery -R$* < @ $=w .DECNET. > $#local $: $1 local DECnet +R$* < @ $=w .DECNET. > $#_LOCAL_ $: $1 local DECnet R$+ < @ $+ .DECNET. > $#mail11 $@ $2 $: $1 DECnet user POPDIVERT PUSHDIVERT(6) CPDECNET POPDIVERT ########################################### ### UTK-MAIL11 Mailer specification ### ########################################### -VERSIONID(`@(#)mail11.m4 8.1 (Berkeley) 5/23/95') +VERSIONID(`@(#)mail11.m4 8.4 (Berkeley) 3/18/97') Mmail11, P=MAIL11_MAILER_PATH, F=MAIL11_MAILER_FLAGS, S=15, R=25, A=MAIL11_MAILER_ARGS S15 R$+ $: $>25 $1 preprocess R$w :: $+ $@ $w :: $1 ready to go S25 R$+ < @ $- .UUCP > $: $2 ! $1 back to old style R$+ < @ $- .DECNET > $: $2 :: $1 convert to DECnet style R$+ < @ $- .LOCAL > $: $2 :: $1 convert to DECnet style R$+ < @ $=w. > $: $2 :: $1 convert to DECnet style R$=w :: $+ $2 strip local names R$+ :: $+ $@ $1 :: $2 already qualified Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/mailer/procmail.m4 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/mailer/procmail.m4 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/mailer/procmail.m4 (revision 26986) @@ -1,54 +1,54 @@ PUSHDIVERT(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # ifdef(`PROCMAIL_MAILER_PATH',, `ifdef(`PROCMAIL_PATH', `define(`PROCMAIL_MAILER_PATH', PROCMAIL_PATH)', `define(`PROCMAIL_MAILER_PATH', /usr/local/bin/procmail)')') ifdef(`PROCMAIL_MAILER_FLAGS',, - `define(`PROCMAIL_MAILER_FLAGS', `Shu')') + `define(`PROCMAIL_MAILER_FLAGS', `SPhnu9')') ifdef(`PROCMAIL_MAILER_ARGS',, - `define(`PROCMAIL_MAILER_ARGS', `procmail -m $h $f $u')') + `define(`PROCMAIL_MAILER_ARGS', `procmail -Y -m $h $f $u')') POPDIVERT ######################*****############## ### PROCMAIL Mailer specification ### ##################*****################## -VERSIONID(`@(#)procmail.m4 8.5 (Berkeley) 12/28/95') +VERSIONID(`@(#)procmail.m4 8.6 (Berkeley) 4/30/97') -Mprocmail, P=PROCMAIL_MAILER_PATH, F=CONCAT(`DFMm', PROCMAIL_MAILER_FLAGS), S=11/31, R=21/31, T=DNS/RFC822/X-Unix, +Mprocmail, P=PROCMAIL_MAILER_PATH, F=CONCAT(`DFM', PROCMAIL_MAILER_FLAGS), S=11/31, R=21/31, T=DNS/RFC822/X-Unix, ifdef(`PROCMAIL_MAILER_MAX', `M=PROCMAIL_MAILER_MAX, ')A=PROCMAIL_MAILER_ARGS Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/mailer/uucp.m4 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/mailer/uucp.m4 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/mailer/uucp.m4 (revision 26986) @@ -1,174 +1,174 @@ PUSHDIVERT(-1) # # Copyright (c) 1983 Eric P. Allman # Copyright (c) 1988, 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. # ifdef(`UUCP_MAILER_PATH',, `define(`UUCP_MAILER_PATH', /usr/bin/uux)') ifdef(`UUCP_MAILER_ARGS',, `define(`UUCP_MAILER_ARGS', `uux - -r -a$g -gC $h!rmail ($u)')') ifdef(`UUCP_MAILER_FLAGS',, `define(`UUCP_MAILER_FLAGS', `')') ifdef(`UUCP_MAILER_MAX',, `define(`UUCP_MAILER_MAX', `ifdef(`UUCP_MAX_SIZE', `UUCP_MAX_SIZE', 100000)')') POPDIVERT ##################################### ### UUCP Mailer specification ### ##################################### -VERSIONID(`@(#)uucp.m4 8.24 (Berkeley) 9/5/95') +VERSIONID(`@(#)uucp.m4 8.25 (Berkeley) 3/16/97') # # There are innumerable variations on the UUCP mailer. It really # is rather absurd. # # old UUCP mailer (two names) Muucp, P=UUCP_MAILER_PATH, F=CONCAT(DFMhuUd, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, A=UUCP_MAILER_ARGS Muucp-old, P=UUCP_MAILER_PATH, F=CONCAT(DFMhuUd, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, A=UUCP_MAILER_ARGS # smart UUCP mailer (handles multiple addresses) (two names) Msuucp, P=UUCP_MAILER_PATH, F=CONCAT(mDFMhuUd, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, A=UUCP_MAILER_ARGS Muucp-new, P=UUCP_MAILER_PATH, F=CONCAT(mDFMhuUd, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, A=UUCP_MAILER_ARGS ifdef(`_MAILER_smtp_', `# domain-ized UUCP mailer Muucp-dom, P=UUCP_MAILER_PATH, F=CONCAT(mDFMhud, UUCP_MAILER_FLAGS), S=52/31, R=ifdef(`_ALL_MASQUERADE_', `21/31', `21'), M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, A=UUCP_MAILER_ARGS # domain-ized UUCP mailer with UUCP-style sender envelope Muucp-uudom, P=UUCP_MAILER_PATH, F=CONCAT(mDFMhud, UUCP_MAILER_FLAGS), S=72/31, R=ifdef(`_ALL_MASQUERADE_', `21/31', `21'), M=UUCP_MAILER_MAX, _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix, A=UUCP_MAILER_ARGS') # # envelope and header sender rewriting # S12 # handle error address as a special case R<@> $n errors to mailer-daemon # list:; syntax should disappear R:; <@> $@ R$* < @ $* . > $* $1 < @ $2 > $3 strip trailing dots R$* < @ $=w > $1 strip local name R<@ $- . UUCP > : $+ $1 ! $2 convert to UUCP format R<@ $+ > : $+ $1 ! $2 convert to UUCP format R$* < @ $- . UUCP > $2 ! $1 convert to UUCP format R$* < @ $+ > $2 ! $1 convert to UUCP format R$&h ! $+ ! $+ $@ $1 ! $2 $h!...!user => ...!user R$&h ! $+ $@ $&h ! $1 $h!user => $h!user R$+ $: $U ! $1 prepend our name R! $+ $: $k ! $1 in case $U undefined # # envelope recipient rewriting # S22 # list:; should disappear R:; <@> $@ R$* < @ $* . > $* $1 < @ $2 > $3 strip trailing dots -R$* < @ $j > $1 strip local name +R$* < @ $=w > $1 strip local name R<@ $- . UUCP > : $+ $1 ! $2 convert to UUCP format R<@ $+ > : $+ $1 ! $2 convert to UUCP format R$* < @ $- . UUCP > $2 ! $1 convert to UUCP format R$* < @ $+ > $2 ! $1 convert to UUCP format # # header recipient rewriting # S42 # list:; syntax should disappear R:; <@> $@ R$* < @ $* . > $* $1 < @ $2 > $3 strip trailing dots -R$* < @ $j > $1 strip local name +R$* < @ $=w > $1 strip local name R<@ $- . UUCP > : $+ $1 ! $2 convert to UUCP format R<@ $+ > : $+ $1 ! $2 convert to UUCP format R$* < @ $- . UUCP > $2 ! $1 convert to UUCP format R$* < @ $+ > $2 ! $1 convert to UUCP format R$&h ! $+ ! $+ $@ $1 ! $2 $h!...!user => ...!user R$&h ! $+ $@ $&h ! $1 $h!user => $h!user R$+ $: $U ! $1 prepend our name R! $+ $: $k ! $1 in case $U undefined ifdef(`_MAILER_smtp_', `# # envelope sender rewriting for uucp-dom mailer # S52 # handle error address as a special case R<@> $n errors to mailer-daemon # pass everything to standard SMTP mailer rewriting R$* $@ $>11 $1 # # envelope sender rewriting for uucp-uudom mailer # S72 # handle error address as a special case R<@> $n errors to mailer-daemon # do standard SMTP mailer rewriting R$* $: $>11 $1 R$* < @ $* . > $* $1 < @ $2 > $3 strip trailing dots R<@ $- . UUCP > : $+ $@ $1 ! $2 convert to UUCP format R<@ $+ > : $+ $@ $1 ! $2 convert to UUCP format R$* < @ $- . UUCP > $@ $2 ! $1 convert to UUCP format R$* < @ $+ > $@ $2 ! $1 convert to UUCP format') PUSHDIVERT(4) # resolve locally connected UUCP links R$* < @ $=Z . UUCP. > $* $#uucp-uudom $@ $2 $: $1 < @ $2 .UUCP. > $3 R$* < @ $=Y . UUCP. > $* $#uucp-new $@ $2 $: $1 < @ $2 .UUCP. > $3 R$* < @ $=U . UUCP. > $* $#uucp-old $@ $2 $: $1 < @ $2 .UUCP. > $3 POPDIVERT Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/ostype/gnuhurd.m4 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/ostype/gnuhurd.m4 (nonexistent) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/ostype/gnuhurd.m4 (revision 26986) @@ -0,0 +1,41 @@ +divert(-1) +# +# Copyright (c) 1983 Eric P. Allman +# Copyright (c) 1988, 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. +# +# + +divert(0) +VERSIONID(`@(#)gnuhurd.m4 8.1 (Berkeley) 3/8/97') +ifdef(`HELP_FILE',, `define(`HELP_FILE', /share/misc/sendmail.hf)')dnl +ifdef(`STATUS_FILE',, `define(`STATUS_FILE', /var/log/sendmail.st)')dnl +ifdef(`LOCAL_MAILER_PATH',, `define(`LOCAL_MAILER_PATH', /libexec/mail.local)')dnl Property changes on: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/ostype/gnuhurd.m4 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/ostype/irix6.m4 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/cf/ostype/irix6.m4 (nonexistent) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/cf/ostype/irix6.m4 (revision 26986) @@ -0,0 +1,61 @@ +divert(-1) +# +# Copyright (c) 1995 Eric P. Allman +# Copyright (c) 1988, 1993 +# The Regents of the University of California. All rights reserved. +# +# Contributed by Kari E. Hurtta +# +# 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. +# + +# +# Notes: +# - SGI's /etc/sendmail.cf defines also 'u' for local mailer flags -- you +# perhaps don't want it. +# - Perhaps is should also add define(`LOCAL_MAILER_CHARSET', iso-8859-1) +# put some Asian sites may prefer otherwise -- or perhaps not. +# - SGI's /etc/sendmail.cf seems use: A=mail -s -d $u +# It seems work without that -s however. +# - SGI's /etc/sendmail.cf set's default uid and gid to 998 (guest) +# - In SGI seems that TZ variable is needed that correct time is marked to +# syslog +# - helpfile is in /etc/sendmail.hf in SGI's /etc/sendmail.cf +# + +divert(0) +VERSIONID(`@(#)irix6.m4 8.1 (Berkeley) 4/11/97') +ifdef(`LOCAL_MAILER_FLAGS',, `define(`LOCAL_MAILER_FLAGS', Ehmu9)')dnl +ifdef(`LOCAL_MAILER_ARGS',, `define(`LOCAL_MAILER_ARGS', `mail -s -d $u')')dnl +ifdef(`QUEUE_DIR',, `define(`QUEUE_DIR', /var/spool/mqueue)')dnl +define(`ALIAS_FILE', /etc/aliases)dnl +ifdef(`STATUS_FILE',, `define(`STATUS_FILE', /var/sendmail.st)')dnl +ifdef(`HELP_FILE',, `define(`HELP_FILE', /etc/sendmail.hf)')dnl +define(`confDEF_USER_ID', `998:998')dnl +define(`confTIME_ZONE', USE_TZ)dnl Property changes on: vendor/sendmail/dist-old/usr.sbin/sendmail/cf/ostype/irix6.m4 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: vendor/sendmail/dist-old/usr.sbin/sendmail/contrib/etrn.pl =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/contrib/etrn.pl (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/contrib/etrn.pl (revision 26986) @@ -1,301 +1,316 @@ #!/usr/local/bin/perl 'di '; 'ds 00 \\"'; 'ig 00 '; # # THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin. # # hardcoded constants, should work fine for BSD-based systems -require 'sys/socket.ph'; +use Socket; +use Getopt::Std; $sockaddr = 'S n a4 x8'; # system requirements: # must have 'hostname' program. ############################################################################# # Copyright (c) 1996 John T. Beck # 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 John T. Beck. # 4. The name of John Beck may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY JOHN T. BECK ``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 JOHN T. BECK 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. # # This copyright notice derived from material copyrighted by the Regents # of the University of California. # # Contributions accepted. ############################################################################# # Further disclaimer: the etrn.pl script was highly leveraged from the # expn.pl script which is (C) 1993 David Muir Sharnoff. ############################################################################# $port = 'smtp'; $av0 = $0; select(STDERR); $0 = "$av0 - running hostname"; chop($name = `hostname || uname -n`); $0 = "$av0 - lookup host FQDN and IP addr"; ($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name); -push(@hosts,$hostname); +$0 = "$av0 - parsing args"; +$usage = "Usage: $av0 [-wd] host [args]"; +getopts('dw'); +$watch = $opt_w; +$debug = $opt_d; +$server = shift(@ARGV); +@hosts = @ARGV; +die $usage unless $server; -$0 = "$av0 - parsing sendmail.cf"; -open(CF, "){ - if (/^Fw.*$/){ # look for a line starting with "Fw" - $cwfile = $_; - chop($cwfile); - $optional = /^Fw-o/; - $cwfile =~ s,^Fw[^/]*,,; # extract the file name - } -} -close(CF); +if (!@hosts) { + push(@hosts,$hostname); -$0 = "$av0 - reading $cwfile"; -if (open(CW, "<$cwfile")){ - while (){ - $thishost = $_; - chop($thishost); - push(@hosts, $thishost) unless $thishost =~ $hostname; + $0 = "$av0 - parsing sendmail.cf"; + open(CF, "){ + if (/^Fw.*$/){ # look for a line starting with "Fw" + $cwfile = $_; + chop($cwfile); + $optional = /^Fw-o/; + $cwfile =~ s,^Fw[^/]*,,; # extract the file name + } + if (/^Cw(.*)$/){ # look for a line starting with "Cw" + @cws = split (' ', $1); + while (@cws) { + $thishost = shift(@cws); + push(@hosts, $thishost) unless $thishost =~ "$hostname|localhost"; + } + } } - close(CW); -} else { - die "open $cwfile: $!" unless optional; -} + close(CF); -$0 = "$av0 - parsing args"; -$usage = "Usage: $av0 [-wd] host"; -for $a (@ARGV) { - die $usage if $a eq "-"; - while ($a =~ s/^(-.*)([wd])/$1/) { - eval '$'."flag_$2 += 1"; + if ($cwfile){ + $0 = "$av0 - reading $cwfile"; + if (open(CW, "<$cwfile")){ + while (){ + $thishost = $_; + chop($thishost); + push(@hosts, $thishost) unless $thishost =~ $hostname; + } + close(CW); + } else { + die "open $cwfile: $!" unless $optional; + } } - next if $a eq "-"; - die $usage if $a =~ /^-/; - $server = $a; } -$watch = $flag_w; -$debug = $flag_d; -die $usage unless $server; - $0 = "$av0 - building local socket"; ($name,$aliases,$proto) = getprotobyname('tcp'); ($name,$aliases,$port) = getservbyname($port,'tcp') unless $port =~ /^\d+/; -$this = pack($sockaddr, &AF_INET, 0, $thisaddr); # look it up $0 = "$av0 - gethostbyname($server)"; ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server); # get a connection $0 = "$av0 - socket to $server"; $that = pack($sockaddr, &AF_INET, $port, $thataddr); socket(S, &AF_INET, &SOCK_STREAM, $proto) || die "socket: $!"; -$0 = "$av0 - bind to $server"; -bind(S, $this) - || die "bind $hostname,0: $!"; $0 = "$av0 - connect to $server"; print "debug = $debug server = $server\n" if $debug > 8; if (! connect(S, $that)) { $0 = "$av0 - $server: could not connect: $!\n"; } select((select(S),$| = 1)[0]); # don't buffer output to S # read the greeting $0 = "$av0 - talking to $server"; &alarm("greeting with $server",''); while() { alarm(0); print if $watch; if (/^(\d+)([- ])/) { if ($1 != 220) { $0 = "$av0 - bad numeric response from $server"; &alarm("giving up after bad response from $server",''); &read_response($2,$watch); alarm(0); print STDERR "$server: NOT 220 greeting: $_" if ($debug || $watch); } last if ($2 eq " "); } else { $0 = "$av0 - bad response from $server"; print STDERR "$server: NOT 220 greeting: $_" if ($debug || $watch); close(S); } &alarm("greeting with $server",''); } alarm(0); # if this causes problems, remove it $0 = "$av0 - sending helo to $server"; &alarm("sending ehlo to $server",""); &ps("ehlo $hostname"); $etrn_support = 0; while() { if (/^250([- ])ETRN(.+)$/){ $etrn_support = 1; } print if $watch; last if /^\d+ /; } alarm(0); if ($etrn_support){ - print "ETRN supported\n" if ($debug) + print "ETRN supported\n" if ($debug); &alarm("sending etrn to $server",''); while (@hosts) { $server = shift(@hosts); &ps("etrn $server"); while() { print if $watch; last if /^\d+ /; } sleep(1); } } else { print "\nETRN not supported\n\n" } &alarm("sending 'quit' to $server",''); $0 = "$av0 - sending 'quit' to $server"; &ps("quit"); while() { print if $watch; last if /^\d+ /; } close(S); alarm(0); select(STDOUT); exit(0); # print to the server (also to stdout, if -w) sub ps { local($p) = @_; print ">>> $p\n" if $watch; print S "$p\n"; } sub alarm { local($alarm_action,$alarm_redirect,$alarm_user) = @_; alarm(3600); $SIG{ALRM} = 'handle_alarm'; } sub handle_alarm { &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user); } # read the rest of the current smtp daemon's response (and toss it away) sub read_response { local($done,$watch) = @_; local(@resp); print $s if $watch; while(($done eq "-") && ($s = ) && ($s =~ /^\d+([- ])/)) { print $s if $watch; $done = $1; push(@resp,$s); } return @resp; } # to pass perl -w: @tp; $flag_a; $flag_d; &handle_alarm; ################### BEGIN PERL/TROFF TRANSITION .00 ; 'di .nr nl 0-1 .nr % 0 .\\"'; __END__ .\" ############## END PERL/TROFF TRANSITION -.TH ETRN 1 "November 16, 1996" +.TH ETRN 1 "January 25, 1997" .AT 3 .SH NAME etrn \- start mail queue run .SH SYNOPSIS .B etrn .RI [ -w ] .RI [ -d ] .IR hostname +.RI [ args ] .SH DESCRIPTION .B etrn will use the SMTP .B etrn command to start mail delivery from the host given on the command line. +.B etrn +usually sends an +.B etrn +for each host the local sendmail accepts e-mail for, but if +.IR args +are specified, +.B etrn +uses these as arguments for the SMTP +.B etrn +commands passed to the host given on the command line. .SH OPTIONS .LP The normal mode of operation for .B etrn is to do all of its work silently. The following options make it more verbose. It is not necessary to make it verbose to see what it is doing because as it works, it changes its .BR argv [0] variable to reflect its current activity. The .IR -w , watch, flag will cause .B etrn to show you its conversations with the mail daemons. The .IR -d , debug, flag will expose many of the inner workings so that it is possible to eliminate bugs. .SH ENVIRONMENT No enviroment variables are used. .SH FILES .B /etc/sendmail.cf .SH SEE ALSO .BR sendmail (8), RFC 1985. .SH BUGS Not all mail daemons will implement .B etrn . .LP It is assumed that you are running domain names. .SH CREDITS Leveraged from David Muir Sharnoff's expn.pl script. +Christian von Roques added support for +.IR args +and fixed a couple of bugs. .SH AVAILABILITY The latest version of .B etrn is available in the contrib directory of the sendmail distribution through anonymous ftp at .IR ftp://ftp.sendmail.org/ucb/src/sendmail/ . .SH AUTHOR .I John T. Beck\ \ \ \ Index: vendor/sendmail/dist-old/usr.sbin/sendmail/contrib/passwd-to-alias.pl =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/contrib/passwd-to-alias.pl (nonexistent) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/contrib/passwd-to-alias.pl (revision 26986) @@ -0,0 +1,28 @@ +#!/bin/perl + +# +# Convert GECOS information in password files to alias syntax. +# +# Contributed by Kari E. Hurtta +# + +print "# Generated from passwd by $0\n"; + +while (@a = getpwent) { + ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = @a; + + ($fullname = $gcos) =~ s/,.*$//; + + if (!-d $dir || !-x $shell) { + print "$name: root\n"; + } + + $fullname =~ s/\.*[ _]+\.*/./g; + if ($fullname =~ /^[a-zA-Z]+(\.[a-zA-Z]+)+$/) { + print "$fullname: $name\n"; + } else { + print "# $fullname: $name\n"; + } +}; + +endpwent; Property changes on: vendor/sendmail/dist-old/usr.sbin/sendmail/contrib/passwd-to-alias.pl ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: vendor/sendmail/dist-old/usr.sbin/sendmail/doc/op/op.me =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/doc/op/op.me (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/doc/op/op.me (revision 26986) @@ -1,8187 +1,8183 @@ .\" Copyright (c) 1983, 1995 Eric P. Allman .\" Copyright (c) 1983, 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. .\" -.\" @(#)op.me 8.103 (Berkeley) 12/13/96 +.\" @(#)op.me 8.104 (Berkeley) 3/10/97 .\" .\" eqn op.me | pic | troff -me .eh 'SMM:08-%''Sendmail Installation and Operation Guide' .oh 'Sendmail Installation and Operation Guide''SMM:08-%' .\" SD is lib if sendmail is installed in /usr/lib, sbin if in /usr/sbin .ds SD sbin .\" SB is bin if newaliases/mailq are installed in /usr/bin, ucb if in /usr/ucb .ds SB bin .nr si 3n .de $0 .(x .in \\$3u*3n .ti -3n \\$2. \\$1 .)x .. .de $C .(x .in 0 \\$1 \\$2. \\$3 .)x .. .sc .+c .(l C .sz 16 .b SENDMAIL .sz 12 .sp .b "INSTALLATION AND OPERATION GUIDE" .sz 10 .sp .r Eric Allman eric@Sendmail.ORG .sp -Version 8.103 +Version 8.104 .sp For Sendmail Version 8.8 .)l .sp 2 .pp .i Sendmail implements a general purpose internetwork mail routing facility under the UNIX\(rg operating system. It is not tied to any one transport protocol \*- its function may be likened to a crossbar switch, relaying messages from one domain into another. In the process, it can do a limited amount of message header editing to put the message into a format that is appropriate for the receiving domain. All of this is done under the control of a configuration file. .pp Due to the requirements of flexibility for .i sendmail , the configuration file can seem somewhat unapproachable. However, there are only a few basic configurations for most sites, for which standard configuration files have been supplied. Most other configurations can be built by adjusting an existing configuration files incrementally. .pp .i Sendmail is based on RFC821 (Simple Mail Transport Protocol), RFC822 (Internet Mail Format Protocol), RFC1123 (Internet Host Requirements), RFC1521 (MIME), RFC1651 (SMTP Service Extensions), RFC1891 (SMTP Delivery Status Notifications), RFC1892 (Multipart/Report), RFC1893 (Mail System Status Codes), RFC1894 (Delivery Status Notifications), and RFC1985 (SMTP Service Extension for Remote Message Queue Starting). However, since .i sendmail is designed to work in a wider world, in many cases it can be configured to exceed these protocols. These cases are described herein. .pp Although .i sendmail is intended to run without the need for monitoring, it has a number of features that may be used to monitor or adjust the operation under unusual circumstances. These features are described. .pp Section one describes how to do a basic .i sendmail installation. Section two explains the day-to-day information you should know to maintain your mail system. If you have a relatively normal site, these two sections should contain sufficient information for you to install .i sendmail and keep it happy. Section three describes some parameters that may be safely tweaked. Section four has information regarding the command line arguments. Section five contains the nitty-gritty information about the configuration file. This section is for masochists and people who must write their own configuration file. Section six describes configuration that can be done at compile time. Section seven gives a brief description of differences in this version of .i sendmail . The appendixes give a brief but detailed explanation of a number of features not described in the rest of the paper. .pp .b WARNING: Several major changes were introduced in version 8.7. You should not attempt to use this document for prior versions of .i sendmail . .bp .rs .sp |4i .ce 2 This page intentionally left blank; replace it with a blank sheet for double-sided output. .bp 7 .sh 1 "BASIC INSTALLATION" .pp There are two basic steps to installing .i sendmail . The hard part is to build the configuration table. This is a file that .i sendmail reads when it starts up that describes the mailers it knows about, how to parse addresses, how to rewrite the message header, and the settings of various options. Although the configuration table is quite complex, a configuration can usually be built by adjusting an existing off-the-shelf configuration. The second part is actually doing the installation, i.e., creating the necessary files, etc. .pp The remainder of this section will describe the installation of .i sendmail assuming you can use one of the existing configurations and that the standard installation parameters are acceptable. All pathnames and examples are given from the root of the .i sendmail subtree, normally .i /usr/src/usr.\*(SD/sendmail on 4.4BSD. .pp If you are loading this off the tape, continue with the next section. If you have a running binary already on your system, you should probably skip to section 1.2. .sh 2 "Compiling Sendmail" .pp All .i sendmail source is in the .i src subdirectory. If you are running on a 4.4BSD system, compile by typing .q make . On other systems, you may have to make some other adjustments. On most systems, you can do the appropriate compilation by typing .(b sh makesendmail .)b This will leave the binary in an appropriately named subdirectory. It works for multiple object versions compiled out of the same directory. .sh 3 "Tweaking the Makefile" .pp .i Sendmail supports two different formats for the local (on disk) version of databases, notably the .i aliases database. At least one of these should be defined if at all possible. .nr ii 1i .ip NDBM The ``new DBM'' format, available on nearly all systems around today. This was the preferred format prior to 4.4BSD. It allows such complex things as multiple databases and closing a currently open database. .ip NEWDB The new database package from Berkeley. If you have this, use it. It allows long records, multiple open databases, real in-memory caching, and so forth. You can define this in conjunction with one of the other two; if you do, old databases are read, but when a new database is created it will be in NEWDB format. As a nasty hack, if you have NEWDB, NDBM, and NIS defined, and if the alias file name includes the substring .q /yp/ , .i sendmail will create both new and old versions of the alias file during a .i newalias command. This is required because the Sun NIS/YP system reads the DBM version of the alias file. It's ugly as sin, but it works. .lp If neither of these are defined, .i sendmail reads the alias file into memory on every invocation. This can be slow and should be avoided. There are also several methods for remote database access: .ip NIS Sun's Network Information Services (formerly YP). .ip NISPLUS Sun's NIS+ services. .ip NETINFO NeXT's NetInfo service. .ip HESIOD Hesiod service (from Athena). .lp Other compilation flags are set in conf.h and should be predefined for you unless you are porting to a new environment. .sh 3 "Compilation and installation" .pp After making the local system configuration described above, You should be able to compile and install the system. The script .q makesendmail is the best approach on most systems: .(b sh makesendmail .)b This will use .i uname (1) to select the correct Makefile for your environment. .pp You may be able to install using .(b sh makesendmail install .)b This should install the binary in /usr/\*(SD and create links from /usr/\*(SB/newaliases and /usr/\*(SB/mailq to /usr/\*(SD/sendmail. On 4.4BSD systems it will also format and install man pages. .sh 2 "Configuration Files" .pp .i Sendmail cannot operate without a configuration file. The configuration defines the mail delivery mechanisms understood at this site, how to access them, how to forward email to remote mail systems, and a number of tuning parameters. This configuration file is detailed in the later portion of this document. .pp The .i sendmail configuration can be daunting at first. The world is complex, and the mail configuration reflects that. The distribution includes an m4-based configuration package that hides a lot of the complexity. .pp These configuration files are simpler than old versions largely because the world has become simpler; in particular, text-based host files are officially eliminated, obviating the need to .q hide hosts behind a registered internet gateway. .pp These files also assume that most of your neighbors use domain-based UUCP addressing; that is, instead of naming hosts as .q host!user they will use .q host.domain!user . The configuration files can be customized to work around this, but it is more complex. .pp Our configuration files are processed by .i m4 to facilitate local customization; the directory .i cf of the .i sendmail distribution directory contains the source files. This directory contains several subdirectories: .nr ii 1i .ip cf Both site-dependent and site-independent descriptions of hosts. These can be literal host names (e.g., .q ucbvax.mc ) when the hosts are gateways or more general descriptions (such as .q "tcpproto.mc" as a general description of an SMTP-connected host or .q "uucpproto.mc" as a general description of a UUCP-connected host). Files ending .b \&.mc (``Master Configuration'') are the input descriptions; the output is in the corresponding .b \&.cf file. The general structure of these files is described below. .ip domain Site-dependent subdomain descriptions. These are tied to the way your organization wants to do addressing. For example, .b domain/cs.exposed.m4 is our description for hosts in the CS.Berkeley.EDU subdomain that want their individual hostname to be externally visible; .b domain/cs.hidden.m4 is the same except that the hostname is hidden (everything looks like it comes from CS.Berkeley.EDU). These are referenced using the .sm DOMAIN .b m4 macro in the .b \&.mc file. .ip feature Definitions of specific features that some particular host in your site might want. These are referenced using the .sm FEATURE .b m4 macro. An example feature is use_cw_file (which tells .i sendmail to read an /etc/sendmail.cw file on startup to find the set of local names). .ip hack Local hacks, referenced using the .sm HACK .b m4 macro. Try to avoid these. The point of having them here is to make it clear that they smell. .ip m4 Site-independent .i m4 (1) include files that have information common to all configuration files. This can be thought of as a .q #include directory. .ip mailer Definitions of mailers, referenced using the .sm MAILER .b m4 macro. The mailer types that are known in this distribution are fax, local, smtp, uucp, and usenet. For example, to include support for the UUCP-based mailers, use .q MAILER(uucp) . .ip ostype Definitions describing various operating system environments (such as the location of support files). These are referenced using the .sm OSTYPE .b m4 macro. .ip sh Shell files used by the .b m4 build process. You shouldn't have to mess with these. .ip siteconfig Local UUCP connectivity information. They normally contain lists of site information, for example: .(b SITE(contessa) SITE(hoptoad) SITE(nkainc) SITE(well) .)b They are referenced using the SITECONFIG macro: .(b SITECONFIG(site.config.file, name_of_site, X) .)b where .i X is the macro/class name to use. It can be U (indicating locally connected hosts) or one of W, X, or Y for up to three remote UUCP hubs. This directory has been supplanted by the mailertable feature; any new configurations should use that feature to do UUCP (and other) routing. .pp If you are in a new domain (e.g., a company), you will probably want to create a cf/domain file for your domain. This consists primarily of relay definitions: for example, Berkeley's domain definition defines relays for BitNET, CSNET, and UUCP. Of these, only the UUCP relay is particularly specific to Berkeley. All of these are internet-style domain names. Please check to make certain they are reasonable for your domain. .pp Subdomains at Berkeley are also represented in the cf/domain directory. For example, the domain cs-exposed is the Computer Science subdomain with the local hostname shown to other users; cs-hidden makes users appear to be from the CS.Berkeley.EDU subdomain (with no local host information included). You will probably have to update this directory to be appropriate for your domain. .pp You will have to use or create .b \&.mc files in the .i cf/cf subdirectory for your hosts. This is detailed in the cf/README file. .sh 2 "Details of Installation Files" .pp This subsection describes the files that comprise the .i sendmail installation. .sh 3 "/usr/\*(SD/sendmail" .pp The binary for .i sendmail is located in /usr/\*(SD\**. .(f \**This is usually /usr/sbin on 4.4BSD and newer systems; many systems install it in /usr/lib. I understand it is in /usr/ucblib on System V Release 4. .)f It should be setuid root. For security reasons, /, /usr, and /usr/\*(SD should be owned by root, mode 755\**. .(f \**Some vendors ship them owned by bin; this creates a security hole that is not actually related to .i sendmail . Other important directories that should have restrictive ownerships and permissions are /bin, /usr/bin, /etc, /usr/etc, /lib, and /usr/lib. .)f .sh 3 "/etc/sendmail.cf" .pp This is the configuration file for .i sendmail \**. .(f \**Actually, the pathname varies depending on the operating system; /etc is the preferred directory. Some older systems install it in .b /usr/lib/sendmail.cf , and I've also seen it in .b /usr/ucblib and .b /etc/mail . If you want to move this file, change .i src/conf.h . .)f This and /etc/sendmail.pid are the only non-library file names compiled into .i sendmail \**. .(f \**The system libraries can reference other files; in particular, system library subroutines that .i sendmail calls probably reference .i /etc/passwd and .i /etc/resolv.conf . .)f .pp The configuration file is normally created using the distribution files described above. If you have a particularly unusual system configuration you may need to create a special version. The format of this file is detailed in later sections of this document. .sh 3 "/usr/\*(SB/newaliases" .pp The .i newaliases command should just be a link to .i sendmail : .(b rm \-f /usr/\*(SB/newaliases ln \-s /usr/\*(SD/sendmail /usr/\*(SB/newaliases .)b This can be installed in whatever search path you prefer for your system. .sh 3 "/usr/\*(SB/hoststat" .pp The .i hoststat command should just be a link to .i sendmail , in a fashion similar to .i newaliases . This command lists the status of the last mail transaction with all remote hosts. It functions only when the .b HostStatusDirectory option is set. .sh 3 "/usr/\*(SB/purgestat" .pp This command is also a link to .i sendmail . It flushes all information that is stored in the .b HostStatusDirectory tree. .sh 3 "/var/spool/mqueue" .pp The directory .i /var/spool/mqueue should be created to hold the mail queue. This directory should be mode 700 and owned by root. .pp The actual path of this directory is defined in the .b Q option of the .i sendmail.cf file. .sh 3 "/var/spool/mqueue/.hoststat" .pp This is a typical value for the .b HostStatusDirectory option, containing one file per host that this sendmail has chatted with recently. It is normally a subdirectory of .i mqueue . .sh 3 "/etc/aliases*" .pp The system aliases are held in .q /etc/aliases . A sample is given in .q lib/aliases which includes some aliases which .i must be defined: .(b cp lib/aliases /etc/aliases .i "edit /etc/aliases" .)b You should extend this file with any aliases that are apropos to your system. .pp Normally .i sendmail looks at a version of these files maintained by the .i dbm \|(3) or .i db \|(3) routines. These are stored either in .q /etc/aliases.dir and .q /etc/aliases.pag or .q /etc/aliases.db depending on which database package you are using. These can initially be created as empty files, but they will have to be initialized promptly. These should be mode 644: .(b cp /dev/null /etc/aliases.dir cp /dev/null /etc/aliases.pag chmod 644 /etc/aliases.* newaliases .)b The .i db routines preset the mode reasonably, so this step can be skipped. The actual path of this file is defined in the .b A option of the .i sendmail.cf file. .sh 3 "/etc/rc" .pp It will be necessary to start up the .i sendmail daemon when your system reboots. This daemon performs two functions: it listens on the SMTP socket for connections (to receive mail from a remote system) and it processes the queue periodically to insure that mail gets delivered when hosts come up. .pp Add the following lines to .q /etc/rc (or .q /etc/rc.local as appropriate) in the area where it is starting up the daemons: .(b if [ \-f /usr/\*(SD/sendmail \-a \-f /etc/sendmail.cf ]; then (cd /var/spool/mqueue; rm \-f [lnx]f*) /usr/\*(SD/sendmail \-bd \-q30m & echo \-n ' sendmail' >/dev/console fi .)b The .q cd and .q rm commands insure that all lock files have been removed; extraneous lock files may be left around if the system goes down in the middle of processing a message. The line that actually invokes .i sendmail has two flags: .q \-bd causes it to listen on the SMTP port, and .q \-q30m causes it to run the queue every half hour. .pp Some people use a more complex startup script, removing zero length qf files and df files for which there is no qf file. For example, see Figure 1 for an example of a complex startup script. .(z .hl # remove zero length qf files for qffile in qf* do if [ \-r $qffile ] then if [ ! \-s $qffile ] then echo \-n " " > /dev/console rm \-f $qffile fi fi done # rename tf files to be qf if the qf does not exist for tffile in tf* do qffile=`echo $tffile | sed 's/t/q/'` if [ \-r $tffile \-a ! \-f $qffile ] then echo \-n " " > /dev/console mv $tffile $qffile else echo \-n " " > /dev/console rm \-f $tffile fi done # remove df files with no corresponding qf files for dffile in df* do qffile=`echo $dffile | sed 's/d/q/'` if [ \-r $dffile \-a ! \-f $qffile ] then echo \-n " " > /dev/console mv $dffile `echo $dffile | sed 's/d/D/'` fi done # announce files that have been saved during disaster recovery for xffile in [A-Z]f* do echo \-n " " > /dev/console done .sp .ce Figure 1 \(em A complex startup script .hl .)z .pp If you are not running a version of UNIX that supports Berkeley TCP/IP, do not include the .b \-bd flag. .sh 3 "/usr/lib/sendmail.hf" .pp This is the help file used by the SMTP .b HELP command. It should be copied from .q lib/sendmail.hf : .(b cp lib/sendmail.hf /usr/lib .)b The actual path of this file is defined in the .b H option of the .i sendmail.cf file. .sh 3 "/etc/sendmail.st" .pp If you wish to collect statistics about your mail traffic, you should create the file .q /etc/sendmail.st : .(b cp /dev/null /etc/sendmail.st chmod 666 /etc/sendmail.st .)b This file does not grow. It is printed with the program .q mailstats/mailstats.c. The actual path of this file is defined in the .b S option of the .i sendmail.cf file. .sh 3 "/usr/\*(SB/mailq" .pp If .i sendmail is invoked as .q mailq, it will simulate the .b \-bp flag (i.e., .i sendmail will print the contents of the mail queue; see below). This should be a link to /usr/\*(SD/sendmail. .sh 1 "NORMAL OPERATIONS" .sh 2 "The System Log" .pp The system log is supported by the .i syslogd \|(8) program. All messages from .i sendmail are logged under the .sm LOG_MAIL facility\**. .(f \**Except on Ultrix, which does not support facilities in the syslog. .)f .sh 3 "Format" .pp Each line in the system log consists of a timestamp, the name of the machine that generated it (for logging from several machines over the local area network), the word .q sendmail: , and a message\**. .(f \**This format may vary slightly if your vendor has changed the syntax. .)f Most messages are a sequence of .i name \c =\c .i value pairs. .pp The two most common lines are logged when a message is processed. The first logs the receipt of a message; there will be exactly one of these per message. Some fields may be omitted if they do not contain interesting information. Fields are: .ip from The envelope sender address. .ip size The size of the message in bytes. .ip class The class (i.e., numeric precedence) of the message. .ip pri The initial message priority (used for queue sorting). .ip nrcpts The number of envelope recipients for this message (after aliasing and forwarding). .ip msgid The message id of the message (from the header). .ip proto The protocol used to receive this message (e.g., ESMTP or UUCP) .ip relay The machine from which it was received. .lp There is also one line logged per delivery attempt (so there can be several per message if delivery is deferred or there are multiple recipients). Fields are: .ip to A comma-separated list of the recipients to this mailer. .ip ctladdr The ``controlling user'', that is, the name of the user whose credentials we use for delivery. .ip delay The total delay between the time this message was received and the time it was delivered. .ip xdelay The amount of time needed in this delivery attempt (normally indicative of the speed of the connection). .ip mailer The name of the mailer used to deliver to this recipient. .ip relay The name of the host that actually accepted (or rejected) this recipient. .ip stat The delivery status. .lp Not all fields are present in all messages; for example, the relay is not listed for local deliveries. .sh 3 "Levels" .pp If you have .i syslogd \|(8) or an equivalent installed, you will be able to do logging. There is a large amount of information that can be logged. The log is arranged as a succession of levels. At the lowest level only extremely strange situations are logged. At the highest level, even the most mundane and uninteresting events are recorded for posterity. As a convention, log levels under ten are considered generally .q useful; log levels above 64 are reserved for debugging purposes. Levels from 11\-64 are reserved for verbose information that some sites might want. .pp A complete description of the log levels is given in section .\" XREF 4.6. .sh 2 "Dumping State" .pp You can ask .i sendmail to log a dump of the open files and the connection cache by sending it a .sm SIGUSR1 signal. The results are logged at .sm LOG_DEBUG priority. .sh 2 "The Mail Queue" .pp Sometimes a host cannot handle a message immediately. For example, it may be down or overloaded, causing it to refuse connections. The sending host is then expected to save this message in its mail queue and attempt to deliver it later. .pp Under normal conditions the mail queue will be processed transparently. However, you may find that manual intervention is sometimes necessary. For example, if a major host is down for a period of time the queue may become clogged. Although .i sendmail ought to recover gracefully when the host comes up, you may find performance unacceptably bad in the meantime. .sh 3 "Printing the queue" .pp The contents of the queue can be printed using the .i mailq command (or by specifying the .b \-bp flag to .i sendmail ): .(b mailq .)b This will produce a listing of the queue id's, the size of the message, the date the message entered the queue, and the sender and recipients. .sh 3 "Forcing the queue" .pp .i Sendmail should run the queue automatically at intervals. The algorithm is to read and sort the queue, and then to attempt to process all jobs in order. When it attempts to run the job, .i sendmail first checks to see if the job is locked. If so, it ignores the job. .pp There is no attempt to insure that only one queue processor exists at any time, since there is no guarantee that a job cannot take forever to process (however, .i sendmail does include heuristics to try to abort jobs that are taking absurd amounts of time; technically, this violates RFC 821, but is blessed by RFC 1123). Due to the locking algorithm, it is impossible for one job to freeze the entire queue. However, an uncooperative recipient host or a program recipient that never returns can accumulate many processes in your system. Unfortunately, there is no completely general way to solve this. .pp In some cases, you may find that a major host going down for a couple of days may create a prohibitively large queue. This will result in .i sendmail spending an inordinate amount of time sorting the queue. This situation can be fixed by moving the queue to a temporary place and creating a new queue. The old queue can be run later when the offending host returns to service. .pp To do this, it is acceptable to move the entire queue directory: .(b cd /var/spool mv mqueue omqueue; mkdir mqueue; chmod 700 mqueue .)b You should then kill the existing daemon (since it will still be processing in the old queue directory) and create a new daemon. .pp To run the old mail queue, run the following command: .(b /usr/\*(SD/sendmail \-oQ/var/spool/omqueue \-q .)b The .b \-oQ flag specifies an alternate queue directory and the .b \-q flag says to just run every job in the queue. If you have a tendency toward voyeurism, you can use the .b \-v flag to watch what is going on. .pp When the queue is finally emptied, you can remove the directory: .(b rmdir /var/spool/omqueue .)b .sh 2 "Disk Based Connection Information" .pp .i Sendmail stores a large amount of information about each remote system it has connected to in memory. It is now possible to preserve some of this information on disk as well, by using the .b HostStatusDirectory option, so that it may be shared between several invocations of .i sendmail . This allows mail to be queued immediately or skipped during a queue run if there has been a recent failure in connecting to a remote machine. .pp Additionally enabling .b SingleThreadDelivery has the added effect of single-threading mail delivery to a destination. This can be quite helpful if the remote machine is running an SMTP server that is easily overloaded or cannot accept more than a single connection at a time, but can cause some messages to be punted to a future queue run. It also applies to .i all hosts, so setting this because you have one machine on site that runs some software that is easily overrun can cause mail to other hosts to be slowed down. If this option is set, you probably want to set the .b MinQueueAge option as well and run the queue fairly frequently; this will cause hosts that are skipped because another .i sendmail instance is talking to it to be tried again soon. .pp The disk based host information is stored in a subdirectory of of the .b mqueue directory called .b \&.hoststat \**. .(f \**This is the usual value of the .b HostStatusDirectory option; it can, of course, go anywhere you like in your filesystem. .)f Removing this directory and its subdirectories has an effect similar to the .i purgestat command and is completely safe. The information in these directories can be perused with the .i hoststat command, which will indicate the host name, the last access, and the status of that access. An asterisk in the left most column indicates that a .i sendmail process currently has the host locked for mail delivery. .pp The disk based connection information is treated the same way as memory based connection information for the purpose of timeouts. By default, information about host failures is valid for 30 minutes. This can be adjusted with the .b Timeout.hoststatus option. .pp The connection information stored on disk may be purged at any time with the .i purgestat command or by invoking sendmail with the .b \-bH switch. The connection information may be viewed with the .i hoststat command or by invoking sendmail with the .b \-bh switch. .sh 2 "The Service Switch" .pp The implementation of certain system services such as host and user name lookup is controlled by the service switch. If the host operating system supports such a switch .i sendmail will use the native version. Ultrix, Solaris, and DEC OSF/1 are examples of such systems. .pp If the underlying operating system does not support a service switch (e.g., SunOS, HP-UX, BSD) then .i sendmail will provide a stub implementation. The .b ServiceSwitchFile option points to the name of a file that has the service definitions Each line has the name of a service and the possible implementations of that service. For example, the file: .(b hosts dns files nis aliases files nis .)b will ask .i sendmail to look for hosts in the Domain Name System first. If the requested host name is not found, it tries local files, and if that fails it tries NIS. Similarly, when looking for aliases it will try the local files first followed by NIS. .pp Service switches are not completely integrated. For example, despite the fact that the host entry listed in the above example specifies to look in NIS, on SunOS this won't happen because the system implementation of .i gethostbyname \|(3) doesn't understand this. If there is enough demand .i sendmail may reimplement .i gethostbyname \|(3), .i gethostbyaddr \|(3), .i getpwent \|(3), and the other system routines that would be necessary to make this work seamlessly. .sh 2 "The Alias Database" .pp The alias database exists in two forms. One is a text form, maintained in the file .i /etc/aliases. The aliases are of the form .(b name: name1, name2, ... .)b Only local names may be aliased; e.g., .(b eric@prep.ai.MIT.EDU: eric@CS.Berkeley.EDU .)b will not have the desired effect (except on prep.ai.MIT.EDU, and they probably don't want me)\**. .(f \**Actually, any mailer that has the `A' mailer flag set will permit aliasing; this is normally limited to the local mailer. .)f Aliases may be continued by starting any continuation lines with a space or a tab. Blank lines and lines beginning with a sharp sign (\c .q # ) are comments. .pp The second form is processed by the .i ndbm \|(3)\** .(f \**The .i gdbm package probably works as well. .)f or .i db \|(3) library. This form is in the files .i /etc/aliases.dir and .i /etc/aliases.pag. This is the form that .i sendmail actually uses to resolve aliases. This technique is used to improve performance. .pp The control of search order is actually set by the service switch. Essentially, the entry .(b OAswitch:aliases .)b is always added as the first alias entry; also, the first alias file name without a class (e.g., without .q nis: on the front) will be used as the name of the file for a ``files'' entry in the aliases switch. For example, if the configuration file contains .(b OA/etc/aliases .)b and the service switch contains .(b aliases nis files nisplus .)b then aliases will first be searched in the NIS database, then in /etc/aliases, then in the NIS+ database. .pp You can also use .sm NIS -based alias files. For example, the specification: .(b OA/etc/aliases OAnis:mail.aliases@my.nis.domain .)b will first search the /etc/aliases file and then the map named .q mail.aliases in .q my.nis.domain . Warning: if you build your own .sm NIS -based alias files, be sure to provide the .b \-l flag to .i makedbm (8) to map upper case letters in the keys to lower case; otherwise, aliases with upper case letters in their names won't match incoming addresses. .pp Additional flags can be added after the colon exactly like a .b K line \(em for example: .(b OAnis:\-N mail.aliases@my.nis.domain .)b will search the appropriate NIS map and always include null bytes in the key. .sh 3 "Rebuilding the alias database" .pp The DB or DBM version of the database may be rebuilt explicitly by executing the command .(b newaliases .)b This is equivalent to giving .i sendmail the .b \-bi flag: .(b /usr/\*(SD/sendmail \-bi .)b .pp If the .b RebuildAliases (old .b D ) option is specified in the configuration, .i sendmail will rebuild the alias database automatically if possible when it is out of date. Auto-rebuild can be dangerous on heavily loaded machines with large alias files; if it might take more than the rebuild timeout (option .b AliasWait , old .b a , which is normally five minutes) to rebuild the database, there is a chance that several processes will start the rebuild process simultaneously. .pp If you have multiple aliases databases specified, the .b \-bi flag rebuilds all the database types it understands (for example, it can rebuild NDBM databases but not NIS databases). .sh 3 "Potential problems" .pp There are a number of problems that can occur with the alias database. They all result from a .i sendmail process accessing the DBM version while it is only partially built. This can happen under two circumstances: One process accesses the database while another process is rebuilding it, or the process rebuilding the database dies (due to being killed or a system crash) before completing the rebuild. .pp Sendmail has three techniques to try to relieve these problems. First, it ignores interrupts while rebuilding the database; this avoids the problem of someone aborting the process leaving a partially rebuilt database. Second, it locks the database source file during the rebuild \(em but that may not work over NFS or if the file is unwritable. Third, at the end of the rebuild it adds an alias of the form .(b @: @ .)b (which is not normally legal). Before .i sendmail will access the database, it checks to insure that this entry exists\**. .(f \**The .b AliasWait option is required in the configuration for this action to occur. This should normally be specified. .)f .sh 3 "List owners" .pp If an error occurs on sending to a certain address, say .q \fIx\fP , .i sendmail will look for an alias of the form .q owner-\fIx\fP to receive the errors. This is typically useful for a mailing list where the submitter of the list has no control over the maintenance of the list itself; in this case the list maintainer would be the owner of the list. For example: .(b unix-wizards: eric@ucbarpa, wnj@monet, nosuchuser, sam@matisse owner-unix-wizards: unix-wizards-request unix-wizards-request: eric@ucbarpa .)b would cause .q eric@ucbarpa to get the error that will occur when someone sends to unix-wizards due to the inclusion of .q nosuchuser on the list. .pp List owners also cause the envelope sender address to be modified. The contents of the owner alias are used if they point to a single user, otherwise the name of the alias itself is used. For this reason, and to obey Internet conventions, the .q owner- address normally points at the .q -request address; this causes messages to go out with the typical Internet convention of using ``\c .i list -request'' as the return address. .sh 2 "User Information Database" .pp If you have a version of .i sendmail with the user information database compiled in, and you have specified one or more databases using the .b U option, the databases will be searched for a .i user :maildrop entry. If found, the mail will be sent to the specified address. .sh 2 "Per-User Forwarding (.forward Files)" .pp As an alternative to the alias database, any user may put a file with the name .q .forward in his or her home directory. If this file exists, .i sendmail redirects mail for that user to the list of addresses listed in the .forward file. For example, if the home directory for user .q mckusick has a .forward file with contents: .(b mckusick@ernie kirk@calder .)b then any mail arriving for .q mckusick will be redirected to the specified accounts. .pp Actually, the configuration file defines a sequence of filenames to check. By default, this is the user's .forward file, but can be defined to be more generally using the .b J option. If you change this, you will have to inform your user base of the change; \&.forward is pretty well incorporated into the collective subconscious. .sh 2 "Special Header Lines" .pp Several header lines have special interpretations defined by the configuration file. Others have interpretations built into .i sendmail that cannot be changed without changing the code. These builtins are described here. .sh 3 "Errors-To:" .pp If errors occur anywhere during processing, this header will cause error messages to go to the listed addresses. This is intended for mailing lists. .pp The Errors-To: header was created in the bad old days when UUCP didn't understand the distinction between an envelope and a header; this was a hack to provide what should now be passed as the envelope sender address. It should go away. It is only used if the .b UseErrorsTo option is set. .pp The Errors-To: header is official deprecated and will go away in a future release. .sh 3 "Apparently-To:" .pp RFC 822 requires at least one recipient field (To:, Cc:, or Bcc: line) in every message. If a message comes in with no recipients listed in the message then .i sendmail will adjust the header based on the .q NoRecipientAction option. One of the possible actions is to add an .q "Apparently-To:" header line for any recipients it is aware of. This is not put in as a standard recipient line to warn any recipients that the list is not complete. .pp The Apparently-To: header is non-standard and is deprecated. .sh 3 "Precedence" .pp The Precedence: header can be used as a crude control of message priority. It tweaks the sort order in the queue and can be configured to change the message timeout values. .sh 2 "IDENT Protocol Support" .pp .i Sendmail supports the IDENT protocol as defined in RFC 1413. Although this enhances identification of the author of an email message by doing a ``call back'' to the originating system to include the owner of a particular TCP connection in the audit trail it is in no sense perfect; a determined forger can easily spoof the IDENT protocol. The following description is excerpted from RFC 1413: .ba +5 .lp 6. Security Considerations .lp The information returned by this protocol is at most as trustworthy as the host providing it OR the organization operating the host. For example, a PC in an open lab has few if any controls on it to prevent a user from having this protocol return any identifier the user wants. Likewise, if the host has been compromised the information returned may be completely erroneous and misleading. .lp The Identification Protocol is not intended as an authorization or access control protocol. At best, it provides some additional auditing information with respect to TCP connections. At worst, it can provide misleading, incorrect, or maliciously incorrect information. .lp The use of the information returned by this protocol for other than auditing is strongly discouraged. Specifically, using Identification Protocol information to make access control decisions - either as the primary method (i.e., no other checks) or as an adjunct to other methods may result in a weakening of normal host security. .lp An Identification server may reveal information about users, entities, objects or processes which might normally be considered private. An Identification server provides service which is a rough analog of the CallerID services provided by some phone companies and many of the same privacy considerations and arguments that apply to the CallerID service apply to Identification. If you wouldn't run a "finger" server due to privacy considerations you may not want to run this protocol. .ba .lp In some cases your system may not work properly with IDENT support due to a bug in the TCP/IP implementation. The symptoms will be that for some hosts the SMTP connection will be closed almost immediately. If this is true or if you do not want to use IDENT, you should set the IDENT timeout to zero; this will disable the IDENT protocol. .sh 1 "ARGUMENTS" .pp The complete list of arguments to .i sendmail is described in detail in Appendix A. Some important arguments are described here. .sh 2 "Queue Interval" .pp The amount of time between forking a process to run through the queue is defined by the .b \-q flag. If you run with delivery mode set to .b i or .b b this can be relatively large, since it will only be relevant when a host that was down comes back up. If you run in .b q mode it should be relatively short, since it defines the maximum amount of time that a message may sit in the queue. (See also the MinQueueAge option.) .pp RFC 1123 section 5.3.1.1 says that this value should be at least 30 minutes (although that probably doesn't make sense if you use ``queue-only'' mode). .sh 2 "Daemon Mode" .pp If you allow incoming mail over an IPC connection, you should have a daemon running. This should be set by your .i /etc/rc file using the .b \-bd flag. The .b \-bd flag and the .b \-q flag may be combined in one call: .(b /usr/\*(SD/sendmail \-bd \-q30m .)b .pp An alternative approach is to invoke sendmail from .i inetd (8) (use the .b \-bs flag to ask sendmail to speak SMTP on its standard input and output). This works and allows you to wrap .i sendmail in a TCP wrapper program, but may be a bit slower since the configuration file has to be re-read on every message that comes in. If you do this, you still need to have a .i sendmail running to flush the queue: .(b /usr/\*(SD/sendmail \-q30m .)b .sh 2 "Forcing the Queue" .pp In some cases you may find that the queue has gotten clogged for some reason. You can force a queue run using the .b \-q flag (with no value). It is entertaining to use the .b \-v flag (verbose) when this is done to watch what happens: .(b /usr/\*(SD/sendmail \-q \-v .)b .pp You can also limit the jobs to those with a particular queue identifier, sender, or recipient using one of the queue modifiers. For example, .q \-qRberkeley restricts the queue run to jobs that have the string .q berkeley somewhere in one of the recipient addresses. Similarly, .q \-qSstring limits the run to particular senders and .q \-qIstring limits it to particular queue identifiers. .sh 2 "Debugging" .pp There are a fairly large number of debug flags built into .i sendmail . Each debug flag has a number and a level, where higher levels means to print out more information. The convention is that levels greater than nine are .q absurd, i.e., they print out so much information that you wouldn't normally want to see them except for debugging that particular piece of code. Debug flags are set using the .b \-d option; the syntax is: .(b .ta \w'debug-option 'u debug-flag: \fB\-d\fP debug-list debug-list: debug-option [ , debug-option ]* debug-option: debug-range [ . debug-level ] debug-range: integer | integer \- integer debug-level: integer .)b where spaces are for reading ease only. For example, .(b \-d12 Set flag 12 to level 1 \-d12.3 Set flag 12 to level 3 \-d3\-17 Set flags 3 through 17 to level 1 \-d3\-17.4 Set flags 3 through 17 to level 4 .)b For a complete list of the available debug flags you will have to look at the code (they are too dynamic to keep this documentation up to date). .sh 2 "Changing the Values of Options" .pp Options can be overridden using the .b \-o or .b \-O command line flags. For example, .(b /usr/\*(SD/sendmail \-oT2m .)b sets the .b T (timeout) option to two minutes for this run only; the equivalent line using the long option name is .(b /usr/\*(SD/sendmail -OTimeout.queuereturn=2m .)b .pp Some options have security implications. Sendmail allows you to set these, but relinquishes its setuid root permissions thereafter\**. .(f \**That is, it sets its effective uid to the real uid; thus, if you are executing as root, as from root's crontab file or during system startup the root permissions will still be honored. .)f .sh 2 "Trying a Different Configuration File" .pp An alternative configuration file can be specified using the .b \-C flag; for example, .(b /usr/\*(SD/sendmail \-Ctest.cf \-oQ/tmp/mqueue .)b uses the configuration file .i test.cf instead of the default .i /etc/sendmail.cf. If the .b \-C flag has no value it defaults to .i sendmail.cf in the current directory. .pp .i Sendmail gives up its setuid root permissions when you use this flag, so it is common to use a publicly writable directory (such as /tmp) as the spool directory (QueueDirectory or Q option) while testing. .sh 2 "Logging Traffic" .pp Many SMTP implementations do not fully implement the protocol. For example, some personal computer based SMTPs do not understand continuation lines in reply codes. These can be very hard to trace. If you suspect such a problem, you can set traffic logging using the .b \-X flag. For example, .(b /usr/\*(SD/sendmail \-X /tmp/traffic \-bd .)b will log all traffic in the file .i /tmp/traffic . .pp This logs a lot of data very quickly and should .b NEVER be used during normal operations. After starting up such a daemon, force the errant implementation to send a message to your host. All message traffic in and out of .i sendmail , including the incoming SMTP traffic, will be logged in this file. .sh 2 "Testing Configuration Files" .pp When you build a configuration table, you can do a certain amount of testing using the .q "test mode" of .i sendmail . For example, you could invoke .i sendmail as: .(b sendmail \-bt \-Ctest.cf .)b which would read the configuration file .q test.cf and enter test mode. In this mode, you enter lines of the form: .(b rwset address .)b where .i rwset is the rewriting set you want to use and .i address is an address to apply the set to. Test mode shows you the steps it takes as it proceeds, finally showing you the address it ends up with. You may use a comma separated list of rwsets for sequential application of rules to an input. For example: .(b 3,1,21,4 monet:bollard .)b first applies ruleset three to the input .q monet:bollard. Ruleset one is then applied to the output of ruleset three, followed similarly by rulesets twenty-one and four. .pp If you need more detail, you can also use the .q \-d21 flag to turn on more debugging. For example, .(b sendmail \-bt \-d21.99 .)b turns on an incredible amount of information; a single word address is probably going to print out several pages worth of information. .pp You should be warned that internally, .i sendmail applies ruleset 3 to all addresses. In test mode you will have to do that manually. For example, older versions allowed you to use .(b 0 bruce@broadcast.sony.com .)b This version requires that you use: .(b 3,0 bruce@broadcast.sony.com .)b .pp As of version 8.7, some other syntaxes are available in test mode: .bu \&.D\|x\|value defines macro .i x to have the indicated .i value . This is useful when debugging rules that use the .b $& \c .i x syntax. .bu \&.C\|c\|value adds the indicated .i value to class .i c . .bu \&.S\|ruleset dumps the contents of the indicated ruleset. .bu \-d\|debug-spec is equivalent to the command-line flag. .sh 2 "Persistent Host Status Information" .pp When .b HostStatusDirectory is enabled, information about the status of hosts is maintained on disk and can thus be shared between different instantiations of .i sendmail . The status of the last connection with each remote host may be viewed with the command: .(b sendmail \-bh .)b This information may be flushed with the command: .(b sendmail \-bH .)b Flushing the information prevents new .i sendmail processes from loading it, but does not prevent existing processes from using the status information that they already have. .sh 1 "TUNING" .pp There are a number of configuration parameters you may want to change, depending on the requirements of your site. Most of these are set using an option in the configuration file. For example, the line .q "O Timeout.queuereturn=5d" sets option .q Timeout.queuereturn to the value .q 5d (five days). .pp Most of these options have appropriate defaults for most sites. However, sites having very high mail loads may find they need to tune them as appropriate for their mail load. In particular, sites experiencing a large number of small messages, many of which are delivered to many recipients, may find that they need to adjust the parameters dealing with queue priorities. .pp All versions of .i sendmail prior to 8.7 had single character option names. As of 8.7, options have long (multi-character names). Although old short names are still accepted, most new options do not have short equivalents. .pp This section only describes the options you are most likely to want to tweak; read section .\"XREF 5 for more details. .sh 2 "Timeouts" .pp All time intervals are set using a scaled syntax. For example, .q 10m represents ten minutes, whereas .q 2h30m represents two and a half hours. The full set of scales is: .(b .ta 4n s seconds m minutes h hours d days w weeks .)b .sh 3 "Queue interval" .pp The argument to the .b \-q flag specifies how often a sub-daemon will run the queue. This is typically set to between fifteen minutes and one hour. RFC 1123 section 5.3.1.1 recommends that this be at least 30 minutes. .sh 3 "Read timeouts" .pp Timeouts all have option names .q Timeout.\fIsuboption\fP . The recognized .i suboption s, their default values, and the minimum values allowed by RFC 1123 section 5.3.2 are: .nr ii 1i .ip connect The time to wait for an SMTP connection to open (the .i connect (2) system call) [0, unspecified]. If zero, uses the kernel default. In no case can this option extend the timeout longer than the kernel provides, but it can shorten it. This is to get around kernels that provide an absurdly long connection timeout (90 minutes in one case). .ip iconnect The same as .i connect, except it applies only to the initial attempt to connect to a host for a given message [0, unspecified]. The concept is that this should be very short (a few seconds); hosts that are well connected and responsive will thus be serviced immediately. Hosts that are slow will not hold up other deliveries in the initial delivery attempt. .ip initial The wait for the initial 220 greeting message [5m, 5m]. .ip helo The wait for a reply from a HELO or EHLO command [5m, unspecified]. This may require a host name lookup, so five minutes is probably a reasonable minimum. .ip mail\(dg The wait for a reply from a MAIL command [10m, 5m]. .ip rcpt\(dg The wait for a reply from a RCPT command [1h, 5m]. This should be long because it could be pointing at a list that takes a long time to expand (see below). .ip datainit\(dg The wait for a reply from a DATA command [5m, 2m]. .ip datablock\(dg The wait for reading a data block (that is, the body of the message). [1h, 3m]. This should be long because it also applies to programs piping input to .i sendmail which have no guarantee of promptness. .ip datafinal\(dg The wait for a reply from the dot terminating a message. [1h, 10m]. If this is shorter than the time actually needed for the receiver to deliver the message, duplicates will be generated. This is discussed in RFC 1047. .ip rset The wait for a reply from a RSET command [5m, unspecified]. .ip quit The wait for a reply from a QUIT command [2m, unspecified]. .ip misc The wait for a reply from miscellaneous (but short) commands such as NOOP (no-operation) and VERB (go into verbose mode). [2m, unspecified]. .ip command\(dg In server SMTP, the time to wait for another command. [1h, 5m]. .ip ident The timeout waiting for a reply to an IDENT query [30s\**, unspecified]. .(f \**On some systems the default is zero to turn the protocol off entirely. .)f .lp For compatibility with old configuration files, if no .i suboption is specified, all the timeouts marked with \(dg are set to the indicated value. .pp Many of the RFC 1123 minimum values may well be too short. .i Sendmail was designed to the RFC 822 protocols, which did not specify read timeouts; hence, versions of .i sendmail prior to version 8.1 did not guarantee to reply to messages promptly. In particular, a .q RCPT command specifying a mailing list will expand and verify the entire list; a large list on a slow system may easily take more than five minutes\**. .(f \**This verification includes looking up every address with the name server; this involves network delays, and can in some cases can be considerable. .)f I recommend a one hour timeout \*- since a communications failure during the RCPT phase is rare, a long timeout is not onerous and may ultimately help reduce network load and duplicated messages. .pp For example, the lines: .(b O Timeout.command=25m O Timeout.datablock=3h .)b sets the server SMTP command timeout to 25 minutes and the input data block timeout to three hours. .sh 3 "Message timeouts" .pp After sitting in the queue for a few days, a message will time out. This is to insure that at least the sender is aware of the inability to send a message. The timeout is typically set to five days. It is sometimes considered convenient to also send a warning message if the message is in the queue longer than a few hours (assuming you normally have good connectivity; if your messages normally took several hours to send you wouldn't want to do this because it wouldn't be an unusual event). These timeouts are set using the .b Timeout.queuereturn and .b Timeout.queuewarn options in the configuration file (previously both were set using the .b T option). .pp Since these options are global, and since you can not know .i "a priori" how long another host outside your domain will be down, a five day timeout is recommended. This allows a recipient to fix the problem even if it occurs at the beginning of a long weekend. RFC 1123 section 5.3.1.1 says that this parameter should be ``at least 4\-5 days''. .pp The .b Timeout.queuewarn value can be piggybacked on the .b T option by indicating a time after which a warning message should be sent; the two timeouts are separated by a slash. For example, the line .(b OT5d/4h .)b causes email to fail after five days, but a warning message will be sent after four hours. This should be large enough that the message will have been tried several times. .sh 2 "Forking During Queue Runs" .pp By setting the .b ForkEachJob (\c .b Y ) option, .i sendmail will fork before each individual message while running the queue. This will prevent .i sendmail from consuming large amounts of memory, so it may be useful in memory-poor environments. However, if the .b ForkEachJob option is not set, .i sendmail will keep track of hosts that are down during a queue run, which can improve performance dramatically. .pp If the .b ForkEachJob option is set, .i sendmail can not use connection caching. .sh 2 "Queue Priorities" .pp Every message is assigned a priority when it is first instantiated, consisting of the message size (in bytes) offset by the message class (which is determined from the Precedence: header) times the .q "work class factor" and the number of recipients times the .q "work recipient factor." The priority is used to order the queue. Higher numbers for the priority mean that the message will be processed later when running the queue. .pp The message size is included so that large messages are penalized relative to small messages. The message class allows users to send .q "high priority" messages by including a .q Precedence: field in their message; the value of this field is looked up in the .b P lines of the configuration file. Since the number of recipients affects the amount of load a message presents to the system, this is also included into the priority. .pp The recipient and class factors can be set in the configuration file using the .b RecipientFactor (\c .b y ) and .b ClassFactor (\c .b z ) options respectively. They default to 30000 (for the recipient factor) and 1800 (for the class factor). The initial priority is: .EQ pri = msgsize - (class times bold ClassFactor) + (nrcpt times bold RecipientFactor) .EN (Remember, higher values for this parameter actually mean that the job will be treated with lower priority.) .pp The priority of a job can also be adjusted each time it is processed (that is, each time an attempt is made to deliver it) using the .q "work time factor," set by the .b RetryFactor (\c .b Z ) option. This is added to the priority, so it normally decreases the precedence of the job, on the grounds that jobs that have failed many times will tend to fail again in the future. The .b RetryFactor option defaults to 90000. .sh 2 "Load Limiting" .pp .i Sendmail can be asked to queue (but not deliver) mail if the system load average gets too high using the .b QueueLA (\c .b x ) option. When the load average exceeds the value of the .b QueueLA option, the delivery mode is set to .b q (queue only) if the .b QueueFactor (\c .b q ) option divided by the difference in the current load average and the .b QueueLA option plus one exceeds the priority of the message \(em that is, the message is queued iff: .EQ pri > { bold QueueFactor } over { LA - { bold QueueLA } + 1 } .EN The .b QueueFactor option defaults to 600000, so each point of load average is worth 600000 priority points (as described above). .pp For drastic cases, the .b RefuseLA (\c .b X ) option defines a load average at which .i sendmail will refuse to accept network connections. Locally generated mail (including incoming UUCP mail) is still accepted. .sh 2 "Delivery Mode" .pp There are a number of delivery modes that .i sendmail can operate in, set by the .b DeliveryMode (\c .b d ) configuration option. These modes specify how quickly mail will be delivered. Legal modes are: .(b .ta 4n i deliver interactively (synchronously) b deliver in background (asynchronously) q queue only (don't deliver) d defer delvery attempts (don't deliver) .)b There are tradeoffs. Mode .q i gives the sender the quickest feedback, but may slow down some mailers and is hardly ever necessary. Mode .q b delivers promptly but can cause large numbers of processes if you have a mailer that takes a long time to deliver a message. Mode .q q minimizes the load on your machine, but means that delivery may be delayed for up to the queue interval. Mode .q d is identical to mode .q q except that it also prevents all the early map lookups from working; it is intended for ``dial on demand'' sites where DNS lookups might cost real money. Some simple error messages (e.g., host unknown during the SMTP protocol) will be delayed using this mode. Mode .q b is the usual default. .pp If you run in mode .q q (queue only), .q d (defer), or .q b (deliver in background) .i sendmail will not expand aliases and follow .forward files upon initial receipt of the mail. This speeds up the response to RCPT commands. Mode .q i cannot be used by the SMTP server. .sh 2 "Log Level" .pp The level of logging can be set for .i sendmail . The default using a standard configuration table is level 9. The levels are as follows: .nr ii 0.5i .ip 0 No logging. .ip 1 Serious system failures and potential security problems. .ip 2 Lost communications (network problems) and protocol failures. .ip 3 Other serious failures. .ip 4 Minor failures. .ip 5 Message collection statistics. .ip 6 Creation of error messages, VRFY and EXPN commands. .ip 7 Delivery failures (host or user unknown, etc.). .ip 8 Successful deliveries and alias database rebuilds. .ip 9 Messages being deferred (due to a host being down, etc.). .ip 10 Database expansion (alias, forward, and userdb lookups). .ip 12 Log all incoming and outgoing SMTP commands. .ip 20 Logs attempts to run locked queue files. These are not errors, but can be useful to note if your queue appears to be clogged. .ip 30 Lost locks (only if using lockf instead of flock). .lp Additionally, values above 64 are reserved for extremely verbose debugging output. No normal site would ever set these. .sh 2 "File Modes" .pp The modes used for files depend on what functionality you want and the level of security you require. .sh 3 "To suid or not to suid?" .pp .i Sendmail can safely be made setuid to root. At the point where it is about to .i exec \|(2) a mailer, it checks to see if the userid is zero; if so, it resets the userid and groupid to a default (set by the .b u and .b g options). (This can be overridden by setting the .b S flag to the mailer for mailers that are trusted and must be called as root.) However, this will cause mail processing to be accounted (using .i sa \|(8)) to root rather than to the user sending the mail. .pp If you don't make .i sendmail setuid to root, it will still run but you lose a lot of functionality and a lot of privacy, since you'll have to make the queue directory world readable. You could also make .i sendmail setuid to some pseudo-user (e.g., create a user called .q sendmail and make .i sendmail setuid to that) which will fix the privacy problems but not the functionality issues. Also, this isn't a guarantee of security: for example, root occasionally sends mail, and the daemon often runs as root. .sh 3 "Should my alias database be writable?" .pp At Berkeley we have the alias database (/etc/aliases*) mode 644. While this is not as flexible as if the database were more 666, it avoids potential security problems with a globally writable database. .pp The database that .i sendmail actually used is represented by the two files .i aliases.dir and .i aliases.pag (both in /etc) (or .i aliases.db if you are running with the new Berkeley database primitives). The mode on these files should match the mode on /etc/aliases. If .i aliases is writable and the DBM files (\c .i aliases.dir and .i aliases.pag ) are not, users will be unable to reflect their desired changes through to the actual database. However, if .i aliases is read-only and the DBM files are writable, a slightly sophisticated user can arrange to steal mail anyway. .pp If your DBM files are not writable by the world or you do not have auto-rebuild enabled (with the .b AutoRebuildAliases option), then you must be careful to reconstruct the alias database each time you change the text version: .(b newaliases .)b If this step is ignored or forgotten any intended changes will also be ignored or forgotten. .sh 2 "Connection Caching" .pp When processing the queue, .i sendmail will try to keep the last few open connections open to avoid startup and shutdown costs. This only applies to IPC connections. .pp When trying to open a connection the cache is first searched. If an open connection is found, it is probed to see if it is still active by sending a .sm RSET command. It is not an error if this fails; instead, the connection is closed and reopened. .pp Two parameters control the connection cache. The .b ConnectionCacheSize (\c .b k ) option defines the number of simultaneous open connections that will be permitted. If it is set to zero, connections will be closed as quickly as possible. The default is one. This should be set as appropriate for your system size; it will limit the amount of system resources that .i sendmail will use during queue runs. Never set this higher than 4. .pp The .b ConnectionCacheTimeout (\c .b K ) option specifies the maximum time that any cached connection will be permitted to idle. When the idle time exceeds this value the connection is closed. This number should be small (under ten minutes) to prevent you from grabbing too many resources from other hosts. The default is five minutes. .sh 2 "Name Server Access" .pp Control of host address lookups is set by the .b hosts service entry in your service switch file. If you are on a system that has built-in service switch support (e.g., Ultrix, Solaris, or DEC OSF/1) then your system is probably configured properly already. Otherwise, .i sendmail will consult the file .b /etc/service.switch , which should be created. .i Sendmail only uses two entries: .b hosts and .b aliases . .pp However, some systems (such as SunOS) will do DNS lookups regardless of the setting of the service switch entry. In particular, the system routine .i gethostbyname (3) is used to look up host names, and many vendor versions try some combination of DNS, NIS, and file lookup in /etc/hosts without consulting a service switch. .i Sendmail makes no attempt to work around this problem, and the DNS lookup will be done anyway. If you do not have a nameserver configured at all, such as at a UUCP-only site, .i sendmail will get a .q "connection refused" message when it tries to connect to the name server. If the .b hosts switch entry has the service .q dns listed somewhere in the list, .i sendmail will interpret this to mean a temporary failure and will queue the mail for later processing; otherwise, it ignores the name server data. .pp The same technique is used to decide whether to do MX lookups. If you want MX support, you .i must have .q dns listed as a service in the .b hosts switch entry. .pp The .b ResolverOptions (\c .b I ) option allows you to tweak name server options. The command line takes a series of flags as documented in .i resolver (3) (with the leading .q RES_ deleted). Each can be preceded by an optional `+' or `\(mi'. For example, the line .(b O ResolverOptions=+AAONLY \(miDNSRCH .)b turns on the AAONLY (accept authoritative answers only) and turns off the DNSRCH (search the domain path) options. Most resolver libraries default DNSRCH, DEFNAMES, and RECURSE flags on and all others off. You can also include .q HasWildcardMX to specify that there is a wildcard MX record matching your domain; this turns off MX matching when canonifying names, which can lead to inappropriate canonifications. .pp Version level 1 configurations turn DNSRCH and DEFNAMES off when doing delivery lookups, but leave them on everywhere else. Version 8 of .i sendmail ignores them when doing canonification lookups (that is, when using $[ ... $]), and always does the search. If you don't want to do automatic name extension, don't call $[ ... $]. .pp The search rules for $[ ... $] are somewhat different than usual. If the name being looked up has at least one dot, it always tries the unmodified name first. If that fails, it tries the reduced search path, and lastly tries the unmodified name (but only for names without a dot, since names with a dot have already been tried). This allows names such as ``utc.CS'' to match the site in Czechoslovakia rather than the site in your local Computer Science department. It also prefers A and CNAME records over MX records \*- that is, if it finds an MX record it makes note of it, but keeps looking. This way, if you have a wildcard MX record matching your domain, it will not assume that all names match. .pp To completely turn off all name server access on systems without service switch support (such as SunOS) you will have to recompile with \-DNAMED_BIND=0 and remove \-lresolv from the list of libraries to be searched when linking. .sh 2 "Moving the Per-User Forward Files" .pp Some sites mount each user's home directory from a local disk on their workstation, so that local access is fast. However, the result is that .forward file lookups are slow. In some cases, mail can even be delivered on machines inappropriately because of a file server being down. The performance can be especially bad if you run the automounter. .pp The .b ForwardPath (\c .b J ) option allows you to set a path of forward files. For example, the config file line .(b O ForwardPath=/var/forward/$u:$z/.forward.$w .)b would first look for a file with the same name as the user's login in /var/forward; if that is not found (or is inaccessible) the file ``.forward.\c .i machinename '' in the user's home directory is searched. A truly perverse site could also search by sender by using $r, $s, or $f. .pp If you create a directory such as /var/forward, it should be mode 1777 (that is, the sticky bit should be set). Users should create the files mode 644. .sh 2 "Free Space" .pp On systems that have one of the system calls in the .i statfs (2) family (including .i statvfs and .i ustat ), you can specify a minimum number of free blocks on the queue filesystem using the .b MinFreeBlocks (\c .b b ) option. If there are fewer than the indicated number of blocks free on the filesystem on which the queue is mounted the SMTP server will reject mail with the 452 error code. This invites the SMTP client to try again later. .pp Beware of setting this option too high; it can cause rejection of email when that mail would be processed without difficulty. .sh 2 "Maximum Message Size" .pp To avoid overflowing your system with a large message, the .b MaxMessageSize option can be set to set an absolute limit on the size of any one message. This will be advertised in the ESMTP dialogue and checked during message collection. .sh 2 "Privacy Flags" .pp The .b PrivacyOptions (\c .b p ) option allows you to set certain ``privacy'' flags. Actually, many of them don't give you any extra privacy, rather just insisting that client SMTP servers use the HELO command before using certain commands or adding extra headers to indicate possible spoof attempts. .pp The option takes a series of flag names; the final privacy is the inclusive or of those flags. For example: .(b O PrivacyOptions=needmailhelo, noexpn .)b insists that the HELO or EHLO command be used before a MAIL command is accepted and disables the EXPN command. .pp The flags are detailed in section .\"XREF 5.6. .sh 2 "Send to Me Too" .pp Normally, .i sendmail deletes the (envelope) sender from any list expansions. For example, if .q matt sends to a list that contains .q matt as one of the members he won't get a copy of the message. If the .b \-m (me too) command line flag, or if the .b MeToo (\c .b m ) option is set in the configuration file, this behaviour is suppressed. Some sites like to run the .sm SMTP daemon with .b \-m . .sh 1 "THE WHOLE SCOOP ON THE CONFIGURATION FILE" .pp This section describes the configuration file in detail. .pp There is one point that should be made clear immediately: the syntax of the configuration file is designed to be reasonably easy to parse, since this is done every time .i sendmail starts up, rather than easy for a human to read or write. On the .q "future project" list is a configuration-file compiler. .pp The configuration file is organized as a series of lines, each of which begins with a single character defining the semantics for the rest of the line. Lines beginning with a space or a tab are continuation lines (although the semantics are not well defined in many places). Blank lines and lines beginning with a sharp symbol (`#') are comments. .sh 2 "R and S \*- Rewriting Rules" .pp The core of address parsing are the rewriting rules. These are an ordered production system. .i Sendmail scans through the set of rewriting rules looking for a match on the left hand side (LHS) of the rule. When a rule matches, the address is replaced by the right hand side (RHS) of the rule. .pp There are several sets of rewriting rules. Some of the rewriting sets are used internally and must have specific semantics. Other rewriting sets do not have specifically assigned semantics, and may be referenced by the mailer definitions or by other rewriting sets. .pp The syntax of these two commands are: .(b F .b S \c .i n .)b Sets the current ruleset being collected to .i n . If you begin a ruleset more than once it appends to the old definition. .(b F .b R \c .i lhs .i rhs .i comments .)b The fields must be separated by at least one tab character; there may be embedded spaces in the fields. The .i lhs is a pattern that is applied to the input. If it matches, the input is rewritten to the .i rhs . The .i comments are ignored. .pp Macro expansions of the form .b $ \c .i x are performed when the configuration file is read. Expansions of the form .b $& \c .i x are performed at run time using a somewhat less general algorithm. This for is intended only for referencing internally defined macros such as .b $h that are changed at runtime. .sh 3 "The left hand side" .pp The left hand side of rewriting rules contains a pattern. Normal words are simply matched directly. Metasyntax is introduced using a dollar sign. The metasymbols are: .(b .ta \w'\fB$=\fP\fIx\fP 'u \fB$*\fP Match zero or more tokens \fB$+\fP Match one or more tokens \fB$\-\fP Match exactly one token \fB$=\fP\fIx\fP Match any phrase in class \fIx\fP \fB$~\fP\fIx\fP Match any word not in class \fIx\fP .)b If any of these match, they are assigned to the symbol .b $ \c .i n for replacement on the right hand side, where .i n is the index in the LHS. For example, if the LHS: .(b $\-:$+ .)b is applied to the input: .(b UCBARPA:eric .)b the rule will match, and the values passed to the RHS will be: .(b .ta 4n $1 UCBARPA $2 eric .)b .pp Additionally, the LHS can include .b $@ to match zero tokens. This is .i not bound to a .b $ \c .i n on the RHS, and is normally only used when it stands alone in order to match the null input. .sh 3 "The right hand side" .pp When the left hand side of a rewriting rule matches, the input is deleted and replaced by the right hand side. Tokens are copied directly from the RHS unless they begin with a dollar sign. Metasymbols are: .(b .ta \w'$#mailer\0\0\0'u \fB$\fP\fIn\fP Substitute indefinite token \fIn\fP from LHS \fB$[\fP\fIname\fP\fB$]\fP Canonicalize \fIname\fP \fB$(\fP\fImap key\fP \fB$@\fP\fIarguments\fP \fB$:\fP\fIdefault\fP \fB$)\fP Generalized keyed mapping function \fB$>\fP\fIn\fP \*(lqCall\*(rq ruleset \fIn\fP \fB$#\fP\fImailer\fP Resolve to \fImailer\fP \fB$@\fP\fIhost\fP Specify \fIhost\fP \fB$:\fP\fIuser\fP Specify \fIuser\fP .)b .pp The .b $ \c .i n syntax substitutes the corresponding value from a .b $+ , .b $\- , .b $* , .b $= , or .b $~ match on the LHS. It may be used anywhere. .pp A host name enclosed between .b $[ and .b $] is looked up in the host database(s) and replaced by the canonical name\**. .(f \**This is actually completely equivalent to $(host \fIhostname\fP$). In particular, a .b $: default can be used. .)f For example, .q $[ftp$] might become .q ftp.CS.Berkeley.EDU and .q $[[128.32.130.2]$] would become .q vangogh.CS.Berkeley.EDU. .i Sendmail recognizes it's numeric IP address without calling the name server and replaces it with it's canonical name. .pp The .b $( \&... .b $) syntax is a more general form of lookup; it uses a named map instead of an implicit map. If no lookup is found, the indicated .i default is inserted; if no default is specified and no lookup matches, the value is left unchanged. The .i arguments are passed to the map for possible use. .pp The .b $> \c .i n syntax causes the remainder of the line to be substituted as usual and then passed as the argument to ruleset .i n . The final value of ruleset .i n then becomes the substitution for this rule. The .b $> syntax can only be used at the beginning of the right hand side; it can be only be preceded by .b $@ or .b $: . .pp The .b $# syntax should .i only be used in ruleset zero or a subroutine of ruleset zero. It causes evaluation of the ruleset to terminate immediately, and signals to .i sendmail that the address has completely resolved. The complete syntax is: .(b \fB$#\fP\fImailer\fP \fB$@\fP\fIhost\fP \fB$:\fP\fIuser\fP .)b This specifies the {mailer, host, user} 3-tuple necessary to direct the mailer. If the mailer is local the host part may be omitted\**. .(f \**You may want to use it for special .q "per user" extensions. For example, in the address .q jgm+foo@CMU.EDU ; the .q +foo part is not part of the user name, and is passed to the local mailer for local use. .)f The .i mailer must be a single word, but the .i host and .i user may be multi-part. If the .i mailer is the builtin IPC mailer, the .i host may be a colon-separated list of hosts that are searched in order for the first working address (exactly like MX records). The .i user is later rewritten by the mailer-specific envelope rewriting set and assigned to the .b $u macro. As a special case, if the mailer specified has the .b F=@ flag specified and the first character of the .b $: value is .q @ , the .q @ is stripped off, and a flag is set in the address descriptor that causes sendmail to not do ruleset 5 processing. .pp Normally, a rule that matches is retried, that is, the rule loops until it fails. A RHS may also be preceded by a .b $@ or a .b $: to change this behavior. A .b $@ prefix causes the ruleset to return with the remainder of the RHS as the value. A .b $: prefix causes the rule to terminate immediately, but the ruleset to continue; this can be used to avoid continued application of a rule. The prefix is stripped before continuing. .pp The .b $@ and .b $: prefixes may precede a .b $> spec; for example: .(b .ta 8n R$+ $: $>7 $1 .)b matches anything, passes that to ruleset seven, and continues; the .b $: is necessary to avoid an infinite loop. .pp Substitution occurs in the order described, that is, parameters from the LHS are substituted, hostnames are canonicalized, .q subroutines are called, and finally .b $# , .b $@ , and .b $: are processed. .sh 3 "Semantics of rewriting rule sets" .pp There are five rewriting sets that have specific semantics. Four of these are related as depicted by figure 1. .(z .hl .ie n \{\ .(c +---+ -->| 0 |-->resolved address / +---+ / +---+ +---+ / ---->| 1 |-->| S |-- +---+ / +---+ / +---+ +---+ \e +---+ addr-->| 3 |-->| D |-- --->| 4 |-->msg +---+ +---+ \e +---+ +---+ / +---+ --->| 2 |-->| R |-- +---+ +---+ .)c .\} .el .ie !"\*(.T"" \ \{\ .PS boxwid = 0.3i boxht = 0.3i movewid = 0.3i moveht = 0.3i linewid = 0.3i lineht = 0.3i box invis "addr"; arrow Box3: box "3" A1: arrow BoxD: box "D"; line; L1: Here C: [ C1: arrow; box "1"; arrow; box "S"; line; E1: Here move to C1 down 0.5; right C2: arrow; box "2"; arrow; box "R"; line; E2: Here ] with .w at L1 + (0.5, 0) move to C.e right 0.5 L4: arrow; box "4"; arrow; box invis "msg" line from L1 to C.C1 line from L1 to C.C2 line from C.E1 to L4 line from C.E2 to L4 move to BoxD.n up 0.6; right Box0: arrow; box "0" arrow; box invis "resolved address" width 1.3 line from 1/3 of the way between A1 and BoxD.w to Box0 .PE .\} .el .sp 2i .ce Figure 1 \*- Rewriting set semantics .(c D \*- sender domain addition S \*- mailer-specific sender rewriting R \*- mailer-specific recipient rewriting .)c .hl .)z .pp Ruleset three should turn the address into .q "canonical form." This form should have the basic syntax: .(b local-part@host-domain-spec .)b Ruleset three is applied by .i sendmail before doing anything with any address. .pp If no .q @ sign is specified, then the host-domain-spec .i may be appended (box .q D in Figure 1) from the sender address (if the .b C flag is set in the mailer definition corresponding to the .i sending mailer). .pp Ruleset zero is applied after ruleset three to addresses that are going to actually specify recipients. It must resolve to a .i "{mailer, host, user}" triple. The .i mailer must be defined in the mailer definitions from the configuration file. The .i host is defined into the .b $h macro for use in the argv expansion of the specified mailer. .pp Rulesets one and two are applied to all sender and recipient addresses respectively. They are applied before any specification in the mailer definition. They must never resolve. .pp Ruleset four is applied to all addresses in the message. It is typically used to translate internal to external form. .pp In addition, ruleset 5 is applied to all local addresses (specifically, those that resolve to a mailer with the `F=5' flag set) that do not have aliases. This allows a last minute hook for local names. .sh 3 "Ruleset hooks" .pp A few extra rulesets are defined as .q hooks that can be defined to get special features. They are all named rulesets. The .q check_* forms all give accept/reject status; falling off the end or returning normally is an accept, and resolving to $#error is a reject. .sh 4 "check_relay" .pp The .i check_relay ruleset is called after a connection is accepted. It is passed .(b client.host.name $| client.host.address .)b where .b $| is a metacharacter separating the two parts. This ruleset can reject connections from various locations. .sh 4 "check_mail" .pp The .i check_mail ruleset is passed the user name parameter of the .sm "SMTP MAIL" command. It can accept or reject the address. .sh 4 "check_rcpt" .pp The .i check_rcpt ruleset is passed the user name parameter of the .sm "SMTP RCPT" command. It can accept or reject the address. .sh 4 "check_compat" .pp The .i check_compat ruleset is passed .(b sender-address $| recipient-address .)b where .b $| is a metacharacter separating the addresses. It can accept or reject mail transfer between these two addresses much like the .i checkcompat() function. .sh 3 "IPC mailers" .pp Some special processing occurs if the ruleset zero resolves to an IPC mailer (that is, a mailer that has .q [IPC] listed as the Path in the .b M configuration line. The host name passed after .q $@ has MX expansion performed; this looks the name up in DNS to find alternate delivery sites. .pp The host name can also be provided as a dotted quad in square brackets; for example: .(b [128.32.149.78] .)b This causes direct conversion of the numeric value to a TCP/IP host address. .pp The host name passed in after the .q $@ may also be a colon-separated list of hosts. Each is separately MX expanded and the results are concatenated to make (essentially) one long MX list. The intent here is to create .q fake MX records that are not published in DNS for private internal networks. .pp As a final special case, the host name can be passed in as a text string in square brackets: .(b [ucbvax.berkeley.edu] .)b This form avoids the MX mapping. .b N.B.: .i This is intended only for situations where you have a network firewall or other host that will do special processing for all your mail, so that your MX record points to a gateway machine; this machine could then do direct delivery to machines within your local domain. Use of this feature directly violates RFC 1123 section 5.3.5: it should not be used lightly. .r .sh 2 "D \*- Define Macro" .pp Macros are named with a single character or with a word in {braces}. Single character names may be selected from the entire ASCII set, but user-defined macros should be selected from the set of upper case letters only. Lower case letters and special symbols are used internally. Long names beginning with a lower case letter or a punctuation character are reserved for use by sendmail, so user-defined long macro names should begin with an upper case letter. .pp The syntax for macro definitions is: .(b F .b D \c .i x\|val .)b where .i x is the name of the macro (which may be a single character or a word in braces) and .i val is the value it should have. There should be no spaces given that do not actually belong in the macro value. .pp Macros are interpolated using the construct .b $ \c .i x , where .i x is the name of the macro to be interpolated. This interpolation is done when the configuration file is read, except in .b M lines. The special construct .b $& \c .i x can be used in .b R lines to get deferred interpolation. .pp Conditionals can be specified using the syntax: .(b $?x text1 $| text2 $. .)b This interpolates .i text1 if the macro .b $x is set, and .i text2 otherwise. The .q else (\c .b $| ) clause may be omitted. .pp Lower case macro names are reserved to have special semantics, used to pass information in or out of .i sendmail , and special characters are reserved to provide conditionals, etc. Upper case names (that is, .b $A through .b $Z ) are specifically reserved for configuration file authors. .pp The following macros are defined and/or used internally by .i sendmail for interpolation into argv's for mailers or for other contexts. The ones marked \(dg are information passed into sendmail\**, .(f \**As of version 8.6, all of these macros have reasonable defaults. Previous versions required that they be defined. .)f the ones marked \(dd are information passed both in and out of sendmail, and the unmarked macros are passed out of sendmail but are not otherwise used internally. These macros are: .nr ii 5n .ip $a The origination date in RFC 822 format. This is extracted from the Date: line. .ip $b The current date in RFC 822 format. .ip $c The hop count. This is a count of the number of Received: lines plus the value of the .b \-h command line flag. .ip $d The current date in UNIX (ctime) format. .ip $e\(dg (Obsolete; use SmtpGreetingMessage option instead.) The SMTP entry message. This is printed out when SMTP starts up. The first word must be the .b $j macro as specified by RFC821. Defaults to .q "$j Sendmail $v ready at $b" . Commonly redefined to include the configuration version number, e.g., .q "$j Sendmail $v/$Z ready at $b" .ip $f The envelope sender (from) address. .ip $g The sender address relative to the recipient. For example, if .b $f is .q foo , .b $g will be .q host!foo , .q foo@host.domain , or whatever is appropriate for the receiving mailer. .ip $h The recipient host. This is set in ruleset 0 from the $# field of a parsed address. .ip $i The queue id, e.g., .q HAA12345 . .ip $j\(dd The \*(lqofficial\*(rq domain name for this site. This is fully qualified if the full qualification can be found. It .i must be redefined to be the fully qualified domain name if your system is not configured so that information can find it automatically. .ip $k The UUCP node name (from the uname system call). .ip $l\(dg (Obsolete; use UnixFromLine option instead.) The format of the UNIX from line. Unless you have changed the UNIX mailbox format, you should not change the default, which is .q "From $g $d" . .ip $m The domain part of the \fIgethostname\fP return value. Under normal circumstances, .b $j is equivalent to .b $w.$m . .ip $n\(dg The name of the daemon (for error messages). Defaults to .q MAILER-DAEMON . .ip $o\(dg (Obsolete: use OperatorChars option instead.) The set of \*(lqoperators\*(rq in addresses. A list of characters which will be considered tokens and which will separate tokens when doing parsing. For example, if .q @ were in the .b $o macro, then the input .q a@b would be scanned as three tokens: .q a, .q @, and .q b. Defaults to .q ".:@[]" , which is the minimum set necessary to do RFC 822 parsing; a richer set of operators is .q ".:%@!/[]" , which adds support for UUCP, the %-hack, and X.400 addresses. .ip $p Sendmail's process id. .ip $q\(dg Default format of sender address. The .b $q macro specifies how an address should appear in a message when it is defaulted. Defaults to .q "<$g>" . It is commonly redefined to be .q "$?x$x <$g>$|$g$." or .q "$g$?x ($x)$." , corresponding to the following two formats: .(b Eric Allman eric@CS.Berkeley.EDU (Eric Allman) .)b .i Sendmail properly quotes names that have special characters if the first form is used. .ip $r Protocol used to receive the message. Set from the .b \-p command line flag or by the SMTP server code. .ip $s Sender's host name. Set from the .b \-p command line flag or by the SMTP server code. .ip $t A numeric representation of the current time. .ip $u The recipient user. .ip $v The version number of the .i sendmail binary. .ip $w\(dd The hostname of this site. This is the root name of this host (but see below for caveats). .ip $x The full name of the sender. .ip $z The home directory of the recipient. .ip $_ The validated sender address. .ip ${bodytype} The message body type (7BIT or 8BITMIME), as determined from the envelope. .ip ${client_addr} The IP address of the SMTP client. Defined in the SMTP server only. .ip ${client_name} The host name of the SMTP client. Defined in the SMTP server only. .ip ${client_port} The port number of the SMTP client. Defined in the SMTP server only. .ip ${envid} The envelope id passed to sendmail as part of the envelope. .ip ${opMode} The current operation mode (from the .b \-b flag). .pp There are three types of dates that can be used. The .b $a and .b $b macros are in RFC 822 format; .b $a is the time as extracted from the .q Date: line of the message (if there was one), and .b $b is the current date and time (used for postmarks). If no .q Date: line is found in the incoming message, .b $a is set to the current time also. The .b $d macro is equivalent to the .b $b macro in UNIX (ctime) format. .pp The macros .b $w , .b $j , and .b $m are set to the identity of this host. .i Sendmail tries to find the fully qualified name of the host if at all possible; it does this by calling .i gethostname (2) to get the current hostname and then passing that to .i gethostbyname (3) which is supposed to return the canonical version of that host name.\** .(f \**For example, on some systems .i gethostname might return .q foo which would be mapped to .q foo.bar.com by .i gethostbyname . .)f Assuming this is successful, .b $j is set to the fully qualified name and .b $m is set to the domain part of the name (everything after the first dot). The .b $w macro is set to the first word (everything before the first dot) if you have a level 5 or higher configuration file; otherwise, it is set to the same value as .b $j . If the canonification is not successful, it is imperative that the config file set .b $j to the fully qualified domain name\**. .(f \**Older versions of sendmail didn't pre-define .b $j at all, so up until 8.6, config files .i always had to define .b $j . .)f .pp The .b $f macro is the id of the sender as originally determined; when mailing to a specific host the .b $g macro is set to the address of the sender .ul relative to the recipient. For example, if I send to .q bollard@matisse.CS.Berkeley.EDU from the machine .q vangogh.CS.Berkeley.EDU the .b $f macro will be .q eric and the .b $g macro will be .q eric@vangogh.CS.Berkeley.EDU. .pp The .b $x macro is set to the full name of the sender. This can be determined in several ways. It can be passed as flag to .i sendmail . It can be defined in the .sm NAME environment variable. The third choice is the value of the .q Full-Name: line in the header if it exists, and the fourth choice is the comment field of a .q From: line. If all of these fail, and if the message is being originated locally, the full name is looked up in the .i /etc/passwd file. .pp When sending, the .b $h , .b $u , and .b $z macros get set to the host, user, and home directory (if local) of the recipient. The first two are set from the .b $@ and .b $: part of the rewriting rules, respectively. .pp The .b $p and .b $t macros are used to create unique strings (e.g., for the .q Message-Id: field). The .b $i macro is set to the queue id on this host; if put into the timestamp line it can be extremely useful for tracking messages. The .b $v macro is set to be the version number of .i sendmail ; this is normally put in timestamps and has been proven extremely useful for debugging. .pp The .b $c field is set to the .q "hop count," i.e., the number of times this message has been processed. This can be determined by the .b \-h flag on the command line or by counting the timestamps in the message. .pp The .b $r and .b $s fields are set to the protocol used to communicate with .i sendmail and the sending hostname. They can be set together using the .b \-p command line flag or separately using the .b \-M or .b \-oM flags. .pp The .b $_ is set to a validated sender host name. If the sender is running an RFC 1413 compliant IDENT server and the receiver has the IDENT protocol turned on, it will include the user name on that host. .pp The .b ${client_name} , .b ${client_addr} , and .b ${client_port} macros are set to the name, address, and port number of the SMTP client who is invoking .i sendmail as a server. These can be used in the .i check_* rulesets (using the .b $& deferred evaluation form, of course!). .sh 2 "C and F \*- Define Classes" .pp Classes of phrases may be defined to match on the left hand side of rewriting rules, where a .q phrase is a sequence of characters that do not contain space characters. For example a class of all local names for this site might be created so that attempts to send to oneself can be eliminated. These can either be defined directly in the configuration file or read in from another file. Classes are named as a single letter or a word in {braces}. Class names beginning with lower case letters and special characters are reserved for system use. Classes defined in config files may be given names from the set of upper case letters for short names or beginning with an upper case letter for long names. .pp The syntax is: .(b F .b C \c .i c\|phrase1 .i phrase2... .br .b F \c .i c\|file .)b The first form defines the class .i c to match any of the named words. It is permissible to split them among multiple lines; for example, the two forms: .(b CHmonet ucbmonet .)b and .(b CHmonet CHucbmonet .)b are equivalent. The ``F'' form reads the elements of the class .i c from the named .i file . .pp Elements of classes can be accessed in rules using .b $= or .b $~ . The .b $~ (match entries not in class) only matches a single word; multi-word entries in the class are ignored in this context. .pp Some classes have internal meaning to .i sendmail : .nr ii 0.5i .\".ip $=b .\"A set of Content-Types that will not have the newline character .\"translated to CR-LF before encoding into base64 MIME. .\"The class can have major times .\"(e.g., .\".q image ) .\"or full types .\"(such as .\".q application/octet-stream ). .\"The class is initialized with .\".q application/octet-stream , .\".q image , .\".q audio , .\"and .\".q video . .ip $=e contains the Content-Transfer-Encodings that can be 8\(->7 bit encoded. It is predefined to contain .q 7bit , .q 8bit , and .q binary . .ip $=k set to be the same as .b $k , that is, the UUCP node name. .ip $=m set to the set of domains by which this host is known, initially just .b $m . .ip $=n can be set to the set of MIME body types that can never be eight to seven bit encoded. It defaults to .q multipart/signed . Message types .q message/* and .q multipart/* are never encoded directly. Multipart messages are always handled recursively. The handling of message/* messages are controlled by class .b $=s . .ip $=q A set of Content-Types that will never be encoded as base64 (if they have to be encoded, they will be encoded as quoted-printable). It can have primary types (e.g., .q text ) or full types (such as .q text/plain ). The class is initialized to have .q text/plain only. .ip $=s contains the set of subtypes of message that can be treated recursively. By default it contains only .q rfc822 . Other .q message/* types cannot be 8\(->7 bit encoded. If a message containing eight bit data is sent to a seven bit host, and that message cannot be encoded into seven bits, it will be stripped to 7 bits. .ip $=t set to the set of trusted users by the .b T configuration line. If you want to read trusted users from a file use .b Ft \c .i /file/name . .ip $=w set to be the set of all names this host is known by. This can be used to match local hostnames. .pp .i Sendmail can be compiled to allow a .i scanf (3) string on the .b F line. This lets you do simplistic parsing of text files. For example, to read all the user names in your system .i /etc/passwd file into a class, use .(b FL/etc/passwd %[^:] .)b which reads every line up to the first colon. .sh 2 "M \*- Define Mailer" .pp Programs and interfaces to mailers are defined in this line. The format is: .(b F .b M \c .i name , {\c .i field =\c .i value \|}* .)b where .i name is the name of the mailer (used internally only) and the .q field=name pairs define attributes of the mailer. Fields are: .(b .ta 1i Path The pathname of the mailer Flags Special flags for this mailer Sender Rewriting set(s) for sender addresses Recipient Rewriting set(s) for recipient addresses Argv An argument vector to pass to this mailer Eol The end-of-line string for this mailer Maxsize The maximum message length to this mailer Linelimit The maximum line length in the message body Directory The working directory for the mailer Userid The default user and group id to run as Nice The nice(2) increment for the mailer Charset The default character set for 8-bit characters Type The MTS type information (used for error messages) .)b Only the first character of the field name is checked. .pp The following flags may be set in the mailer description. Any other flags may be used freely to conditionally assign headers to messages destined for particular mailers. Flags marked with \(dg are not interpreted by the .i sendmail binary; these are the conventionally used to correlate to the flags portion of the .b H line. Flags marked with \(dd apply to the mailers for the sender address rather than the usual recipient mailers. .nr ii 4n .ip a Run Extended SMTP (ESMTP) protocol (defined in RFCs 1651, 1652, and 1653). This flag defaults on if the SMTP greeting message includes the word .q ESMTP . .ip A Look up the user part of the address in the alias database. Normally this is only set for local mailers. .ip b Force a blank line on the end of a message. This is intended to work around some stupid versions of /bin/mail that require a blank line, but do not provide it themselves. It would not normally be used on network mail. .ip c Do not include comments in addresses. This should only be used if you have to work around a remote mailer that gets confused by comments. This strips addresses of the form .q "Phrase
" or .q "address (Comment)" down to just .q address . .ip C\(dd If mail is .i received from a mailer with this flag set, any addresses in the header that do not have an at sign (\c .q @ ) after being rewritten by ruleset three will have the .q @domain clause from the sender envelope address tacked on. This allows mail with headers of the form: .(b From: usera@hosta To: userb@hostb, userc .)b to be rewritten as: .(b From: usera@hosta To: userb@hostb, userc@hosta .)b automatically. However, it doesn't really work reliably. .ip d Do not include angle brackets around route-address syntax addresses. This is useful on mailers that are going to pass addresses to a shell that might interpret angle brackets as I/O redirection. .ip D\(dg This mailer wants a .q Date: header line. .ip e This mailer is expensive to connect to, so try to avoid connecting normally; any necessary connection will occur during a queue run. .ip E Escape lines beginning with .q From in the message with a `>' sign. .ip f The mailer wants a .b \-f .i from flag, but only if this is a network forward operation (i.e., the mailer will give an error if the executing user does not have special permissions). .ip F\(dg This mailer wants a .q From: header line. .ip g Normally, .i sendmail sends internally generated email (e.g., error messages) using the null return address as required by RFC 1123. However, some mailers don't accept a null return address. If necessary, you can set the .b g flag to prevent .i sendmail from obeying the standards; error messages will be sent as from the MAILER-DAEMON (actually, the value of the .b $n macro). .ip h Upper case should be preserved in host names for this mailer. .ip I This mailer will be speaking SMTP to another .i sendmail \*- as such it can use special protocol features. This option is not required (i.e., if this option is omitted the transmission will still operate successfully, although perhaps not as efficiently as possible). .ip j Do User Database rewriting on recipients as well as senders. .ip k Normally when .i sendmail connects to a host via SMTP, it checks to make sure that this isn't accidently the same host name as might happen if .i sendmail is misconfigured or if a long-haul network interface is set in loopback mode. This flag disables the loopback check. It should only be used under very unusual circumstances. .ip K Currently unimplemented. Reserved for chunking. .ip l This mailer is local (i.e., final delivery will be performed). .ip L Limit the line lengths as specified in RFC821. This deprecated option should be replaced by the .b L= mail declaration. For historic reasons, the .b L flag also sets the .b 7 flag. .ip m This mailer can send to multiple users on the same host in one transaction. When a .b $u macro occurs in the .i argv part of the mailer definition, that field will be repeated as necessary for all qualifying users. .ip M\(dg This mailer wants a .q Message-Id: header line. .ip n Do not insert a UNIX-style .q From line on the front of the message. .ip o Always run as the owner of the recipient mailbox. Normally .i sendmail runs as the sender for locally generated mail or as .q daemon (actually, the user specified in the .b u option) when delivering network mail. The normal behaviour is required by most local mailers, which will not allow the envelope sender address to be set unless the mailer is running as daemon. This flag is ignored if the .b S flag is set. .ip p Use the route-addr style reverse-path in the SMTP .q "MAIL FROM:" command rather than just the return address; although this is required in RFC821 section 3.1, many hosts do not process reverse-paths properly. Reverse-paths are officially discouraged by RFC 1123. .ip P\(dg This mailer wants a .q Return-Path: line. .ip q When an address that resolves to this mailer is verified (SMTP VRFY command), generate 250 responses instead of 252 responses. This will imply that the address is local. .ip r Same as .b f , but sends a .b \-r flag. .ip R Open SMTP connections from a .q secure port. Secure ports aren't (secure, that is) except on UNIX machines, so it is unclear that this adds anything. .ip s Strip quote characters (" and \e) off of the address before calling the mailer. .ip S Don't reset the userid before calling the mailer. This would be used in a secure environment where .i sendmail ran as root. This could be used to avoid forged addresses. If the .b U= field is also specified, this flag causes the user id to always be set to that user and group (instead of leaving it as root). .ip u Upper case should be preserved in user names for this mailer. .ip U This mailer wants UUCP-style .q From lines with the ugly .q "remote from " on the end. .ip w The user must have a valid account on this machine, i.e., getpwnam must succeed. If not, the mail is bounced. This is required to get .q \&.forward capability. .ip x\(dg This mailer wants a .q Full-Name: header line. .ip X This mailer want to use the hidden dot algorithm as specified in RFC821; basically, any line beginning with a dot will have an extra dot prepended (to be stripped at the other end). This insures that lines in the message containing a dot will not terminate the message prematurely. .ip 0 Don't look up MX records for hosts sent via SMTP. .ip 3 Extend the list of characters converted to =XX notation when converting to Quoted-Printable to include those that don't map cleanly between ASCII and EBCDIC. Useful if you have IBM mainframes on site. .ip 5 If no aliases are found for this address, pass the address through ruleset 5 for possible alternate resolution. This is intended to forward the mail to an alternate delivery spot. .ip 7 Strip all output to seven bits. This is the default if the .b L flag is set. Note that clearing this option is not sufficient to get full eight bit data passed through .i sendmail . If the .b 7 option is set, this is essentially always set, since the eighth bit was stripped on input. Note that this option will only impact messages that didn't have 8\(->7 bit MIME conversions performed. .ip 8 If set, it is acceptable to send eight bit data to this mailer; the usual attempt to do 8\(->7 bit MIME conversions will be bypassed. .ip 9 If set, do .i limited 7\(->8 bit MIME conversions. These conversions are limited to text/plain data. .ip : Check addresses to see if they begin .q :include: ; if they do, convert them to the .q *include* mailer. .ip | Check addresses to see if they begin with a `|'; if they do, convert them to the .q prog mailer. .ip / Check addresses to see if they begin with a `/'; if they do, convert them to the .q *file* mailer. .ip @ Look up addresses in the user database. .pp Configuration files prior to level 6 assume the `A', `w', `5', `:', `|', `/', and `@' options on the mailer named .q local . .pp The mailer with the special name .q error can be used to generate a user error. The (optional) host field is an exit status to be returned, and the user field is a message to be printed. The exit status may be numeric or one of the values USAGE, NOUSER, NOHOST, UNAVAILABLE, SOFTWARE, TEMPFAIL, PROTOCOL, or CONFIG to return the corresponding EX_ exit code, or an enhanced error code as described in RFC 1893, .ul Enhanced Mail System Status Codes. For example, the entry: .(b $#error $@ NOHOST $: Host unknown in this domain .)b on the RHS of a rule will cause the specified error to be generated and the .q "Host unknown" exit status to be returned if the LHS matches. This mailer is only functional in rulesets 0, 5, or one of the check_* rulesets. .pp The mailer named .q local .i must be defined in every configuration file. This is used to deliver local mail, and is treated specially in several ways. Additionally, three other mailers named .q prog , .q *file* , and .q *include* may be defined to tune the delivery of messages to programs, files, and :include: lists respectively. They default to: .(b Mprog, P=/bin/sh, F=lsD, A=sh \-c $u M*file*, P=/dev/null, F=lsDFMPEu, A=FILE M*include*, P=/dev/null, F=su, A=INCLUDE .)b .pp The Sender and Recipient rewriting sets may either be a simple ruleset id or may be two ids separated by a slash; if so, the first rewriting set is applied to envelope addresses and the second is applied to headers. .pp The Directory is actually a colon-separated path of directories to try. For example, the definition .q D=$z:/ first tries to execute in the recipient's home directory; if that is not available, it tries to execute in the root of the filesystem. This is intended to be used only on the .q prog mailer, since some shells (such as .i csh ) refuse to execute if they cannot read the home directory. Since the queue directory is not normally readable by unprivileged users .i csh scripts as recipients can fail. .pp The Userid specifies the default user and group id to run as, overriding the .b DefaultUser option (q.v.). If the .b S mailer flag is also specified, this is the user and group to run as in all circumstances. This may be given as .i user:group to set both the user and group id; either may be an integer or a symbolic name to be looked up in the .i passwd and .i group files respectively. If only a symbolic user name is specified, the group id in the .i passwd file for that user is used as the group id. .pp The Charset field is used when converting a message to MIME; this is the character set used in the Content-Type: header. If this is not set, the .b DefaultCharset option is used, and if that is not set, the value .q unknown-8bit is used. .b WARNING: this field applies to the sender's mailer, not the recipient's mailer. For example, if the envelope sender address lists an address on the local network and the recipient is on an external network, the character set will be set from the Charset= field for the local network mailer, not that of the external network mailer. .pp The Type= field sets the type information used in MIME error messages as defined by RFC 1894. It is actually three values separated by slashes: the MTA-type (that is, the description of how hosts are named), the address type (the description of e-mail addresses), and the diagnostic type (the description of error diagnostic codes). Each of these must be a registered value or begin with .q X\- . The default is .q dns/rfc822/smtp . .sh 2 "H \*- Define Header" .pp The format of the header lines that .i sendmail inserts into the message are defined by the .b H line. The syntax of this line is: .(b F .b H [\c .b ? \c .i mflags \c .b ? ]\c .i hname \c .b : .i htemplate .)b Continuation lines in this spec are reflected directly into the outgoing message. The .i htemplate is macro expanded before insertion into the message. If the .i mflags (surrounded by question marks) are specified, at least one of the specified flags must be stated in the mailer definition for this header to be automatically output. If one of these headers is in the input it is reflected to the output regardless of these flags. .pp Some headers have special semantics that will be described later. .sh 2 "O \*- Set Option" .pp There are a number of global options that can be set from a configuration file. Options are represented by full words; some are also representable as single characters for back compatibility. The syntax of this line is: .(b F .b O \0 .i option \c .b = \c .i value .)b This sets option .i option to be .i value . Note that there .i must be a space between the letter `O' and the name of the option. An older version is: .(b F .b O \c .i o\|value .)b where the option .i o is a single character. Depending on the option, .i value may be a string, an integer, a boolean (with legal values .q t , .q T , .q f , or .q F ; the default is TRUE), or a time interval. .pp The options supported (with the old, one character names in brackets) are: .nr ii 1i .ip "AliasFile=\fIspec, spec, ...\fP" [A] Specify possible alias file(s). Each .i spec should be in the format ``\c .i class \c .b : .i file '' where .i class \c .b : is optional and defaults to ``implicit''. Depending on how .i sendmail is compiled, valid classes are .q implicit (search through a compiled-in list of alias file types, for back compatibility), .q hash (if .sm NEWDB is specified), .q dbm (if .sm NDBM is specified), .q stab (internal symbol table \*- not normally used unless you have no other database lookup), or .q nis (if .sm NIS is specified). If a list of .i spec s are provided, .i sendmail searches them in order. .ip AliasWait=\fItimeout\fP [a] If set, wait up to .i timeout (units default to minutes) for an .q @:@ entry to exist in the alias database before starting up. If it does not appear in the .i timeout interval rebuild the database (if the .b AutoRebuildAliases option is also set) or issue a warning. .ip AllowBogusHELO [no short name] If set, allow HELO SMTP commands that don't include a host name. Setting this violates RFC 1123 section 5.2.5, but is necessary to interoperate with several SMTP clients. If there is a value, it is still checked for legitimacy. .ip AutoRebuildAliases [D] If set, rebuild the alias database if necessary and possible. If this option is not set, .i sendmail will never rebuild the alias database unless explicitly requested using .b \-bi . Not recommended \(em can cause thrashing. .ip BlankSub=\fIc\fP [B] Set the blank substitution character to .i c . Unquoted spaces in addresses are replaced by this character. Defaults to space (i.e., no change is made). .ip CheckAliases [n] Validate the RHS of aliases when rebuilding the alias database. .ip CheckpointInterval=\fIN\fP [C] Checkpoints the queue every .i N (default 10) addresses sent. If your system crashes during delivery to a large list, this prevents retransmission to any but the last .I N recipients. .ip ClassFactor=\fIfact\fP [z] The indicated .i fact or is multiplied by the message class (determined by the Precedence: field in the user header and the .b P lines in the configuration file) and subtracted from the priority. Thus, messages with a higher Priority: will be favored. Defaults to 1800. .ip ColonOkInAddr [no short name] If set, colons are acceptable in e-mail addresses (e.g., .q host:user ). If not set, colons indicate the beginning of a RFC 822 group construct (\c .q "groupname: member1, member2, ... memberN;" ). Doubled colons are always acceptable (\c .q nodename::user ) and proper route-addr nesting is understood (\c .q <@relay:user@host> ). Furthermore, this option defaults on if the configuration version level is less than 6 (for back compatibility). However, it must be off for full compatibility with RFC 822. .ip ConnectionCacheSize=\fIN\fP [k] The maximum number of open connections that will be cached at a time. The default is one. This delays closing the current connection until either this invocation of .i sendmail needs to connect to another host or it terminates. Setting it to zero defaults to the old behavior, that is, connections are closed immediately. Since this consumes file descriptors, the connection cache should be kept small: 4 is probably a practical maximum. .ip ConnectionCacheTimeout=\fItimeout\fP [K] The maximum amount of time a cached connection will be permitted to idle without activity. If this time is exceeded, the connection is immediately closed. This value should be small (on the order of ten minutes). Before .i sendmail uses a cached connection, it always sends a RSET command to check the connection; if this fails, it reopens the connection. This keeps your end from failing if the other end times out. The point of this option is to be a good network neighbor and avoid using up excessive resources on the other end. The default is five minutes. .ip ConnectionRateThrottle=\fIN\fP [no short name] If set to a positive value, allow no more than .i N incoming daemon connections in a one second period. This is intended to flatten out peaks and allow the load average checking to cut in. Defaults to zero (no limits). .ip DaemonPortOptions=\fIoptions\fP [O] Set server SMTP options. The options are .i key=value pairs. Known keys are: .(b .ta 1i Port Name/number of listening port (defaults to "smtp") Addr Address mask (defaults INADDR_ANY) Family Address family (defaults to INET) Listen Size of listen queue (defaults to 10) SndBufSize Size of TCP send buffer RcvBufSize Size of TCP receive buffer .)b The .i Addr ess mask may be a numeric address in dot notation or a network name. .ip DefaultCharSet=\fIcharset\fP [no short name] When a message that has 8-bit characters but is not in MIME format is converted to MIME (see the EightBitMode option) a character set must be included in the Content-Type: header. This character set is normally set from the Charset= field of the mailer descriptor. If that is not set, the value of this option is used. If this option is not set, the value .q unknown-8bit is used. .ip DefaultUser=\fIuser:group\fP [u] Set the default userid for mailers to .i user:group . If .i group is omitted and .i user is a user name (as opposed to a numeric user id) the default group listed in the /etc/passwd file for that user is used as the default group. Both .i user and .i group may be numeric. Mailers without the .i S flag in the mailer definition will run as this user. Defaults to 1:1. The value can also be given as a symbolic user name.\** .(f \**The old .b g option has been combined into the .b DefaultUser option. .)f .ip DeliveryMode=\fIx\fP [d] Deliver in mode .i x . Legal modes are: .(b .ta 4n i Deliver interactively (synchronously) b Deliver in background (asynchronously) q Just queue the message (deliver during queue run) d Defer delivery and all map lookups (deliver during queue run) .)b Defaults to ``b'' if no option is specified, ``i'' if it is specified but given no argument (i.e., ``Od'' is equivalent to ``Odi''). The .b \-v command line flag sets this to .b i . .ip DialDelay=\fIsleeptime\fP [no short name] Dial-on-demand network connections can see timeouts if a connection is opened before the call is set up. If this is set to an interval and a connection times out on the first connection being attempted .i sendmail will sleep for this amount of time and try again. This should give your system time to establish the connection to your service provider. Units default to seconds, so .q DialDelay=5 uses a five second delay. Defaults to zero (no retry). .ip DontExpandCnames [no short name] The standards say that all host addresses used in a mail message must be fully canonical. For example, if your host is named .q Cruft.Foo.ORG and also has an alias of .q FTP.Foo.ORG , the former name must be used at all times. This is enforced during host name canonification ($[ ... $] lookups). If this option is set, the protocols are ignored and the .q wrong thing is done. However, the IETF is moving toward changing this standard, so the behaviour may become acceptable. Please note that hosts downstream may still rewrite the address to be the true canonical name however. .ip DontInitGroups [no short name] If set, .i sendmail will avoid using the initgroups(3) call. If you are running NIS, this causes a sequential scan of the groups.byname map, which can cause your NIS server to be badly overloaded in a large domain. The cost of this is that the only group found for users will be their primary group (the one in the password file), which will make file access permissions somewhat more restrictive. Has no effect on systems that don't have group lists. .ip DontPruneRoutes [R] Normally, .i sendmail tries to eliminate any unnecessary explicit routes when sending an error message (as discussed in RFC 1123 \(sc 5.2.6). For example, when sending an error message to .(b <@known1,@known2,@known3:user@unknown> .)b .i sendmail will strip off the .q @known1,@known2 in order to make the route as direct as possible. However, if the .b R option is set, this will be disabled, and the mail will be sent to the first address in the route, even if later addresses are known. This may be useful if you are caught behind a firewall. .ip DoubleBounceAddress=\fIerror-address\fP [no short name] If an error occurs when sending an error message, send the error report (termed a .q "double bounce" because it is an error .q bounce that occurs when trying to send another error .q bounce ) to the indicated address. If not set, defaults to .q postmaster . .ip EightBitMode=\fIaction\fP [8] Set handling of eight-bit data. There are two kinds of eight-bit data: that declared as such using the .b BODY=8BITMIME ESMTP declaration or the .b \-B8BITMIME command line flag, and undeclared 8-bit data, that is, input that just happens to be eight bits. There are three basic operations that can happen: undeclared 8-bit data can be automatically converted to 8BITMIME, undeclared 8-bit data can be passed as-is without conversion to MIME (``just send 8''), and declared 8-bit data can be converted to 7-bits for transmission to a non-8BITMIME mailer. The possible .i action s are: .(b .\" r Reject undeclared 8-bit data; .\" don't convert 8BITMIME\(->7BIT (``reject'') s Reject undeclared 8-bit data (``strict'') .\" do convert 8BITMIME\(->7BIT (``strict'') .\" c Convert undeclared 8-bit data to MIME; .\" don't convert 8BITMIME\(->7BIT (``convert'') m Convert undeclared 8-bit data to MIME (``mime'') .\" do convert 8BITMIME\(->7BIT (``mime'') .\" j Pass undeclared 8-bit data; .\" don't convert 8BITMIME\(->7BIT (``just send 8'') p Pass undeclared 8-bit data (``pass'') .\" do convert 8BITMIME\(->7BIT (``pass'') .\" a Adaptive algorithm: see below .)b .\"The adaptive algorithm is to accept 8-bit data, .\"converting it to 8BITMIME only if the receiver understands that, .\"otherwise just passing it as undeclared 8-bit data; .\"8BITMIME\(->7BIT conversions are done. In all cases properly declared 8BITMIME data will be converted to 7BIT as needed. .ip ErrorHeader=\fIfile-or-message\fP [E] Prepend error messages with the indicated message. If it begins with a slash, it is assumed to be the pathname of a file containing a message (this is the recommended setting). Otherwise, it is a literal message. The error file might contain the name, email address, and/or phone number of a local postmaster who could provide assistance in to end users. If the option is missing or null, or if it names a file which does not exist or which is not readable, no message is printed. .ip ErrorMode=\fIx\fP [e] Dispose of errors using mode .i x . The values for .i x are: .(b p Print error messages (default) q No messages, just give exit status m Mail back errors w Write back errors (mail if user not logged in) e Mail back errors and give zero exit stat always .)b .ip FallbackMXhost=\fIfallbackhost\fP [V] If specified, the .i fallbackhost acts like a very low priority MX on every host. This is intended to be used by sites with poor network connectivity. .ip ForkEachJob [Y] If set, deliver each job that is run from the queue in a separate process. Use this option if you are short of memory, since the default tends to consume considerable amounts of memory while the queue is being processed. .ip ForwardPath=\fIpath\fP [J] Set the path for searching for users' .forward files. The default is .q $z/.forward . Some sites that use the automounter may prefer to change this to .q /var/forward/$u to search a file with the same name as the user in a system directory. It can also be set to a sequence of paths separated by colons; .i sendmail stops at the first file it can successfully and safely open. For example, .q /var/forward/$u:$z/.forward will search first in /var/forward/\c .i username and then in .i ~username /.forward (but only if the first file does not exist). .ip HelpFile=\fIfile\fP [H] Specify the help file for SMTP. .ip HoldExpensive [c] If an outgoing mailer is marked as being expensive, don't connect immediately. This requires that queueing be compiled in, since it will depend on a queue run process to actually send the mail. .ip HostsFile=\fIpath\fP [no short name] The path to the hosts database, normally .q /etc/hosts . This option is only consulted when sendmail is canonifying addresses, and then only when .q files is in the .q hosts service switch entry. In particular, this file is .i never used when looking up host addresses; that is under the control of the system .i gethostbyname (3) routine. .ip HostStatusDirectory=\fIpath\fP [no short name] The location of the long term host status information. When set, information about the status of hosts (e.g., host down or not accepting connections) will be shared between all .i sendmail processes; normally, this information is only held within a single queue run. This option requires a connection cache of at least 1 to function. If the option begins with a leading `/', it is an absolute pathname; otherwise, it is relative to the mail queue directory. A suggested value for sites desiring persistent host status is .q \&.hoststat (i.e., a subdirectory of the queue directory). .ip IgnoreDots [i] Ignore dots in incoming messages. This is always disabled (that is, dots are always accepted) when reading SMTP mail. .ip LogLevel=\fIn\fP [L] Set the default log level to .i n . Defaults to 9. .ip M\fIx\|value\fP [no long version] Set the macro .i x to .i value . This is intended only for use from the command line. The .b \-M flag is preferred. .ip MatchGECOS [G] Allow fuzzy matching on the GECOS field. If this flag is set, and the usual user name lookups fail (that is, there is no alias with this name and a .i getpwnam fails), sequentially search the password file for a matching entry in the GECOS field. This also requires that MATCHGECOS be turned on during compilation. This option is not recommended. .ip MaxDaemonChildren=\fIN\fP [no short name] If set, .i sendmail will refuse connections when it has more than .i N children processing incoming mail. This does not limit the number of outgoing connections. If not set, there is no limit to the number of children -- that is, the system load averaging controls this. .ip MaxHopCount=\fIN\fP [h] The maximum hop count. Messages that have been processed more than .i N times are assumed to be in a loop and are rejected. Defaults to 25. .ip MaxHostStatAge=\fIage\fP [no short name] Not yet implemented. This option specifies how long host status information will be retained. For example, if a host is found to be down, connections to that host will not be retried for this interval. The units default to minutes. .ip MaxMessageSize=\fIN\fP [no short name] Specify the maximum message size to be advertised in the ESMTP EHLO response. Messages larger than this will be rejected. .ip MaxQueueRunSize=\fIN\fP [no short name] The maximum number of jobs that will be processed in a single queue run. If not set, there is no limit on the size. If you have very large queues or a very short queue run interval this could be unstable. However, since the first .i N jobs in queue directory order are run (rather than the .i N highest priority jobs) this should be set as high as possible to avoid .q losing jobs that happen to fall late in the queue directory. .ip MeToo [m] Send to me too, even if I am in an alias expansion. .ip MinFreeBlocks=\fIN\fP [b] Insist on at least .i N blocks free on the filesystem that holds the queue files before accepting email via SMTP. If there is insufficient space .i sendmail gives a 452 response to the MAIL command. This invites the sender to try again later. .ip MinQueueAge=\fPage\fP [no short name] Don't process any queued jobs that have been in the queue less than the indicated time interval. This is intended to allow you to get responsiveness by processing the queue fairly frequently without thrashing your system by trying jobs too often. The default units are minutes. .ip MustQuoteChars=\fIs\fP [no short name] Sets the list of characters that must be quoted if used in a full name that is in the phrase part of a ``phrase
'' syntax. The default is ``\'.''. The characters ``@,;:\e()[]'' are always added to this list. .ip NoRecipientAction [no short name] The action to take when you receive a message that has no valid recipient headers (To:, Cc:, Bcc:, or Apparently-To: \(em the last included for back compatibility with old .i sendmail s). It can be .b None to pass the message on unmodified, which violates the protocol, .b Add-To to add a To: header with any recipients it can find in the envelope (which might expose Bcc: recipients), .b Add-Apparently-To to add an Apparently-To: header (this is only for back-compatibility and is officially deprecated), .b Add-To-Undisclosed to add a header .q "To: undisclosed-recipients:;" to make the header legal without disclosing anything, or .b Add-Bcc to add an empty Bcc: header. .ip OldStyleHeaders [o] Assume that the headers may be in old format, i.e., spaces delimit names. This actually turns on an adaptive algorithm: if any recipient address contains a comma, parenthesis, or angle bracket, it will be assumed that commas already exist. If this flag is not on, only commas delimit names. Headers are always output with commas between the names. Defaults to off. .ip OperatorChars=\fIcharlist\fP [$o macro] The list of characters that are considered to be .q operators , that is, characters that delimit tokens. All operator characters are tokens by themselves; sequences of non-operator characters are also tokens. White space characters separate tokens but are not tokens themselves \(em for example, .q AAA.BBB has three tokens, but .q "AAA BBB" has two. If not set, OperatorChars defaults to .q \&.\|:\|@\|[\|] ; additionally, the characters .q (\|)\|<\|>\|,\|; are always operators. .ip PostmasterCopy=\fIpostmaster\fP [P] If set, copies of error messages will be sent to the named .i postmaster . Only the header of the failed message is sent. Since most errors are user problems, this is probably not a good idea on large sites, and arguably contains all sorts of privacy violations, but it seems to be popular with certain operating systems vendors. Defaults to no postmaster copies. .ip PrivacyOptions=\fI\|opt,opt,...\fP [p] Set the privacy .i opt ions. ``Privacy'' is really a misnomer; many of these are just a way of insisting on stricter adherence to the SMTP protocol. The .i opt ions can be selected from: .(b .ta \w'needvrfyhelo'u+3n public Allow open access needmailhelo Insist on HELO or EHLO command before MAIL needexpnhelo Insist on HELO or EHLO command before EXPN noexpn Disallow EXPN entirely needvrfyhelo Insist on HELO or EHLO command before VRFY novrfy Disallow VRFY entirely restrictmailq Restrict mailq command restrictqrun Restrict \-q command line flag noreceipts Don't return success DSNs goaway Disallow essentially all SMTP status queries authwarnings Put X-Authentication-Warning: headers in messages .)b The .q goaway pseudo-flag sets all flags except .q restrictmailq and .q restrictqrun . If mailq is restricted, only people in the same group as the queue directory can print the queue. If queue runs are restricted, only root and the owner of the queue directory can run the queue. Authentication Warnings add warnings about various conditions that may indicate attempts to spoof the mail system, such as using an non-standard queue directory. .ip QueueDirectory=\fIdir\fP [Q] Use the named .i dir as the queue directory. .ip QueueFactor=\fIfactor\fP [q] Use .i factor as the multiplier in the map function to decide when to just queue up jobs rather than run them. This value is divided by the difference between the current load average and the load average limit (\c .b QueueLA option) to determine the maximum message priority that will be sent. Defaults to 600000. .ip QueueLA=\fILA\fP [x] When the system load average exceeds .i LA , just queue messages (i.e., don't try to send them). Defaults to 8. .ip QueueSortOrder=\fIalgorithm\fP [no short name] Sets the .i algorithm used for sorting the queue. Only the first character of the value is used. Legal values are .q host (to order by the name of the first host name of the first recipient), .q time (to order by the submission time), and .q priority (to order by message priority). Host ordering makes better use of the connection cache, but may tend to process low priority messages that go to a single host over high priority messages that go to several hosts; it probably shouldn't be used on slow network links. Time ordering is almost always a bad idea, since it allows large, bulk mail to go out before smaller, personal mail, but may have applicability on some hosts with very fast connections. Priority ordering is the default. .ip QueueTimeout=\fItimeout\fP [T] A synonym for .q Timeout.queuereturn . Use that form instead of the .q QueueTimeout form. .ip ResolverOptions=\fIoptions\fP [I] Set resolver options. Values can be set using .b + \c .i flag and cleared using .b \- \c .i flag ; the .i flag s can be .q debug , .q aaonly , .q usevc , .q primary , .q igntc , .q recurse , .q defnames , .q stayopen , or .q dnsrch . The string .q HasWildcardMX (without a .b + or .b \- ) can be specified to turn off matching against MX records when doing name canonifications. .b N.B. Prior to 8.7, this option indicated that the name server be responding in order to accept addresses. This has been replaced by checking to see if the .q dns method is listed in the service switch entry for the .q hosts service. .ip RunAsUser=\fIuser\fP [no short name] The .i user parameter may be a user name (looked up in .i /etc/passwd ) or a numeric user id; either form can have .q ":group" attached (where group can be numeric or symbolic). If set to a non-zero (non-root) value, .i sendmail will change to this user id shortly after startup\**. .(f \**When running as a daemon, it changes to this user after accepting a connection but before reading any .sm SMTP commands. .)f This avoids a certain class of security problems. However, this means that all .q \&.forward and .q :include: files must be readable by the indicated .i user , and on systems that don't support the saved uid bit properly, all files to be written must be writable by .i user and all programs will be executed by .i user . It is also incompatible with the .b SafeFileEnvironment option. In other words, it may not actually add much to security on an average system, and may in fact detract from security (because other file permissions must be loosened). However, it should be useful on firewalls and other places where users don't have accounts and the aliases file is well constrained. .ip RecipientFactor=\fIfact\fP [y] The indicated .i fact or is added to the priority (thus .i lowering the priority of the job) for each recipient, i.e., this value penalizes jobs with large numbers of recipients. Defaults to 30000. .ip RefuseLA=\fILA\fP [X] When the system load average exceeds .i LA , refuse incoming SMTP connections. Defaults to 12. .ip RetryFactor=\fIfact\fP [Z] The .i fact or is added to the priority every time a job is processed. Thus, each time a job is processed, its priority will be decreased by the indicated value. In most environments this should be positive, since hosts that are down are all too often down for a long time. Defaults to 90000. .ip SafeFileEnvironment=\fIdir\fP [no short name] If this option is set, .i sendmail will do a .i chroot (2) call into the indicated .i dir ectory before doing any file writes. If the file name specified by the user begins with .i dir , that partial path name will be stripped off before writing, so (for example) if the SafeFileEnvironment variable is set to .q /safe then aliases of .q /safe/logs/file and .q /logs/file actually indicate the same file. Additionally, if this option is set, .i sendmail refuses to deliver to symbolic links. .ip SaveFromLine [f] Save Unix-style .q From lines at the front of headers. Normally they are assumed redundant and discarded. .ip SendMIMEErrors [j] If set, send error messages in MIME format (see RFC1521 and RFC1344 for details). If disabled, .i sendmail will not return the DSN keyword in response to an EHLO and will not do Delivery Status Notification processing as described in RFC1891. .ip ServiceSwitchFile=\fIfilename\fP [no short name] If your host operating system has a service switch abstraction (e.g., /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix and DEC OSF/1) that service will be consulted and this option is ignored. Otherwise, this is the name of a file that provides the list of methods used to implement particular services. The syntax is a series of lines, each of which is a sequence of words. The first word is the service name, and following words are service types. The services that .i sendmail consults directly are .q aliases and .q hosts. Service types can be .q dns , .q nis , .q nisplus , or .q files (with the caveat that the appropriate support must be compiled in before the service can be referenced). If ServiceSwitchFile is not specified, it defaults to /etc/service.switch. If that file does not exist, the default switch is: .(b aliases files hosts dns nis files .)b The default file is .q /etc/service.switch . .ip SevenBitInput [7] Strip input to seven bits for compatibility with old systems. This shouldn't be necessary. .ip SingleLineFromHeader [no short name] If set, From: lines that have embedded newlines are unwrapped onto one line. This is to get around a botch in Lotus Notes that apparently cannot understand legally wrapped RFC822 headers. .ip SingleThreadDelivery [no short name] If set, a client machine will never try to open two SMTP connections to a single server machine at the same time, even in different processes. That is, if another .i sendmail is already talking to some host a new .i sendmail will not open another connection. This property is of mixed value; although this reduces the load on the other machine, it can cause mail to be delayed (for example, if one .i sendmail is delivering a huge message, other .i sendmail s won't be able to send even small messages). Also, it requires another file descriptor (for the lock file) per connection, so you may have to reduce the .b ConnectionCacheSize option to avoid running out of per-process file descriptors. Requires the .b HostStatusDirectory option. .ip SmtpGreetingMessage=\fImessage\fP [$e macro] The message printed when the SMTP server starts up. Defaults to .q "$j Sendmail $v ready at $b". .ip StatusFile=\fIfile\fP [S] Log summary statistics in the named .i file . If not set, no summary statistics are saved. This file does not grow in size. It can be printed using the .i mailstats (8) program. .ip SuperSafe [s] Be super-safe when running things, i.e., always instantiate the queue file, even if you are going to attempt immediate delivery. .i Sendmail always instantiates the queue file before returning control the client under any circumstances. This should really .i always be set. .ip TempFileMode=\fImode\fP [F] The file mode for queue files. It is interpreted in octal by default. Defaults to 0600. .ip Timeout.\fItype\fP=\|\fItimeout\fP [r; subsumes old T option as well] Set timeout values. The actual timeout is indicated by the .i type . The recognized timeouts and their default values, and their minimum values specified in RFC 1123 section 5.3.2 are: .(b .ta \w'datafinal'u+3n initial wait for initial greeting message [5m, 5m] helo reply to HELO or EHLO command [5m, none] mail reply to MAIL command [10m, 5m] rcpt reply to RCPT command [1h, 5m] datainit reply to DATA command [5m, 2m] datablock data block read [1h, 3m] datafinal reply to final ``.'' in data [1h, 10m] rset reply to RSET command [5m, none] quit reply to QUIT command [2m, none] misc reply to NOOP and VERB commands [2m, none] ident IDENT protocol timeout [30s, none] fileopen\(dg timeout on opening .forward and :include: files [60s, none] command\(dg command read [1h, 5m] queuereturn\(dg how long until a message is returned [5d, 5d] queuewarn\(dg how long until a warning is sent [none, none] hoststatus\(dg how long until host status is ``stale'' [30m, none] .)b All but those marked with a dagger (\(dg) apply to client SMTP. If the message is submitted using the .sm NOTIFY .sm SMTP extension, warning messages will only be sent if .sm NOTIFY=DELAY is specified. The queuereturn and queuewarn timeouts can be further qualified with a tag based on the Precedence: field in the message; they must be one of .q urgent (indicating a positive non-zero precedence) .q normal (indicating a zero precedence), or .q non-urgent (indicating negative precedences). For example, setting .q Timeout.queuewarn.urgent=1h sets the warning timeout for urgent messages only to one hour. The default if no precedence is indicated is to set the timeout for all precedences. .ip TimeZoneSpec=\fItzinfo\fP [t] Set the local time zone info to .i tzinfo \*- for example, .q PST8PDT . Actually, if this is not set, the TZ environment variable is cleared (so the system default is used); if set but null, the user's TZ variable is used, and if set and non-null the TZ variable is set to this value. .ip TryNullMXList [w] If this system is the .q best (that is, lowest preference) MX for a given host, its configuration rules should normally detect this situation and treat that condition specially by forwarding the mail to a UUCP feed, treating it as local, or whatever. However, in some cases (such as Internet firewalls) you may want to try to connect directly to that host as though it had no MX records at all. Setting this option causes .i sendmail to try this. The downside is that errors in your configuration are likely to be diagnosed as .q "host unknown" or .q "message timed out" instead of something more meaningful. This option is disrecommended. .ip UnixFromLine=\fIfromline\fP [$l macro] Defines the format used when .i sendmail must add a UNIX-style From_ line (that is, a line beginning .q Fromuser ). Defaults to .q "From $g $d" . Don't change this unless your system uses a different UNIX mailbox format (very unlikely). .ip UnsafeGroupWrites [no short name] If set, :include: and .forward files that are group writable are considered .q unsafe , that is, they cannot reference programs or write directly to files. World writable :include: and .forward files are always unsafe.. .ip UseErrorsTo [l] If there is an .q Errors-To: header, send error messages to the addresses listed there. They normally go to the envelope sender. Use of this option causes .i sendmail to violate RFC 1123. This option is disrecommended and deprecated. .ip UserDatabaseSpec=\fIudbspec\fP [U] The user database specification. .ip UserSubmission [no short name] This is an initial submission directly from a Mail User Agent. This can be set in the configuration file if you have MUAs that don't pass the .b \-U flag or use the XUSR ESMTP extension, but some relayed mail may get inappropriately rewritten if you do. .ip Verbose [v] Run in verbose mode. If this is set, .i sendmail adjusts options .b HoldExpensive (old .b c ) and .b DeliveryMode (old .b d ) so that all mail is delivered completely in a single job so that you can see the entire delivery process. Option .b Verbose should .i never be set in the configuration file; it is intended for command line use only. .lp All options can be specified on the command line using the \-O or \-o flag, but most will cause .i sendmail to relinquish its setuid permissions. The options that will not cause this are MinFreeBlocks [b], DeliveryMode [d], ErrorMode [e], IgnoreDots [i], LogLevel [L], MeToo [m], OldStyleHeaders [o], PrivacyOptions [p], Timeouts [r], SuperSafe [s], Verbose [v], CheckpointInterval [C], and SevenBitInput [7]. Also, M (define macro) when defining the r or s macros is also considered .q safe . .sh 2 "P \*- Precedence Definitions" .pp Values for the .q "Precedence:" field may be defined using the .b P control line. The syntax of this field is: .(b \fBP\fP\fIname\fP\fB=\fP\fInum\fP .)b When the .i name is found in a .q Precedence: field, the message class is set to .i num . Higher numbers mean higher precedence. Numbers less than zero have the special property that if an error occurs during processing the body of the message will not be returned; this is expected to be used for .q "bulk" mail such as through mailing lists. The default precedence is zero. For example, our list of precedences is: .(b Pfirst-class=0 Pspecial-delivery=100 Plist=\-30 Pbulk=\-60 Pjunk=\-100 .)b People writing mailing list exploders are encouraged to use .q "Precedence: list" . Older versions of .i sendmail (which discarded all error returns for negative precedences) didn't recognize this name, giving it a default precedence of zero. This allows list maintainers to see error returns on both old and new versions of .i sendmail . .sh 2 "V \*- Configuration Version Level" .pp To provide compatibility with old configuration files, the .b V line has been added to define some very basic semantics of the configuration file. These are not intended to be long term supports; rather, they describe compatibility features which will probably be removed in future releases. .pp .b N.B.: these version .i levels have nothing to do with the version .i number on the files. For example, as of this writing version 8 config files (specifically, 8.7) used version level 6 configurations. .pp .q Old configuration files are defined as version level one. Version level two files make the following changes: .np Host name canonification ($[ ... $]) appends a dot if the name is recognized; this gives the config file a way of finding out if anything matched. (Actually, this just initializes the .q host map with the .q \-a. flag \*- you can reset it to anything you prefer by declaring the map explicitly.) .np Default host name extension is consistent throughout processing; version level one configurations turned off domain extension (that is, adding the local domain name) during certain points in processing. Version level two configurations are expected to include a trailing dot to indicate that the name is already canonical. .np Local names that are not aliases are passed through a new distinguished ruleset five; this can be used to append a local relay. This behaviour can be prevented by resolving the local name with an initial `@'. That is, something that resolves to a local mailer and a user name of .q vikki will be passed through ruleset five, but a user name of .q @vikki will have the `@' stripped, will not be passed through ruleset five, but will otherwise be treated the same as the prior example. The expectation is that this might be used to implement a policy where mail sent to .q vikki was handled by a central hub, but mail sent to .q vikki@localhost was delivered directly. .pp Version level three files allow # initiated comments on all lines. Exceptions are backslash escaped # marks and the $# syntax. .pp Version level four configurations are completely equivalent to level three for historical reasons. .pp Version level five configuration files change the default definition of .b $w to be just the first component of the hostname. .pp Version level six configuration files change many of the local processing options (such as aliasing and matching the beginning of the address for `|' characters) to be mailer flags; this allows fine-grained control over the special local processing. Level six configuration files may also use long option names. The .b ColonOkInAddr option (to allow colons in the local-part of addresses) defaults .b on for lower numbered configuration files; the configuration file requires some additional intelligence to properly handle the RFC 822 group construct. .pp The .b V line may have an optional .b / \c .i vendor to indicate that this configuration file uses modifications specific to a particular vendor\**. .(f \**And of course, vendors are encouraged to add themselves to the list of recognized vendors by editing the routine .i setvendor in .i conf.c . Please send e-mail to sendmail@Sendmail.ORG to register your vendor dialect. .)f You may use .q /Berkeley to emphasize that this configuration file uses the Berkeley dialect of .i sendmail . .sh 2 "K \*- Key File Declaration" .pp Special maps can be defined using the line: .(b Kmapname mapclass arguments .)b The .i mapname is the handle by which this map is referenced in the rewriting rules. The .i mapclass is the name of a type of map; these are compiled in to .i sendmail . The .i arguments are interpreted depending on the class; typically, there would be a single argument naming the file containing the map. .pp Maps are referenced using the syntax: .(b $( \fImap\fP \fIkey\fP $@ \fIarguments\fP $: \fIdefault\fP $) .)b where either or both of the .i arguments or .i default portion may be omitted. The .i "$@ arguments" may appear more than once. The indicated .i key and .i arguments are passed to the appropriate mapping function. If it returns a value, it replaces the input. If it does not return a value and the .i default is specified, the .i default replaces the input. Otherwise, the input is unchanged. .pp The .i arguments are passed to the map for arbitrary use. Most map classes can interpolate these arguments into their values using the syntax .q %\fIn\fP (where .i n is a digit) to indicate the corresponding .i argument . Argument .q %0 indicates the database key. For example, the rule .(b .ta 1.5i R$\- ! $+ $: $(uucp $1 $@ $2 $: %1 @ %0 . UUCP $) .)b Looks up the UUCP name in a (user defined) UUCP map; if not found it turns it into .q \&.UUCP form. The database might contain records like: .(b decvax %1@%0.DEC.COM research %1@%0.ATT.COM .)b Note that .i default clauses never do this mapping. .pp The built in map with both name and class .q host is the host name canonicalization lookup. Thus, the syntax: .(b $(host \fIhostname\fP$) .)b is equivalent to: .(b $[\fIhostname\fP$] .)b .pp There are many defined classes. .ip dbm Database lookups using the ndbm(3) library. .i Sendmail must be compiled with .b NDBM defined. .ip btree Database lookups using the btree interface to the Berkeley db(3) library. .i Sendmail must be compiled with .b NEWDB defined. .ip hash Database lookups using the hash interface to the Berkeley db(3) library. .i Sendmail must be compiled with .b NEWDB defined. .ip nis NIS lookups. .i Sendmail must be compiled with .b NIS defined. .ip nisplus NIS+ lookups. .i Sendmail must be compiled with .b NISPLUS defined. The argument is the name of the table to use for lookups, and the .b \-k and .b \-v flags may be used to set the key and value columns respectively. .ip hesiod Hesiod lookups. .i Sendmail must be compiled with .b HESIOD defined. .ip ldapx LDAP X500 directory lookups. .i Sendmail must be compiled with .b LDAPMAP defined. The map supports most of the standard arguments and most of the command line arguments of the .i ldapsearch program. .ip netinfo NeXT NetInfo lookups. .i Sendmail must be compiled with .b NETINFO defined. .ip text Text file lookups. The format of the text file is defined by the .b \-k (key field number), .b \-v (value field number), and .b \-z (field delimiter) flags. .ip stab Internal symbol table lookups. Used internally for aliasing. .ip implicit Really should be called .q alias \(em this is used to get the default lookups for alias files, and is the default if no class is specified for alias files. .ip user Looks up users using .i getpwnam (3). The .b \-v flag can be used to specify the name of the field to return (although this is normally used only to check the existence of a user). .ip host Canonifies host domain names. Given a host name it calls the name server to find the canonical name for that host. .ip sequence The arguments on the `K' line are a list of maps; the resulting map searches the argument maps in order until it finds a match for the indicated key. For example, if the key definition is: .(b Kmap1 ... Kmap2 ... Kseqmap sequence map1 map2 .)b then a lookup against .q seqmap first does a lookup in map1. If that is found, it returns immediately. Otherwise, the same key is used for map2. .ip switch Much like the .q sequence map except that the order of maps is determined by the service switch. The argument is the name of the service to be looked up; the values from the service switch are appended to the map name to create new map names. For example, consider the key definition: .(b Kali switch aliases .)b together with the service switch entry: .(b aliases nis files .)b This causes a query against the map .q ali to search maps named .q ali.nis and .q ali.files in that order. .ip dequote Strip double quotes (") from a name. It does not strip backslashes, and will not strip quotes if the resulting string would contain unscannable syntax (that is, basic errors like unbalanced angle brackets; more sophisticated errors such as unknown hosts are not checked). The intent is for use when trying to accept mail from systems such as DECnet that routinely quote odd syntax such as .(b "49ers::ubell" .)b A typical usage is probably something like: .(b Kdequote dequote \&... R$\- $: $(dequote $1 $) R$\- $+ $: $>3 $1 $2 .)b Care must be taken to prevent unexpected results; for example, .(b "|someprogram < input > output" .)b will have quotes stripped, but the result is probably not what you had in mind. Fortunately these cases are rare. .pp Most of these accept as arguments the same optional flags and a filename (or a mapname for NIS; the filename is the root of the database path, so that .q .db or some other extension appropriate for the database type will be added to get the actual database name). Known flags are: .ip "\-o" Indicates that this map is optional \*- that is, if it cannot be opened, no error is produced, and .i sendmail will behave as if the map existed but was empty. .ip "\-N, \-O" If neither .b \-N or .b \-O are specified, .i sendmail uses an adaptive algorithm to decide whether or not to look for null bytes on the end of keys. It starts by trying both; if it finds any key with a null byte it never tries again without a null byte and vice versa. If .b \-N is specified it never tries without a null byte and if .b \-O is specified it never tries with a null byte. Setting one of these can speed matches but are never necessary. If both .b \-N and .b \-O are specified, .i sendmail will never try any matches at all \(em that is, everything will appear to fail. .ip "\-a\fIx\fP" Append the string .i x on successful matches. For example, the default .i host map appends a dot on successful matches. .ip "\-f" Do not fold upper to lower case before looking up the key. .ip "\-m" Match only (without replacing the value). If you only care about the existence of a key and not the value (as you might when searching the NIS map .q hosts.byname for example), this flag prevents the map from substituting the value. However, The \-a argument is still appended on a match, and the default is still taken if the match fails. .ip "\-k\fIkeycol\fP" The key column name (for NIS+) or number (for text lookups). For LDAP maps this is a filter string passed to printf with a %s where the string to be .q "mapped" is inserted. .ip "\-v\fIvalcol\fP" The value column name (for NIS+) or number (for text lookups). For LDAP maps this is the name of the attribute to be returned. .ip "\-z\fIdelim\fP" The column delimiter (for text lookups). It can be a single character or one of the special strings .q \|\en or .q \|\et to indicate newline or tab respectively. If omitted entirely, the column separator is any sequence of whitespace. .ip "\-t" Normally, when a map attempts to do a lookup and the server fails (e.g., .i sendmail couldn't contact any name server; this is .i not the same as an entry not being found in the map), the message being processed is queued for future processing. The .b \-t flag turns off this behaviour, letting the temporary failure (server down) act as though it were a permanent failure (entry not found). It is particularly useful for DNS lookups, where someone else's misconfigured name server can cause problems on your machine. However, care must be taken to ensure that you don't bounce mail that would be resolved correctly if you tried again. A common strategy is to forward such mail to another, possibly better connected, mail server. .ip "\-s\fIspacesub\fP For the dequote map only, the character to use to replace space characters after a successful dequote. .pp The .i dbm map appends the strings .q \&.pag and .q \&.dir to the given filename; the two .i db -based maps append .q \&.db . For example, the map specification .(b Kuucp dbm \-o \-N /usr/lib/uucpmap .)b specifies an optional map named .q uucp of class .q dbm ; it always has null bytes at the end of every string, and the data is located in /usr/lib/uucpmap.{dir,pag}. .pp The program .i makemap (8) can be used to build any of the three database-oriented maps. It takes the following flags: .ip \-f Do not fold upper to lower case in the map. .ip \-N Include null bytes in keys. .ip \-o Append to an existing (old) file. .ip \-r Allow replacement of existing keys; normally, re-inserting an existing key is an error. .ip \-v Print what is happening. .lp The .i sendmail daemon does not have to be restarted to read the new maps as long as you change them in place; file locking is used so that the maps won't be read while they are being updated.\** .(f \**That is, don't create new maps and then use .i mv (1) to move them into place. Since the maps are already open the new maps will never be seen. .)f .pp New classes can be added in the routine .b setupmaps in file .b conf.c . .sh 2 "The User Database" .pp If you have a version of .i sendmail with the user database package compiled in, the handling of sender and recipient addresses is modified. .pp The location of this database is controlled with the .b UserDatabaseSpec option. .sh 3 "Structure of the user database" .pp The database is a sorted (BTree-based) structure. User records are stored with the key: .(b \fIuser-name\fP\fB:\fP\fIfield-name\fP .)b The sorted database format ensures that user records are clustered together. Meta-information is always stored with a leading colon. .pp Field names define both the syntax and semantics of the value. Defined fields include: .nr ii 1i .ip maildrop The delivery address for this user. There may be multiple values of this record. In particular, mailing lists will have one .i maildrop record for each user on the list. .ip "mailname" The outgoing mailname for this user. For each outgoing name, there should be an appropriate .i maildrop record for that name to allow return mail. See also .i :default:mailname . .ip mailsender Changes any mail sent to this address to have the indicated envelope sender. This is intended for mailing lists, and will normally be the name of an appropriate -request address. It is very similar to the owner-\c .i list syntax in the alias file. .ip fullname The full name of the user. .ip office-address The office address for this user. .ip office-phone The office phone number for this user. .ip office-fax The office FAX number for this user. .ip home-address The home address for this user. .ip home-phone The home phone number for this user. .ip home-fax The home FAX number for this user. .ip project A (short) description of the project this person is affiliated with. In the University this is often just the name of their graduate advisor. .ip plan A pointer to a file from which plan information can be gathered. .pp As of this writing, only a few of these fields are actually being used by .i sendmail : .i maildrop and .i mailname . A .i finger program that uses the other fields is planned. .sh 3 "User database semantics" .pp When the rewriting rules submit an address to the local mailer, the user name is passed through the alias file. If no alias is found (or if the alias points back to the same address), the name (with .q :maildrop appended) is then used as a key in the user database. If no match occurs (or if the maildrop points at the same address), forwarding is tried. .pp If the first token of the user name returned by ruleset 0 is an .q @ sign, the user database lookup is skipped. The intent is that the user database will act as a set of defaults for a cluster (in our case, the Computer Science Division); mail sent to a specific machine should ignore these defaults. .pp When mail is sent, the name of the sending user is looked up in the database. If that user has a .q mailname record, the value of that record is used as their outgoing name. For example, I might have a record: .(b eric:mailname Eric.Allman@CS.Berkeley.EDU .)b This would cause my outgoing mail to be sent as Eric.Allman. .pp If a .q maildrop is found for the user, but no corresponding .q mailname record exists, the record .q :default:mailname is consulted. If present, this is the name of a host to override the local host. For example, in our case we would set it to .q CS.Berkeley.EDU . The effect is that anyone known in the database gets their outgoing mail stamped as .q user@CS.Berkeley.EDU , but people not listed in the database use the local hostname. .sh 3 "Creating the database\**" .(f \**These instructions are known to be incomplete. A future version of the user database is planned including things such as finger service \*- and good documentation. .)f .pp The user database is built from a text file using the .i makemap utility (in the distribution in the makemap subdirectory). The text file is a series of lines corresponding to userdb records; each line has a key and a value separated by white space. The key is always in the format described above \*- for example: .(b eric:maildrop .)b This file is normally installed in a system directory; for example, it might be called .i /etc/userdb . To make the database version of the map, run the program: .(b makemap btree /etc/userdb.db < /etc/userdb .)b Then create a config file that uses this. For example, using the V8 M4 configuration, include the following line in your .mc file: .(b define(\`confUSERDB_SPEC\', /etc/userdb.db) .)b .sh 1 "OTHER CONFIGURATION" .pp There are some configuration changes that can be made by recompiling .i sendmail . This section describes what changes can be made and what has to be modified to make them. In most cases this should be unnecessary unless you are porting .i sendmail to a new environment. .sh 2 "Parameters in src/Makefile" .pp These parameters are intended to describe the compilation environment, not site policy, and should normally be defined in src/Makefile. .ip NDBM If set, the new version of the DBM library that allows multiple databases will be used. If neither NDBM nor NEWDB are set, a much less efficient method of alias lookup is used. .ip NEWDB If set, use the new database package from Berkeley (from 4.4BSD). This package is substantially faster than DBM or NDBM. If NEWDB and NDBM are both set, .i sendmail will read DBM files, but will create and use NEWDB files. .ip NIS Include support for NIS. If set together with .i both NEWDB and NDBM, .i sendmail will create both DBM and NEWDB files if and only if an alias file includes the substring .q /yp/ in the name. This is intended for compatibility with Sun Microsystems' .i mkalias program used on YP masters. .ip NISPLUS Compile in support for NIS+. .ip NETINFO Compile in support for NetInfo (NeXT stations). .ip LDAPMAP Compile in support for LDAP X500 queries. Requires libldap and liblber from the Umich LDAP 3.2 or 3.3 release. .ip HESIOD Compile in support for Hesiod. .ip _PATH_SENDMAILCF The pathname of the sendmail.cf file. .ip _PATH_SENDMAILPID The pathname of the sendmail.pid file. .pp There are also several compilation flags to indicate the environment such as .q _AIX3 and .q _SCO_unix_ . See the READ_ME file for the latest scoop on these flags. .sh 2 "Parameters in src/conf.h" .pp Parameters and compilation options are defined in conf.h. Most of these need not normally be tweaked; common parameters are all in sendmail.cf. However, the sizes of certain primitive vectors, etc., are included in this file. The numbers following the parameters are their default value. .pp This document is not the best source of information for compilation flags in conf.h \(em see src/READ_ME or src/conf.h itself. .nr ii 1.2i .ip "MAXLINE [2048]" The maximum line length of any input line. If message lines exceed this length they will still be processed correctly; however, header lines, configuration file lines, alias lines, etc., must fit within this limit. .ip "MAXNAME [256]" The maximum length of any name, such as a host or a user name. .ip "MAXPV [40]" The maximum number of parameters to any mailer. This limits the number of recipients that may be passed in one transaction. It can be set to any arbitrary number above about 10, since .i sendmail will break up a delivery into smaller batches as needed. A higher number may reduce load on your system, however. .ip "MAXATOM [100]" The maximum number of atoms (tokens) in a single address. For example, the address .q "eric@CS.Berkeley.EDU" is seven atoms. .ip "MAXMAILERS [25]" The maximum number of mailers that may be defined in the configuration file. .ip "MAXRWSETS [200]" The maximum number of rewriting sets that may be defined. The first half of these are reserved for numeric specification (e.g., ``S92''), while the upper half are reserved for auto-numbering (e.g., ``Sfoo''). Thus, with a value of 200 an attempt to use ``S99'' will succeed, but ``S100'' will fail. .ip "MAXPRIORITIES [25]" The maximum number of values for the .q Precedence: field that may be defined (using the .b P line in sendmail.cf). .ip "MAXUSERENVIRON [100]" The maximum number of items in the user environment that will be passed to subordinate mailers. .ip "MAXMXHOSTS [100]" The maximum number of MX records we will accept for any single host. .ip "MAXALIASDB [12]" The maximum number of alias databases that can be open at any time. Note that there may also be an open file limit. .ip "MAXMAPSTACK [12]" The maximum number of maps that may be "stacked" in a .b sequence class map. .ip "MAXMIMEARGS [20]" The maximum number of arguments in a MIME Content-Type: header; additional arguments will be ignored. .ip "MAXMIMENESTING [20]" The maximum depth to which MIME messages may be nested (that is, nested Message or Multipart documents; this does not limit the number of components in a single Multipart document). .lp A number of other compilation options exist. These specify whether or not specific code should be compiled in. Ones marked with \(dg are 0/1 valued. .nr ii 1.2i .ip NETINET\(dg If set, support for Internet protocol networking is compiled in. Previous versions of .i sendmail referred to this as .sm DAEMON ; this old usage is now incorrect. Defaults on; turn it off in the Makefile if your system doesn't support the Internet protocols. .ip NETISO\(dg If set, support for ISO protocol networking is compiled in (it may be appropriate to #define this in the Makefile instead of conf.h). .ip LOG If set, the .i syslog routine in use at some sites is used. This makes an informational log record for each message processed, and makes a higher priority log record for internal system errors. .b "STRONGLY RECOMMENDED" \(em if you want no logging, turn it off in the configuration file. .ip MATCHGECOS\(dg Compile in the code to do ``fuzzy matching'' on the GECOS field in /etc/passwd. This also requires that the .b MatchGECOS option be turned on. .ip NAMED_BIND\(dg Compile in code to use the Berkeley Internet Name Domain (BIND) server to resolve TCP/IP host names. .ip NOTUNIX If you are using a non-UNIX mail format, you can set this flag to turn off special processing of UNIX-style .q "From " lines. .ip QUEUE\(dg This flag should be set to compile in the queueing code. If this is not set, mailers must accept the mail immediately or it will be returned to the sender. .ip SMTP\(dg If set, the code to handle user and server SMTP will be compiled in. This is only necessary if your machine has some mailer that speaks SMTP (this means most machines everywhere). .ip USERDB\(dg Include the .b experimental Berkeley user information database package. This adds a new level of local name expansion between aliasing and forwarding. It also uses the NEWDB package. This may change in future releases. .lp The following options are normally turned on in per-operating-system clauses in conf.h. .ip IDENTPROTO\(dg Compile in the IDENT protocol as defined in RFC 1413. This defaults on for all systems except Ultrix, which apparently has the interesting .q feature that when it receives a .q "host unreachable" message it closes all open connections to that host. Since some firewall gateways send this error code when you access an unauthorized port (such as 113, used by IDENT), Ultrix cannot receive email from such hosts. .ip SYSTEM5 Set all of the compilation parameters appropriate for System V. .ip HASFLOCK\(dg Use Berkeley-style .b flock instead of System V .b lockf to do file locking. Due to the highly unusual semantics of locks across forks in .b lockf , this should always be used if at all possible. .ip HASINITGROUPS Set this if your system has the .i initgroups() call (if you have multiple group support). This is the default if SYSTEM5 is .i not defined or if you are on HPUX. .ip HASUNAME Set this if you have the .i uname (2) system call (or corresponding library routine). Set by default if SYSTEM5 is set. .ip HASGETDTABLESIZE Set this if you have the .i getdtablesize (2) system call. .ip HASWAITPID Set this if you have the .i haswaitpid (2) system call. .ip SFS_TYPE The mechanism that can be used to get file system capacity information. The values can be one of SFS_USTAT (use the ustat(2) syscall), SFS_4ARGS (use the four argument statfs(2) syscall), SFS_VFS (use the two argument statfs(2) syscall including ), SFS_MOUNT (use the two argument statfs(2) syscall including ), SFS_STATFS (use the two argument statfs(2) syscall including ), SFS_STATVFS (use the two argument statfs(2) syscall including ), or SFS_NONE (no way to get this information). .ip LA_TYPE The load average type. Details are described below. .lp The are several built-in ways of computing the load average. .i Sendmail tries to auto-configure them based on imperfect guesses; you can select one using the .i cc option .b \-DLA_TYPE= \c .i type , where .i type is: .ip LA_INT The kernel stores the load average in the kernel as an array of long integers. The actual values are scaled by a factor FSCALE (default 256). .ip LA_SHORT The kernel stores the load average in the kernel as an array of short integers. The actual values are scaled by a factor FSCALE (default 256). .ip LA_FLOAT The kernel stores the load average in the kernel as an array of double precision floats. .ip LA_MACH Use MACH-style load averages. .ip LA_SUBR Call the .i getloadavg routine to get the load average as an array of doubles. .ip LA_ZERO Always return zero as the load average. This is the fallback case. .lp If type .sm LA_INT , .sm LA_SHORT , or .sm LA_FLOAT is specified, you may also need to specify .sm _PATH_UNIX (the path to your system binary) and .sm LA_AVENRUN (the name of the variable containing the load average in the kernel; usually .q _avenrun or .q avenrun ). .sh 2 "Configuration in src/conf.c" .pp The following changes can be made in conf.c. .sh 3 "Built-in Header Semantics" .pp Not all header semantics are defined in the configuration file. Header lines that should only be included by certain mailers (as well as other more obscure semantics) must be specified in the .i HdrInfo table in .i conf.c . This table contains the header name (which should be in all lower case) and a set of header control flags (described below), The flags are: .ip H_ACHECK Normally when the check is made to see if a header line is compatible with a mailer, .i sendmail will not delete an existing line. If this flag is set, .i sendmail will delete even existing header lines. That is, if this bit is set and the mailer does not have flag bits set that intersect with the required mailer flags in the header definition in sendmail.cf, the header line is .i always deleted. .ip H_EOH If this header field is set, treat it like a blank line, i.e., it will signal the end of the header and the beginning of the message text. .ip H_FORCE Add this header entry even if one existed in the message before. If a header entry does not have this bit set, .i sendmail will not add another header line if a header line of this name already existed. This would normally be used to stamp the message by everyone who handled it. .ip H_TRACE If set, this is a timestamp (trace) field. If the number of trace fields in a message exceeds a preset amount the message is returned on the assumption that it has an aliasing loop. .ip H_RCPT If set, this field contains recipient addresses. This is used by the .b \-t flag to determine who to send to when it is collecting recipients from the message. .ip H_FROM This flag indicates that this field specifies a sender. The order of these fields in the .i HdrInfo table specifies .i sendmail 's preference for which field to return error messages to. .ip H_ERRORSTO Addresses in this header should receive error messages. .ip H_CTE This header is a Content-Transfer-Encoding header. .ip H_CTYPE This header is a Content-Type header. .ip H_STRIPVAL Strip the value from the header (for Bcc:). .nr ii 5n .lp Let's look at a sample .i HdrInfo specification: .(b .ta 4n +\w'"content-transfer-encoding", 'u struct hdrinfo HdrInfo[] = \&{ /* originator fields, most to least significant */ "resent-sender", H_FROM, "resent-from", H_FROM, "sender", H_FROM, "from", H_FROM, "full-name", H_ACHECK, "errors-to", H_FROM\^|\^H_ERRORSTO, /* destination fields */ "to", H_RCPT, "resent-to", H_RCPT, "cc", H_RCPT, "bcc", H_RCPT\^|\^H_STRIPVAL, /* message identification and control */ "message", H_EOH, "text", H_EOH, /* trace fields */ "received", H_TRACE\^|\^H_FORCE, /* miscellaneous fields */ "content-transfer-encoding", H_CTE, "content-type", H_CTYPE, NULL, 0, }; .)b This structure indicates that the .q To: , .q Resent-To: , and .q Cc: fields all specify recipient addresses. Any .q Full-Name: field will be deleted unless the required mailer flag (indicated in the configuration file) is specified. The .q Message: and .q Text: fields will terminate the header; these are used by random dissenters around the network world. The .q Received: field will always be added, and can be used to trace messages. .pp There are a number of important points here. First, header fields are not added automatically just because they are in the .i HdrInfo structure; they must be specified in the configuration file in order to be added to the message. Any header fields mentioned in the configuration file but not mentioned in the .i HdrInfo structure have default processing performed; that is, they are added unless they were in the message already. Second, the .i HdrInfo structure only specifies cliched processing; certain headers are processed specially by ad hoc code regardless of the status specified in .i HdrInfo . For example, the .q Sender: and .q From: fields are always scanned on ARPANET mail to determine the sender\**; .(f \**Actually, this is no longer true in SMTP; this information is contained in the envelope. The older ARPANET protocols did not completely distinguish envelope from header. .)f this is used to perform the .q "return to sender" function. The .q "From:" and .q "Full-Name:" fields are used to determine the full name of the sender if possible; this is stored in the macro .b $x and used in a number of ways. .sh 3 "Restricting Use of Email" .pp If it is necessary to restrict mail through a relay, the .i checkcompat routine can be modified. This routine is called for every recipient address. It returns an exit status indicating the status of the message. The status .sm EX_OK accepts the address, .sm EX_TEMPFAIL queues the message for a later try, and other values (commonly .sm EX_UNAVAILABLE ) reject the message. It is up to .i checkcompat to print an error message (using .i usrerr ) if the message is rejected. For example, .i checkcompat could read: .(b .re .sz -1 .ta 4n +4n +4n +4n +4n +4n +4n int checkcompat(to, e) register ADDRESS *to; register ENVELOPE *e; \&{ register STAB *s; s = stab("private", ST_MAILER, ST_FIND); if (s != NULL && e\->e_from.q_mailer != LocalMailer && to->q_mailer == s->s_mailer) { usrerr("No private net mail allowed through this machine"); return (EX_UNAVAILABLE); } if (MsgSize > 50000 && bitnset(M_LOCALMAILER, to\->q_mailer)) { usrerr("Message too large for non-local delivery"); e\->e_flags |= EF_NORETURN; return (EX_UNAVAILABLE); } return (EX_OK); } .sz .)b This would reject messages greater than 50000 bytes unless they were local. The .i EF_NORETURN flag can be set in .i e\(->e_flags to suppress the return of the actual body of the message in the error return. The actual use of this routine is highly dependent on the implementation, and use should be limited. .sh 3 "Load Average Computation" .pp The routine .i getla should return an approximation of the current system load average as an integer. There are several versions included on compilation flags as described above. .sh 3 "New Database Map Classes" .pp New key maps can be added by creating a class initialization function and a lookup function. These are then added to the routine .i setupmaps. .pp The initialization function is called as .(b -\fIxxx\fP_map_init(MAP *map, char *mapname, char *args) +\fIxxx\fP_map_init(MAP *map, char *args) .)b The .i map is an internal data structure. The -.i mapname -is the name of the map (used for error messages). -The .i args -is a pointer to the rest of the configuration file line; +is a pointer to the portion of the configuration file line +following the map class name; flags and filenames can be extracted from this line. The initialization function must return .sm TRUE if it successfully opened the map, .sm FALSE otherwise. .pp The lookup function is called as .(b -\fIxxx\fP_map_lookup(MAP *map, char buf[], int bufsize, char **av, int *statp) +\fIxxx\fP_map_lookup(MAP *map, char buf[], char **av, int *statp) .)b The .i map defines the map internally. -The parameters +The .i buf -and -.i bufsize -have the input key. +has the input key. This may be (and often is) used destructively. The .i av is a list of arguments passed in from the rewrite line. The lookup function should return a pointer to the new value. IF the map lookup fails, .i *statp should be set to an exit status code; in particular, it should be set to .sm EX_TEMPFAIL if recovery is to be attempted by the higher level code. .sh 3 "Queueing Function" .pp The routine .i shouldqueue is called to decide if a message should be queued or processed immediately. Typically this compares the message priority to the current load average. The default definition is: .(b bool shouldqueue(pri, ctime) long pri; time_t ctime; { if (CurrentLA < QueueLA) return (FALSE); return (pri > (QueueFactor / (CurrentLA \- QueueLA + 1))); } .)b If the current load average (global variable .i CurrentLA , which is set before this function is called) is less than the low threshold load average (option .b x , variable .i QueueLA ), .i shouldqueue returns .sm FALSE immediately (that is, it should .i not queue). If the current load average exceeds the high threshold load average (option .b X , variable .i RefuseLA ), .i shouldqueue returns .sm TRUE immediately. Otherwise, it computes the function based on the message priority, the queue factor (option .b q , global variable .i QueueFactor ), and the current and threshold load averages. .pp An implementation wishing to take the actual age of the message into account can also use the .i ctime parameter, which is the time that the message was first submitted to .i sendmail . Note that the .i pri parameter is already weighted by the number of times the message has been tried (although this tends to lower the priority of the message with time); the expectation is that the .i ctime would be used as an .q "escape clause" to ensure that messages are eventually processed. .sh 3 "Refusing Incoming SMTP Connections" .pp The function .i refuseconnections returns .sm TRUE if incoming SMTP connections should be refused. The current implementation is based exclusively on the current load average and the refuse load average option (option .b X , global variable .i RefuseLA ): .(b bool refuseconnections() { return (CurrentLA >= RefuseLA); } .)b A more clever implementation could look at more system resources. .sh 3 "Load Average Computation" .pp The routine .i getla returns the current load average (as a rounded integer). The distribution includes several possible implementations. If you are porting to a new environment you may need to add some new tweaks.\** .(f \**If you do, please send updates to sendmail@Sendmail.ORG. .)f .sh 2 "Configuration in src/daemon.c" .pp The file .i src/daemon.c contains a number of routines that are dependent on the local networking environment. The version supplied assumes you have BSD style sockets. .pp In previous releases, we recommended that you modify the routine .i maphostname if you wanted to generalize .b $[ \&...\& .b $] lookups. We now recommend that you create a new keyed map instead. .sh 1 "CHANGES IN VERSION 8" .pp The following summarizes changes since the last commonly available version of .i sendmail (5.67). For a detailed list, consult the file RELEASE_NOTES in the root directory of the .i sendmail distribution. .sh 2 "Connection Caching" .pp Instead of closing SMTP connections immediately, those connections are cached for possible future use. The advent of MX records made this effective for mailing lists; in addition, substantial performance improvements can be expected for queue processing. .sh 2 "MX Piggybacking" .pp If two hosts with different names in a single message happen to have the same set of MX hosts, they can be sent in the same transaction. Version 8 notices this and tries to batch the messages. .sh 2 "RFC 1123 Compliance" .pp A number of changes have been made to make .i sendmail .q "conditionally compliant" (that is, .i sendmail satisfies all of the .q MUST clauses and most but not all of the .q SHOULD clauses in RFC 1123). .pp The major areas of change are (numbers are RFC 1123 section numbers): .nr ii \w'5.3.1.1\0\0'u .ip 5.2.7 Response to RCPT command is fast. .ip 5.2.8 Numeric IP addresses are logged in Received: lines. .ip 5.2.17 Self domain literal is properly handled. .ip 5.3.2 Better control over individual timeouts. .ip 5.3.3 Error messages are sent as .q From:<> . .ip 5.3.3 Error messages are never sent to .q <> . .ip 5.3.3 Route-addrs are pruned. .lp The areas in which .i sendmail is not .q "unconditionally compliant" are: .ip 5.2.6 .i Sendmail does do header munging. .ip 5.2.10 .i Sendmail doesn't always use the exact SMTP message text as listed in RFC 821. .ip 5.3.1.1 .i Sendmail doesn't guarantee only one connect for each host in queue runs. .ip 5.3.1.1 .i Sendmail doesn't always provide adequate concurrency limits. .sh 2 "Extended SMTP Support" .pp Version 8 includes both sending and receiving support for Extended SMTP support as defined by RFC 1651 (basic) and RFC 1653 (SIZE); and limited support for RFC 1652 (BODY). .sh 2 "Eight-Bit Clean" .pp Previous versions of .i sendmail used the 0200 bit for quoting. This version avoids that use. However, for compatibility with RFC 822, you can set option `7' to get seven bit stripping. .pp Individual mailers can still produce seven bit output using the `7' mailer flag. .sh 2 "User Database" .pp The user database is an as-yet experimental attempt to provide unified large-site name support. We are installing it at Berkeley; future versions may show significant modifications. .sh 2 "Improved BIND Support" .pp The BIND support, particularly for MX records, had a number of annoying .q features which have been removed in this release. In particular, these more tightly bind (pun intended) the name server to .i sendmail , so that the name server resolution rules are incorporated directly into .b sendmail . .sh 2 "Keyed Files" .pp Generalized keyed files is an idea taken directly from .sm IDA .i sendmail (albeit with a completely different implementation). They can be useful on large sites. .pp Version 8 also understands YP. .sh 2 "Multi-Word Classes" .pp Classes can now be multiple words. For example, .(b CShofmann.CS.Berkeley.EDU .)b allows you to match the entire string .q hofmann.CS.Berkeley.EDU using the single construct .q $=S . .sh 2 "Deferred Macro Expansion" .pp The .b $& \c .i x construct has been adopted from .sm IDA . .sh 2 "IDENT Protocol Support" .pp The IDENT protocol as defined in RFC 1413 is supported. .sh 2 "Parsing Bug Fixes" .pp A number of small bugs having to do with things like backslash-escaped quotes inside of comments have been fixed. .sh 2 "Separate Envelope/Header Processing" .pp Since the From: line is passed in separately from the envelope sender, these have both been made visible; the .b $g macro is set to the envelope sender during processing of mailer argument vectors and the header sender during processing of headers. .pp It is also possible to specify separate per-mailer envelope and header processing. The .b S enderRWSet and .b R ecipientRWset arguments for mailers can be specified as .i envelope/header to give different rewritings for envelope versus header addresses. .sh 2 "Owner-List Propagates to Envelope" .pp When an alias has an associated owner\-list name, that alias is used to change the envelope sender address. This will cause downstream errors to be returned to that owner. .sh 2 "Dynamic Header Allocation" .pp The fixed size limit on header lines has been eliminated. .sh 2 "New Command Line Flags" .pp The .b \-B flag has been added to pass in body type information. .pp The .b \-p flag has been added to pass in protocol information. .pp The .b \-X flag has been added to allow logging of all protocol in and out of .i sendmail for debugging. .pp The .b \-O flag implies setting long-form options. .sh 2 "Enhanced Command Line Flags" .pp The .b \-q flag can limit limit a queue run to specific recipients, senders, or queue ids using .b \-qR\c .i substring , .b \-qS\c .i substring , or .b \-qI\c .i substring respectively. .sh 2 "New and Old Configuration Line Types" .pp The .b K line has been added to declare database maps. .pp The .b V line has been added to declare the configuration version level. .pp The .b M line has a .q D= field that lets you change into a temporary directory while that mailer is running. It also has a .q U= field to allow you to set the user and group id to be used when running the mailer. .sh 2 "New Options" .pp Several new options have been added, many to support new features, others to allow tuning that was previously available only by recompiling. They are described in detail in Section 5.6. Briefly, .nr ii 0.5i .ip b Insist on a minimum number of disk blocks. .ip C Set checkpoint interval. .ip E Default error message. .ip G Enable GECOS matching. .ip h Maximum hop count. .ip j Send errors in MIME-encapsulated format. .ip J Forward file path. .ip k Connection cache size .ip K Connection cache lifetime. .ip l Enable Errors-To: header. These headers violate RFC 1123; this option is included to provide back compatibility with old versions of .i sendmail . .ip O Set incoming SMTP daemon options, such as an alternate SMTP port. .ip p Privacy options. .ip R Don't prune route-addrs. .ip U User database spec. .ip V Fallback .q MX host. .ip w .q "Best MX" handling technique. .ip 7 Do not run eight bit clean. .ip 8 Eight bit data handling mode. .sh 2 "Extended Options" .pp The .b r (read timeout), .b I (use BIND), and .b T (queue timeout) options have been extended to pass in more information. .sh 2 "New Mailer Flags" .pp Several new mailer flags have been added. .ip a Try to use ESMTP when creating a connection. If this is not set, .i sendmail will still try if the other end hints that it knows about ESMTP in its greeting message; this flag says to try even if it doesn't hint. If the EHLO (extended hello) command fails, .i sendmail falls back to old SMTP. .ip A Try the user part of addresses for this mailer as aliases. .ip b Ensure that there is a blank line at the end of all messages. .ip c Strip all comments from addresses; this should only be used as a last resort when dealing with cranky mailers. .ip g Never use the null sender as the envelope sender, even when running SMTP. Although this violates RFC 1123, it may be necessary when you must deal with some obnoxious old hosts. .ip k Turn off the loopback check in the HELO protocol; doing this may cause mailer loops. .ip o Always run the mailer as the recipient of the message. .ip w This user should have a passwd file entry. .ip 5 Try ruleset 5 if no local aliases. .ip 7 Strip all output to 7 bits. .ip : Check for :include: files. .ip | Check for |program addresses. .ip / Check for /file addresses. .ip @ Check this user against the user database. .sh 2 "Long Option Names" .pp All options can be specified using long names, and some new options can only be specified with long names. .sh 2 "New Pre-Defined Macros" .pp The following macros are pre-defined: .ip $k The UUCP node name, nominally from .i uname (2) call. .ip $m The domain part of our full hostname. .ip $_ The RFC 1413-provided sender address. .sh 2 "New LHS Token" .pp Version 8 allows .b $@ on the Left Hand Side of an .q R line to match zero tokens. This is intended to be used to match the null input. .sh 2 "Bigger Defaults" .pp Version 8 allows up to 100 rulesets instead of 30. It is recommended that rulesets 0\-9 be reserved for .i sendmail 's dedicated use in future releases. .pp The total number of MX records that can be used has been raised to 20. .pp The number of queued messages that can be handled at one time has been raised from 600 to 1000. .sh 2 "Different Default Tuning Parameters" .pp Version 8 has changed the default parameters for tuning queue costs to make the number of recipients more important than the size of the message (for small messages). This is reasonable if you are connected with reasonably fast links. .sh 2 "Auto-Quoting in Addresses" .pp Previously, the .q "Full Name " syntax would generate incorrect protocol output if .q "Full Name" had special characters such as dot. This version puts quotes around such names. .sh 2 "Symbolic Names On Error Mailer" .pp Several names have been built in to the $@ portion of the $#error mailer. .sh 2 "SMTP VRFY Doesn't Expand" .pp Previous versions of .i sendmail treated VRFY and EXPN the same. In this version, VRFY doesn't expand aliases or follow .forward files. EXPN still does. .pp As an optimization, if you run with your default delivery mode being queue-only or deliver-in-background, the RCPT command will also not chase aliases and .forward files. It will chase them when it processes the queue. .sh 2 "[IPC] Mailers Allow Multiple Hosts" .pp When an address resolves to a mailer that has .q [IPC] as its .q Path , the $@ part (host name) can be a colon-separated list of hosts instead of a single hostname. This asks .i sendmail to search the list for the first entry that is available exactly as though it were an MX record. The intent is to route internal traffic through internal networks without publishing an MX record to the net. MX expansion is still done on the individual items. .sh 2 "Aliases Extended" .pp The implementation has been merged with maps. Among other things, this supports NIS-based aliases. .sh 2 "Portability and Security Enhancements" .pp A number of internal changes have been made to enhance portability. .pp Several fixes have been made to increase the paranoia factor. .sh 2 "Miscellaneous Changes" .pp .i Sendmail writes a .i /etc/sendmail.pid file with the current process id of the SMTP daemon. .pp Two people using the same program in their .forward file are considered different so that duplicate elimination doesn't delete one of them. .pp The .i mailstats program prints mailer names and gets the location of the .i sendmail.st file from .i /etc/sendmail.cf . .pp Many minor bugs have been fixed, such as handling of backslashes inside of quotes. .pp A hook (ruleset 5) has been added to allow rewriting of local addresses after aliasing. .sh 1 "ACKNOWLEDGEMENTS" .pp I've worked on .i sendmail for many years, and many employers have been remarkably patient about letting me work on a large project that was not part of my official job. This includes time on the INGRES Project at the University of California at Berkeley, at Britton Lee, and again on the Mammoth and Titan Projects at Berkeley. .pp Much of the second wave of improvements should be credited to Bryan Costales of ICSI. As he passed me drafts of his book on .i sendmail I was inspired to start working on things again. Bryan was also available to bounce ideas off of. .pp Many, many people contributed chunks of code and ideas to .i sendmail . It has proven to be a group network effort. Version 8 in particular was a group project. The following people made notable contributions: .(l John Beck, Hewlett-Packard Keith Bostic, CSRG, University of California, Berkeley Andrew Cheng, Sun Microsystems Michael J. Corrigan, University of California, San Diego Bryan Costales, International Computer Science Institute Pa\*:r (Pell) Emanuelsson Craig Everhart, Transarc Corporation Tom Ivar Helbekkmo, Norwegian School of Economics Allan E. Johannesen, WPI Jonathan Kamens, OpenVision Technologies, Inc. Takahiro Kanbe, Fuji Xerox Information Systems Co., Ltd. Brian Kantor, University of California, San Diego Murray S. Kucherawy, HookUp Communication Corp. Bruce Lilly, Sony U.S. Karl London Motonori Nakamura, Ritsumeikan University & Kyoto University John Gardiner Myers, Carnegie Mellon University Neil Rickert, Northern Illinois University Eric Schnoebelen, Convex Computer Corp. Eric Wassenaar, National Institute for Nuclear and High Energy Physics, Amsterdam Christophe Wolfhugel, Pasteur Institute & Herve Schauer Consultants (Paris) .)l I apologize for anyone I have omitted, misspelled, misattributed, or otherwise missed. At this point, I suspect that at least a hundred people have contributed code, and many more have contributed ideas, comments, and encouragement. I've tried to list them in the RELEASE_NOTES in the distribution directory. I appreciate their contribution as well. .pp Special thanks are reserved for Michael Corrigan and Christophe Wolfhugel, who besides being wonderful guinea pigs and contributors have also consented to be added to the ``sendmail@Sendmail.ORG'' list and, by answering the bulk of the questions sent to that list, have freed me up to do other work. .++ A .+c "COMMAND LINE FLAGS" .ba 0 .nr ii 1i .pp Arguments must be presented with flags before addresses. The flags are: .ip \-b\fIx\fP Set operation mode to .i x . Operation modes are: .(b .ta 4n m Deliver mail (default) s Speak SMTP on input side a\(dg ``Arpanet'' mode (get envelope sender information from header) d Run as a daemon in background D Run as a daemon in foreground t Run in test mode v Just verify addresses, don't collect or deliver i Initialize the alias database p Print the mail queue .)b .(f \(dgDeprecated. .)f .ip \-B\fItype\fP Indicate body type. .ip \-C\fIfile\fP Use a different configuration file. .i Sendmail runs as the invoking user (rather than root) when this flag is specified. .ip \-d\fIlevel\fP Set debugging level. .ip "\-f\ \fIaddr\fP" The sender's machine address is .i addr . .ip \-F\fIname\fP Sets the full name of this user to .i name . .ip "\-h\ \fIcnt\fP" Sets the .q "hop count" to .i cnt . This represents the number of times this message has been processed by .i sendmail (to the extent that it is supported by the underlying networks). .i Cnt is incremented during processing, and if it reaches MAXHOP (currently 30) .i sendmail throws away the message with an error. .ip \-n Don't do aliasing or forwarding. .ip "\-N \fInotifications\fP" Tag all addresses being sent as wanting the indicated .i notifications , which consists of the word .q NEVER or a comma-separated list of .q SUCCESS , .q FAILURE , and .q DELAY for successful delivery, failure, and a message that is stuck in a queue somewhere. The default is .q FAILURE,DELAY . .ip "\-r\ \fIaddr\fP" An obsolete form of .b \-f . .ip \-o\fIx\|value\fP Set option .i x to the specified .i value . These options are described in Section 5.6. .ip \-O\fIoption\fP\fB=\fP\fIvalue\fP Set .i option to the specified .i value (for long form option names). These options are described in Section 5.6. .ip \-M\fIx\|value Set macro .i x to the specified .i value . .ip \-p\fIprotocol\fP Set the sending protocol. Programs are encouraged to set this. The protocol field can be in the form .i protocol \c .b : \c .i host to set both the sending protocol and sending host. For example, .q \-pUUCP:uunet sets the sending protocol to UUCP and the sending host to uunet. (Some existing programs use \-oM to set the r and s macros; this is equivalent to using \-p.) .ip \-q\fItime\fP Try to process the queued up mail. If the time is given, a .i sendmail will run through the queue at the specified interval to deliver queued mail; otherwise, it only runs once. .ip \-q\fIXstring\fP Run the queue once, limiting the jobs to those matching .i Xstring . The key letter .i X can be .b I to limit based on queue identifier, .b R to limit based on recipient, or .b S to limit based on sender. A particular queued job is accepted if one of the corresponding addresses contains the indicated .i string . .ip "\-R ret" What information you want returned if the message bounces; .i ret can be .q HDRS for headers only or .q FULL for headers plus body. This is a request only; the other end is not required to honor the parameter. .ip \-t Read the header for .q To: , .q Cc: , and .q Bcc: lines, and send to everyone listed in those lists. The .q Bcc: line will be deleted before sending. Any addresses in the argument vector will be deleted from the send list. .ip "\-U" Indicate that this is an initial User Agent submission. In future releases, sendmail may complain about syntactically invalid messages rather than fixing them when this flag is not set. .ip "\-V envid" The indicated .i envid is passed with the envelope of the message and returned if the message bounces. .ip "\-X \fIlogfile\fP" Log all traffic in and out of .i sendmail in the indicated .i logfile for debugging mailer problems. This produces a lot of data very quickly and should be used sparingly. .pp There are a number of options that may be specified as primitive flags. These are the e, i, m, and v options. Also, the f option may be specified as the .b \-s flag. .+c "QUEUE FILE FORMATS" .pp This appendix describes the format of the queue files. These files live in the directory defined by the .b Q option in the .i sendmail.cf file, usually .i /var/spool/mqueue or .i /usr/spool/mqueue . .pp All queue files have the name \fIx\fP\|\fBf\fP\fIAAA99999\fP where .i AAA99999 is the .i id for this message and the .i x is a type. The first letter of the id encodes the hour of the day that the message was received by the system (with A being the hour between midnight and 1:00AM). All files with the same id collectively define one message. .pp The types are: .nr ii 0.5i .ip d The data file. The message body (excluding the header) is kept in this file. .ip q The queue control file. This file contains the information necessary to process the job. .ip t A temporary file. These are an image of the .b qf file when it is being rebuilt. It should be renamed to a .b qf file very quickly. .ip x A transcript file, existing during the life of a session showing everything that happens during that session. .pp The .b qf file is structured as a series of lines each beginning with a code letter. The lines are as follows: .ip V The version number of the queue file format, used to allow new .i sendmail binaries to read queue files created by older versions. Defaults to version zero. Must be the first line of the file if present. .ip H A header definition. There may be any number of these lines. The order is important: they represent the order in the final message. These use the same syntax as header definitions in the configuration file. .ip C The controlling address. The syntax is .q localuser:aliasname . Recipient addresses following this line will be flagged so that deliveries will be run as the .i localuser (a user name from the /etc/passwd file); .i aliasname is the name of the alias that expanded to this address (used for printing messages). .ip Q The ``original recipient'', specified by the ORCPT= field in an ESMTP transaction. Used exclusively for Delivery Status Notifications. It applies only to the immediately following `R' line. .ip R A recipient address. This will normally be completely aliased, but is actually realiased when the job is processed. There will be one line for each recipient. Version 1 qf files also include a leading colon-terminated list of flags, which can be `S' to return a message on successful final delivery, `F' to return a message on failure, `D' to return a message if the message is delayed, `B' to indicate that the body should be returned, `N' to suppress returning the body, and `P' to declare this as a ``primary'' (command line or SMTP-session) address. .ip S The sender address. There may only be one of these lines. .ip T The job creation time. This is used to compute when to time out the job. .ip P The current message priority. This is used to order the queue. Higher numbers mean lower priorities. The priority changes as the message sits in the queue. The initial priority depends on the message class and the size of the message. .ip M A message. This line is printed by the .i mailq command, and is generally used to store status information. It can contain any text. .ip F Flag bits, represented as one letter per flag. Defined flag bits are .b r indicating that this is a response message and .b w indicating that a warning message has been sent announcing that the mail has been delayed. .ip N The total number of delivery attempts. .ip K The time (as seconds since January 1, 1970) of the last delivery attempt. .ip I The i-number of the data file; this can be used to recover your mail queue after a disastrous disk crash. .ip $ A macro definition. The values of certain macros (as of this writing, only .b $r and .b $s ) are passed through to the queue run phase. .ip B The body type. The remainder of the line is a text string defining the body type. If this field is missing, the body type is assumed to be .q "undefined" and no special processing is attempted. Legal values are .q 7BIT and .q 8BITMIME . .ip O The original MTS value (from the ESMTP transaction). For Deliver Status Notifications only. .ip Z The original envelope id (from the ESMTP transaction). For Deliver Status Notifications only. .pp As an example, the following is a queue file sent to .q eric@mammoth.Berkeley.EDU and .q bostic@okeeffe.CS.Berkeley.EDU \**: .(f \**This example is contrived and probably inaccurate for your environment. Glance over it to get an idea; nothing can replace looking at what your own system generates. .)f .(b P835771 T404261372 Seric Ceric:sendmail@vangogh.CS.Berkeley.EDU Reric@mammoth.Berkeley.EDU Rbostic@okeeffe.CS.Berkeley.EDU H?P?return-path: Hreceived: by vangogh.CS.Berkeley.EDU (5.108/2.7) id AAA06703; Fri, 17 Jul 92 00:28:55 -0700 Hreceived: from mail.CS.Berkeley.EDU by vangogh.CS.Berkeley.EDU (5.108/2.7) id AAA06698; Fri, 17 Jul 92 00:28:54 -0700 Hreceived: from [128.32.31.21] by mail.CS.Berkeley.EDU (5.96/2.5) id AA22777; Fri, 17 Jul 92 03:29:14 -0400 Hreceived: by foo.bar.baz.de (5.57/Ultrix3.0-C) id AA22757; Fri, 17 Jul 92 09:31:25 GMT H?F?from: eric@foo.bar.baz.de (Eric Allman) H?x?full-name: Eric Allman Hmessage-id: <9207170931.AA22757@foo.bar.baz.de> HTo: sendmail@vangogh.CS.Berkeley.EDU Hsubject: this is an example message .)b This shows the person who sent the message, the submission time (in seconds since January 1, 1970), the message priority, the message class, the recipients, and the headers for the message. .+c "SUMMARY OF SUPPORT FILES" .pp This is a summary of the support files that .i sendmail creates or generates. Many of these can be changed by editing the sendmail.cf file; check there to find the actual pathnames. .nr ii 1i .ip "/usr/\*(SD/sendmail" The binary of .i sendmail . .ip /usr/\*(SB/newaliases A link to /usr/\*(SD/sendmail; causes the alias database to be rebuilt. Running this program is completely equivalent to giving .i sendmail the .b \-bi flag. .ip /usr/\*(SB/mailq Prints a listing of the mail queue. This program is equivalent to using the .b \-bp flag to .i sendmail . .ip /etc/sendmail.cf The configuration file, in textual form. .ip /usr/lib/sendmail.hf The SMTP help file. .ip /etc/sendmail.st A statistics file; need not be present. .ip /etc/sendmail.pid Created in daemon mode; it contains the process id of the current SMTP daemon. If you use this in scripts; use ``head \-1'' to get just the first line; later versions of .i sendmail may add information to subsequent lines. .ip /etc/aliases The textual version of the alias file. .ip /etc/aliases.{pag,dir} The alias file in .i dbm \|(3) format. .ip /var/spool/mqueue The directory in which the mail queue and temporary files reside. .ip /var/spool/mqueue/qf* Control (queue) files for messages. .ip /var/spool/mqueue/df* Data files. .ip /var/spool/mqueue/tf* Temporary versions of the qf files, used during queue file rebuild. .ip /var/spool/mqueue/xf* A transcript of the current session. .if e \ \{\ . bp . rs . sp |4i . ce 2 This page intentionally left blank; replace it with a blank sheet for double-sided output. .\} .\".ro .\".ls 1 .\".tp .\".sp 2i .\".in 0 .\".ce 100 .\".sz 24 .\".b SENDMAIL .\".sz 14 .\".sp .\"INSTALLATION AND OPERATION GUIDE .\".sp .\".sz 10 .\"Eric Allman .\".sp -.\"Version 8.103 +.\"Version 8.104 .\".ce 0 .bp 3 .ce .sz 12 TABLE OF CONTENTS .sz 10 .sp .\" remove some things to avoid "out of temp file space" problem .rm sh .rm (x .rm )x .rm ip .rm pp .rm lp .rm he .rm fo .rm eh .rm oh .rm ef .rm of .xp Index: vendor/sendmail/dist-old/usr.sbin/sendmail/mail.local/mail.local.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/mail.local/mail.local.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/mail.local/mail.local.c (revision 26986) @@ -1,922 +1,936 @@ /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1990, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mail.local.c 8.34 (Berkeley) 11/24/96"; +static char sccsid[] = "@(#)mail.local.c 8.39 (Berkeley) 5/28/97"; #endif /* not lint */ /* * This is not intended to compile on System V derived systems * such as Solaris or HP-UX, since they use a totally different * approach to mailboxes (essentially, they have a setgid program * rather than setuid, and they rely on the ability to "give away" * files to do their work). IT IS NOT A BUG that this doesn't * compile on such architectures. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EX_OK # undef EX_OK /* unistd.h may have another use for this */ #endif #include #include #if __STDC__ #include #else #include #endif #if (defined(sun) && defined(__svr4__)) || defined(__SVR4) # define USE_LOCKF 1 # define USE_SETEUID 1 # define _PATH_MAILDIR "/var/mail" #endif #if defined(_AIX) # define USE_LOCKF 1 +# define USET_SETEUID 1 # define USE_VSYSLOG 0 #endif #if defined(ultrix) # define USE_VSYSLOG 0 #endif #if defined(__osf__) # define USE_VSYSLOG 0 #endif #if defined(NeXT) # include # define _PATH_MAILDIR "/usr/spool/mail" # define __dead /* empty */ # define S_IRUSR S_IREAD # define S_IWUSR S_IWRITE #endif /* * If you don't have flock, you could try using lockf instead. */ #ifdef USE_LOCKF # define flock(a, b) lockf(a, b, 0) # define LOCK_EX F_LOCK #endif #ifndef USE_VSYSLOG # define USE_VSYSLOG 1 #endif #ifndef LOCK_EX # include #endif #ifdef BSD4_4 # include "pathnames.h" #endif #ifndef __P # ifdef __STDC__ # define __P(protos) protos # else # define __P(protos) () # define const # endif #endif #ifndef __dead # if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__) # define __dead __volatile # else # define __dead # endif #endif #ifndef BSD4_4 # define _BSD_VA_LIST_ va_list #endif +#if defined(BSD4_4) || defined(linux) +# define HASSNPRINTF 1 +#endif + +#if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) +# define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ +#endif + #if !defined(BSD4_4) && !defined(linux) extern char *strerror __P((int)); -extern int snprintf __P((char *, int, const char *, ...)); +extern int snprintf __P((char *, size_t, const char *, ...)); extern FILE *fdopen __P((int, const char *)); #endif /* * If you don't have setreuid, and you have saved uids, and you have * a seteuid() call that doesn't try to emulate using setuid(), then * you can try defining USE_SETEUID. */ #ifdef USE_SETEUID # define setreuid(r, e) seteuid(e) #endif #ifndef _PATH_LOCTMP # define _PATH_LOCTMP "/tmp/local.XXXXXX" #endif #ifndef _PATH_MAILDIR # define _PATH_MAILDIR "/var/spool/mail" #endif #ifndef S_ISREG # define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG) #endif int eval = EX_OK; /* sysexits.h error value. */ void deliver __P((int, char *)); void e_to_sys __P((int)); __dead void err __P((const char *, ...)); void notifybiff __P((char *)); int store __P((char *)); void usage __P((void)); void vwarn __P((const char *, _BSD_VA_LIST_)); void warn __P((const char *, ...)); void lockmbox __P((char *)); void unlockmbox __P((void)); int main(argc, argv) int argc; char *argv[]; { struct passwd *pw; int ch, fd; uid_t uid; char *from; extern char *optarg; extern int optind; /* make sure we have some open file descriptors */ for (fd = 10; fd < 30; fd++) (void) close(fd); /* use a reasonable umask */ (void) umask(0077); #ifdef LOG_MAIL openlog("mail.local", 0, LOG_MAIL); #else openlog("mail.local", 0); #endif from = NULL; while ((ch = getopt(argc, argv, "df:r:")) != EOF) switch(ch) { case 'd': /* Backward compatible. */ break; case 'f': case 'r': /* Backward compatible. */ if (from != NULL) { warn("multiple -f options"); usage(); } from = optarg; break; case '?': default: usage(); } argc -= optind; argv += optind; if (!*argv) usage(); /* * If from not specified, use the name from getlogin() if the * uid matches, otherwise, use the name from the password file * corresponding to the uid. */ uid = getuid(); if (!from && (!(from = getlogin()) || !(pw = getpwnam(from)) || pw->pw_uid != uid)) from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; /* * There is no way to distinguish the error status of one delivery * from the rest of the deliveries. So, if we failed hard on one * or more deliveries, but had no failures on any of the others, we * return a hard failure. If we failed temporarily on one or more * deliveries, we return a temporary failure regardless of the other * failures. This results in the delivery being reattempted later * at the expense of repeated failures and multiple deliveries. */ for (fd = store(from); *argv; ++argv) deliver(fd, *argv); exit(eval); } int store(from) char *from; { FILE *fp; time_t tval; int fd, eline; char line[2048]; char tmpbuf[sizeof _PATH_LOCTMP + 1]; strcpy(tmpbuf, _PATH_LOCTMP); if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { e_to_sys(errno); err("unable to open temporary file"); } (void)unlink(tmpbuf); (void)time(&tval); (void)fprintf(fp, "From %s %s", from, ctime(&tval)); line[0] = '\0'; for (eline = 1; fgets(line, sizeof(line), stdin);) { if (line[0] == '\n') eline = 1; else { if (eline && line[0] == 'F' && !memcmp(line, "From ", 5)) (void)putc('>', fp); eline = 0; } (void)fprintf(fp, "%s", line); if (ferror(fp)) { e_to_sys(errno); err("temporary file write error"); } } /* If message not newline terminated, need an extra. */ if (!strchr(line, '\n')) (void)putc('\n', fp); /* Output a newline; note, empty messages are allowed. */ (void)putc('\n', fp); if (fflush(fp) == EOF || ferror(fp)) { e_to_sys(errno); err("temporary file write error"); } return (fd); } void deliver(fd, name) int fd; char *name; { struct stat fsb, sb; struct passwd *pw; int mbfd, nr, nw, off; char *p; char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; off_t curoff; /* * Disallow delivery to unknown names -- special mailboxes can be * handled in the sendmail aliases file. */ if (!(pw = getpwnam(name))) { if (eval != EX_TEMPFAIL) eval = EX_UNAVAILABLE; warn("unknown name: %s", name); return; } endpwent(); /* * Keep name reasonably short to avoid buffer overruns. * This isn't necessary on BSD because of the proper * definition of snprintf(), but it can cause problems * on other systems. * Also, clear out any bogus characters. */ if (strlen(name) > 40) name[40] = '\0'; for (p = name; *p != '\0'; p++) { if (!isascii(*p)) *p &= 0x7f; else if (!isprint(*p)) *p = '.'; } (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name); /* * If the mailbox is linked or a symlink, fail. There's an obvious * race here, that the file was replaced with a symbolic link after * the lstat returned, but before the open. We attempt to detect * this by comparing the original stat information and information * returned by an fstat of the file descriptor returned by the open. * * NB: this is a symptom of a larger problem, that the mail spooling * directory is writeable by the wrong users. If that directory is * writeable, system security is compromised for other reasons, and * it cannot be fixed here. * * If we created the mailbox, set the owner/group. If that fails, * just return. Another process may have already opened it, so we * can't unlink it. Historically, binmail set the owner/group at * each mail delivery. We no longer do this, assuming that if the * ownership or permissions were changed there was a reason. * * XXX * open(2) should support flock'ing the file. */ tryagain: lockmbox(path); - if (lstat(path, &sb)) { + if (lstat(path, &sb) < 0) { mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); + if (lstat(path, &sb) < 0) + goto filechanged; + else + sb.st_uid = pw->pw_uid; if (mbfd == -1) { if (errno == EEXIST) goto tryagain; } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) { e_to_sys(errno); warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name); goto err1; } } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) { e_to_sys(errno); warn("%s: irregular file", path); goto err0; } else if (sb.st_uid != pw->pw_uid) { eval = EX_CANTCREAT; warn("%s: wrong ownership (%d)", path, sb.st_uid); goto err0; } else { mbfd = open(path, O_APPEND|O_WRONLY, 0); - if (mbfd != -1 && - (fstat(mbfd, &fsb) || fsb.st_nlink != 1 || - !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev || - sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid)) { - eval = EX_CANTCREAT; - warn("%s: file changed after open", path); - goto err1; - } } if (mbfd == -1) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); goto err0; + } else if (fstat(mbfd, &fsb) < 0 || + fsb.st_nlink != 1 || sb.st_nlink != 1 || + !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev || + sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid) { +filechanged: + eval = EX_CANTCREAT; + warn("%s: file changed after open", path); + goto err1; } /* Wait until we can get a lock on the file. */ if (flock(mbfd, LOCK_EX)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); goto err1; } /* Get the starting offset of the new message for biff. */ curoff = lseek(mbfd, (off_t)0, SEEK_END); (void)snprintf(biffmsg, sizeof(biffmsg), sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n", name, curoff); /* Copy the message into the file. */ if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { e_to_sys(errno); warn("temporary file: %s", strerror(errno)); goto err1; } if (setreuid(0, pw->pw_uid) < 0) { e_to_sys(errno); warn("setreuid(0, %d): %s (r=%d, e=%d)", pw->pw_uid, strerror(errno), getuid(), geteuid()); goto err1; } #ifdef DEBUG printf("new euid = %d\n", geteuid()); #endif while ((nr = read(fd, buf, sizeof(buf))) > 0) for (off = 0; off < nr; off += nw) if ((nw = write(mbfd, buf + off, nr - off)) < 0) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); goto err3; } if (nr < 0) { e_to_sys(errno); warn("temporary file: %s", strerror(errno)); goto err3; } /* Flush to disk, don't wait for update. */ if (fsync(mbfd)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); err3: if (setreuid(0, 0) < 0) { e_to_sys(errno); warn("setreuid(0, 0): %s", strerror(errno)); } #ifdef DEBUG printf("reset euid = %d\n", geteuid()); #endif err2: (void)ftruncate(mbfd, curoff); err1: (void)close(mbfd); err0: unlockmbox(); return; } /* Close and check -- NFS doesn't write until the close. */ if (close(mbfd)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); - unlockmbox(); - return; - } + truncate(path, curoff); + } else + notifybiff(biffmsg); if (setreuid(0, 0) < 0) { e_to_sys(errno); warn("setreuid(0, 0): %s", strerror(errno)); } #ifdef DEBUG printf("reset euid = %d\n", geteuid()); #endif unlockmbox(); - notifybiff(biffmsg); } /* * user.lock files are necessary for compatibility with other * systems, e.g., when the mail spool file is NFS exported. * Alas, mailbox locking is more than just a local matter. * EPA 11/94. */ char lockname[MAXPATHLEN]; int locked = 0; void lockmbox(path) char *path; { int statfailed = 0; if (locked) return; + if (strlen(path) + 6 > sizeof lockname) + return; sprintf(lockname, "%s.lock", path); for (;; sleep(5)) { int fd; struct stat st; time_t now; fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0); if (fd >= 0) { locked = 1; close(fd); return; } if (stat(lockname, &st) < 0) { if (statfailed++ > 5) return; continue; } statfailed = 0; time(&now); if (now < st.st_ctime + 300) continue; unlink(lockname); } } void unlockmbox() { if (!locked) return; unlink(lockname); locked = 0; } void notifybiff(msg) char *msg; { static struct sockaddr_in addr; static int f = -1; struct hostent *hp; struct servent *sp; int len; if (!addr.sin_family) { /* Be silent if biff service not available. */ if (!(sp = getservbyname("biff", "udp"))) return; if (!(hp = gethostbyname("localhost"))) { warn("localhost: %s", strerror(errno)); return; } addr.sin_family = hp->h_addrtype; memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); addr.sin_port = sp->s_port; } if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { warn("socket: %s", strerror(errno)); return; } len = strlen(msg) + 1; if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)) != len) warn("sendto biff: %s", strerror(errno)); } void usage() { eval = EX_USAGE; err("usage: mail.local [-f from] user ..."); } #if __STDC__ void err(const char *fmt, ...) #else void err(fmt, va_alist) const char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif vwarn(fmt, ap); va_end(ap); exit(eval); } void #if __STDC__ warn(const char *fmt, ...) #else warn(fmt, va_alist) const char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif vwarn(fmt, ap); va_end(ap); } void vwarn(fmt, ap) const char *fmt; _BSD_VA_LIST_ ap; { /* * Log the message to stderr. * * Don't use LOG_PERROR as an openlog() flag to do this, * it's not portable enough. */ if (eval != EX_USAGE) (void)fprintf(stderr, "mail.local: "); (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, "\n"); #if USE_VSYSLOG /* Log the message to syslog. */ vsyslog(LOG_ERR, fmt, ap); #else { char fmtbuf[10240]; (void) vsprintf(fmtbuf, fmt, ap); syslog(LOG_ERR, "%s", fmtbuf); } #endif } /* * e_to_sys -- * Guess which errno's are temporary. Gag me. */ void e_to_sys(num) int num; { /* Temporary failures override hard errors. */ if (eval == EX_TEMPFAIL) return; switch(num) { /* Hopefully temporary errors. */ #ifdef EAGAIN case EAGAIN: /* Resource temporarily unavailable */ #endif #ifdef EDQUOT case EDQUOT: /* Disc quota exceeded */ #endif #ifdef EBUSY case EBUSY: /* Device busy */ #endif #ifdef EPROCLIM case EPROCLIM: /* Too many processes */ #endif #ifdef EUSERS case EUSERS: /* Too many users */ #endif #ifdef ECONNABORTED case ECONNABORTED: /* Software caused connection abort */ #endif #ifdef ECONNREFUSED case ECONNREFUSED: /* Connection refused */ #endif #ifdef ECONNRESET case ECONNRESET: /* Connection reset by peer */ #endif #ifdef EDEADLK case EDEADLK: /* Resource deadlock avoided */ #endif #ifdef EFBIG case EFBIG: /* File too large */ #endif #ifdef EHOSTDOWN case EHOSTDOWN: /* Host is down */ #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: /* No route to host */ #endif #ifdef EMFILE case EMFILE: /* Too many open files */ #endif #ifdef ENETDOWN case ENETDOWN: /* Network is down */ #endif #ifdef ENETRESET case ENETRESET: /* Network dropped connection on reset */ #endif #ifdef ENETUNREACH case ENETUNREACH: /* Network is unreachable */ #endif #ifdef ENFILE case ENFILE: /* Too many open files in system */ #endif #ifdef ENOBUFS case ENOBUFS: /* No buffer space available */ #endif #ifdef ENOMEM case ENOMEM: /* Cannot allocate memory */ #endif #ifdef ENOSPC case ENOSPC: /* No space left on device */ #endif #ifdef EROFS case EROFS: /* Read-only file system */ #endif #ifdef ESTALE case ESTALE: /* Stale NFS file handle */ #endif #ifdef ETIMEDOUT case ETIMEDOUT: /* Connection timed out */ #endif #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK case EWOULDBLOCK: /* Operation would block. */ #endif eval = EX_TEMPFAIL; break; default: eval = EX_UNAVAILABLE; break; } } #if !defined(BSD4_4) && !defined(__osf__) char * strerror(eno) int eno; { extern int sys_nerr; extern char *sys_errlist[]; static char ebuf[60]; if (eno >= 0 && eno <= sys_nerr) return sys_errlist[eno]; (void) sprintf(ebuf, "Error %d", eno); return ebuf; } -# endif +#endif /* !defined(BSD4_4) && !defined(__osf__) */ -#if !defined(BSD4_4) && !defined(linux) +#if !HASSNPRINTF # if __STDC__ -snprintf(char *buf, int bufsiz, const char *fmt, ...) +snprintf(char *buf, size_t bufsiz, const char *fmt, ...) # else snprintf(buf, bufsiz, fmt, va_alist) char *buf; - int bufsiz; + size_t bufsiz; const char *fmt; va_dcl # endif { va_list ap; # if __STDC__ va_start(ap, fmt); # else va_start(ap); # endif vsprintf(buf, fmt, ap); va_end(ap); } -#endif +#endif /* !HASSNPRINTF */ #ifdef ultrix /* * 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[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include static int _gettemp(); mkstemp(path) char *path; { int fd; return (_gettemp(path, &fd) ? fd : -1); } /* char * mktemp(path) char *path; { return(_gettemp(path, (int *)NULL) ? path : (char *)NULL); } */ static _gettemp(path, doopen) char *path; register int *doopen; { extern int errno; register char *start, *trv; struct stat sbuf; u_int pid; pid = getpid(); for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ while (*--trv == 'X') { *trv = (pid % 10) + '0'; pid /= 10; } /* * check the target directory; if you have six X's and it * doesn't exist this runs for a *very* long time. */ for (start = trv + 1;; --trv) { if (trv <= path) break; if (*trv == '/') { *trv = '\0'; - if (stat(path, &sbuf)) + if (stat(path, &sbuf) < 0) return(0); if (!S_ISDIR(sbuf.st_mode)) { errno = ENOTDIR; return(0); } *trv = '/'; break; } } for (;;) { if (doopen) { if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) return(1); if (errno != EEXIST) return(0); } - else if (stat(path, &sbuf)) + else if (stat(path, &sbuf) < 0) return(errno == ENOENT ? 1 : 0); /* tricky little algorithm for backward compatibility */ for (trv = start;;) { if (!*trv) return(0); if (*trv == 'z') *trv++ = 'a'; else { if (isdigit(*trv)) *trv = 'a'; else ++*trv; break; } } } /*NOTREACHED*/ } -#endif +#endif /* ultrix */ Index: vendor/sendmail/dist-old/usr.sbin/sendmail/mailstats/mailstats.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/mailstats/mailstats.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/mailstats/mailstats.c (revision 26986) @@ -1,240 +1,247 @@ /* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988, 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. * */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mailstats.c 8.8 (Berkeley) 9/25/96"; +static char sccsid[] = "@(#)mailstats.c 8.10 (Berkeley) 5/30/97"; #endif /* not lint */ #define NOT_SENDMAIL #include #include #include #define MNAMELEN 20 /* max length of mailer name */ main(argc, argv) int argc; char **argv; { extern char *optarg; extern int optind; struct statistics stat; register int i; int mno; int ch, fd; char *sfile; char *cfile; FILE *cfp; bool mnames; long frmsgs = 0, frbytes = 0, tomsgs = 0, tobytes = 0; char mtable[MAXMAILERS][MNAMELEN+1]; - char sfilebuf[100]; + char sfilebuf[MAXLINE]; char buf[MAXLINE]; extern char *ctime(); cfile = _PATH_SENDMAILCF; sfile = NULL; mnames = TRUE; while ((ch = getopt(argc, argv, "C:f:o")) != EOF) { switch (ch) { case 'C': cfile = optarg; break; case 'f': sfile = optarg; break; case 'o': mnames = FALSE; break; case '?': default: usage: fputs("usage: mailstats [-C cffile] [-f stfile] -o\n", stderr); exit(EX_USAGE); } } argc -= optind; argv += optind; if (argc != 0) goto usage; if ((cfp = fopen(cfile, "r")) == NULL) { fprintf(stderr, "mailstats: "); perror(cfile); exit(EX_NOINPUT); } mno = 0; (void) strcpy(mtable[mno++], "prog"); (void) strcpy(mtable[mno++], "*file*"); (void) strcpy(mtable[mno++], "*include*"); while (fgets(buf, sizeof(buf), cfp) != NULL) { register char *b; char *s; register char *m; b = buf; switch (*b++) { case 'M': /* mailer definition */ break; case 'O': /* option -- see if .st file */ if (strncasecmp(b, " StatusFile", 11) == 0 && !isalnum(b[11])) { /* new form -- find value */ b = strchr(b, '='); if (b == NULL) continue; while (isspace(*++b)) continue; } else if (*b++ != 'S') { /* something else boring */ continue; } /* this is the S or StatusFile option -- save it */ + if (strlen(b) >= sizeof sfilebuf) + { + fprintf(stderr, + "StatusFile filename too long: %.30s...\n", + s); + exit(EX_CONFIG); + } strcpy(sfilebuf, b); b = strchr(sfilebuf, '#'); if (b == NULL) b = strchr(sfilebuf, '\n'); if (b == NULL) b = &sfilebuf[strlen(sfilebuf)]; while (isspace(*--b)) continue; *++b = '\0'; if (sfile == NULL) sfile = sfilebuf; default: continue; } if (mno >= MAXMAILERS) { fprintf(stderr, "Too many mailers defined, %d max.\n", MAXMAILERS); exit(EX_SOFTWARE); } m = mtable[mno]; s = m + MNAMELEN; /* is [MNAMELEN+1] */ while (*b != ',' && !isspace(*b) && *b != '\0' && m < s) *m++ = *b++; *m = '\0'; for (i = 0; i < mno; i++) { if (strcmp(mtable[i], mtable[mno]) == 0) break; } if (i == mno) mno++; } (void) fclose(cfp); for (; mno < MAXMAILERS; mno++) mtable[mno][0]='\0'; if (sfile == NULL) { fprintf(stderr, "mailstats: no statistics file located\n"); exit (EX_OSFILE); } if ((fd = open(sfile, O_RDONLY)) < 0 || (i = read(fd, &stat, sizeof stat)) < 0) { fputs("mailstats: ", stderr); perror(sfile); exit(EX_NOINPUT); } if (i == 0) { sleep(1); i = read(fd, &stat, sizeof stat); if (i == 0) { bzero((ARBPTR_T) &stat, sizeof stat); (void) time(&stat.stat_itime); } } else if (i != sizeof stat || stat.stat_size != sizeof(stat)) { fputs("mailstats: file size changed.\n", stderr); exit(EX_OSERR); } printf("Statistics from %s", ctime(&stat.stat_itime)); printf(" M msgsfr bytes_from msgsto bytes_to%s\n", mnames ? " Mailer" : ""); for (i = 0; i < MAXMAILERS; i++) { if (stat.stat_nf[i] || stat.stat_nt[i]) { printf("%2d %6ld %10ldK %6ld %10ldK", i, stat.stat_nf[i], stat.stat_bf[i], stat.stat_nt[i], stat.stat_bt[i]); if (mnames) printf(" %s", mtable[i]); printf("\n"); frmsgs += stat.stat_nf[i]; frbytes += stat.stat_bf[i]; tomsgs += stat.stat_nt[i]; tobytes += stat.stat_bt[i]; } } printf("========================================\n"); printf(" T %6ld %10ldK %6ld %10ldK\n", frmsgs, frbytes, tomsgs, tobytes); exit(EX_OK); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/makemap/Makefile =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/makemap/Makefile (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/makemap/Makefile (revision 26986) @@ -1,8 +1,13 @@ -# @(#)Makefile 8.1 (Berkeley) 6/7/93 +# @(#)Makefile 8.4 (Berkeley) 6/10/97 PROG= makemap MAN8= makemap.0 -CFLAGS+=-I${.CURDIR}/../src -DNDBM -DNEWDB +CFLAGS+=-I${.CURDIR}/../src -DNEWDB -DNOT_SENDMAIL + +SRCS= makemap.c safefile.c + +safefile.c: ${.CURDIR}/../src/safefile.c + ln -s ${.CURDIR}/../src/safefile.c .include "../../Makefile.inc" .include Index: vendor/sendmail/dist-old/usr.sbin/sendmail/makemap/makemap.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/makemap/makemap.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/makemap/makemap.c (revision 26986) @@ -1,534 +1,775 @@ /* * Copyright (c) 1992 Eric P. Allman. * Copyright (c) 1992, 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. */ #ifndef lint -static char sccsid[] = "@(#)makemap.c 8.19 (Berkeley) 11/18/96"; +static char sccsid[] = "@(#)makemap.c 8.35 (Berkeley) 6/10/97"; #endif /* not lint */ -#include -#include #include -#include -#include #include #ifndef ISC_UNIX # include #endif -#define NOT_SENDMAIL -#include "useful.h" -#include "conf.h" +#include "sendmail.h" #ifdef NDBM #include #endif #ifdef NEWDB #include #endif enum type { T_DBM, T_BTREE, T_HASH, T_ERR, T_UNKNOWN }; union dbent { #ifdef NDBM datum dbm; #endif #ifdef NEWDB DBT db; #endif struct { char *data; size_t size; } xx; }; +uid_t RealUid; +gid_t RealGid; +char *RealUserName; +uid_t RunAsUid; +uid_t RunAsGid; +char *RunAsUserName; +int Verbose = 2; +bool DontInitGroups = TRUE; +bool UnsafeGroupWrites = FALSE; +u_char tTdvect[100]; + #define BUFSIZE 1024 main(argc, argv) int argc; char **argv; { char *progname; bool inclnull = FALSE; bool notrunc = FALSE; bool allowreplace = FALSE; bool allowdups = FALSE; bool verbose = FALSE; bool foldcase = TRUE; + bool ignoresafeties = FALSE; int exitstat; int opt; char *typename; char *mapname; char *ext; - char *lext; int lineno; int st; int mode; int putflags; long dbcachesize = 1024 * 1024; enum type type; int fd; union { #ifdef NDBM DBM *dbm; #endif #ifdef NEWDB DB *db; #endif void *dbx; } dbp; union dbent key, val; #ifdef NEWDB BTREEINFO bti; HASHINFO hinfo; #endif char ibuf[BUFSIZE]; char fbuf[MAXNAME]; - char lbuf[MAXNAME]; + char dbuf[MAXNAME]; + char pbuf[MAXNAME]; + static char rnamebuf[MAXNAME]; /* holds RealUserName */ + struct passwd *pw; + int sff = SFF_ROOTOK|SFF_REGONLY|SFF_NOLINK|SFF_NOWLINK; + struct stat std, stp; extern char *optarg; extern int optind; extern bool lockfile(); progname = argv[0]; -#ifdef FFR_CFLAG -#define OPTIONS "Nc:dforv" + RunAsUid = RealUid = getuid(); + RunAsGid = RealGid = getgid(); + pw = getpwuid(RealUid); + if (pw != NULL) + { + if (strlen(pw->pw_name) > MAXNAME - 1) + pw->pw_name[MAXNAME] = 0; + sprintf(rnamebuf, "%s", pw->pw_name); + } + else + sprintf(rnamebuf, "Unknown UID %d", RealUid); + RunAsUserName = RealUserName = rnamebuf; + +#if _FFR_NEW_MAKEMAP_FLAGS +#define OPTIONS "Nc:dforsv" #else #define OPTIONS "Ndforv" #endif while ((opt = getopt(argc, argv, OPTIONS)) != EOF) { switch (opt) { case 'N': inclnull = TRUE; break; -#ifdef FFR_CFLAG +#if _FFR_NEW_MAKEMAP_FLAGS case 'c': dbcachesize = atol(optarg); break; #endif case 'd': allowdups = TRUE; break; case 'f': foldcase = FALSE; break; case 'o': notrunc = TRUE; break; case 'r': allowreplace = TRUE; break; +#if _FFR_NEW_MAKEMAP_FLAGS + case 's': + ignoresafeties = TRUE; + break; +#endif + case 'v': verbose = TRUE; break; default: type = T_ERR; break; } } argc -= optind; argv += optind; if (argc != 2) type = T_ERR; else { typename = argv[0]; mapname = argv[1]; ext = NULL; - lext = NULL; if (strcmp(typename, "dbm") == 0) { type = T_DBM; - lext = ".dir"; } else if (strcmp(typename, "btree") == 0) { type = T_BTREE; ext = ".db"; } else if (strcmp(typename, "hash") == 0) { type = T_HASH; ext = ".db"; } else type = T_UNKNOWN; } switch (type) { case T_ERR: -#ifdef FFR_CFLAG - fprintf(stderr, "Usage: %s [-N] [-c cachesize] [-d] [-f] [-o] [-r] [-v] type mapname\n", progname); +#if _FFR_NEW_MAKEMAP_FLAGS + fprintf(stderr, + "Usage: %s [-N] [-c cachesize] [-d] [-f] [-o] [-r] [-s] [-v] type mapname\n", + progname); #else fprintf(stderr, "Usage: %s [-N] [-d] [-f] [-o] [-r] [-v] type mapname\n", progname); #endif exit(EX_USAGE); case T_UNKNOWN: fprintf(stderr, "%s: Unknown database type %s\n", progname, typename); exit(EX_USAGE); #ifndef NDBM case T_DBM: #endif #ifndef NEWDB case T_BTREE: case T_HASH: #endif fprintf(stderr, "%s: Type %s not supported in this version\n", progname, typename); exit(EX_UNAVAILABLE); #ifdef NEWDB case T_BTREE: bzero(&bti, sizeof bti); if (allowdups) bti.flags |= R_DUP; if (allowdups || allowreplace) putflags = 0; else putflags = R_NOOVERWRITE; break; case T_HASH: bzero(&hinfo, sizeof hinfo); if (allowreplace) putflags = 0; else putflags = R_NOOVERWRITE; break; #endif #ifdef NDBM case T_DBM: if (allowdups) { fprintf(stderr, "%s: Type %s does not support -d (allow dups)\n", progname, typename); exit(EX_UNAVAILABLE); } if (allowreplace) putflags = DBM_REPLACE; else putflags = DBM_INSERT; break; #endif } /* ** Adjust file names. */ if (ext != NULL) { int el, fl; el = strlen(ext); fl = strlen(mapname); + if (el + fl + 1 >= sizeof fbuf) + { + fprintf(stderr, "%s: file name too long", mapname); + exit(EX_USAGE); + } if (fl < el || strcmp(&mapname[fl - el], ext) != 0) { strcpy(fbuf, mapname); strcat(fbuf, ext); mapname = fbuf; } } - strcpy(lbuf, mapname); - if (lext != NULL) - strcat(lbuf, lext); + if (!notrunc) + sff |= SFF_CREAT; + switch (type) + { +#ifdef NEWDB + case T_BTREE: + case T_HASH: + if (strlen(mapname) >= sizeof dbuf) + { + fprintf(stderr, + "%s: map name too long\n", mapname); + exit(EX_USAGE); + } + strcpy(dbuf, mapname); + if (!ignoresafeties && + (st = safefile(dbuf, RealUid, RealGid, RealUserName, + sff, S_IWUSR, &std)) != 0) + { + fprintf(stderr, + "%s: could not create: %s\n", + dbuf, errstring(st)); + exit(EX_CANTCREAT); + } + break; +#endif +#ifdef NDBM + case T_DBM: + if (strlen(mapname) + 5 > sizeof dbuf) + { + fprintf(stderr, + "%s: map name too long\n", mapname); + exit(EX_USAGE); + } + sprintf(dbuf, "%s.dir", mapname); + if (!ignoresafeties && + (st = safefile(dbuf, RealUid, RealGid, RealUserName, + sff, S_IWUSR, &std)) != 0) + { + fprintf(stderr, + "%s: could not create: %s\n", + dbuf, errstring(st)); + exit(EX_CANTCREAT); + } + sprintf(pbuf, "%s.pag", mapname); + if (!ignoresafeties && + (st = safefile(pbuf, RealUid, RealGid, RealUserName, + sff, S_IWUSR, &stp)) != 0) + { + fprintf(stderr, + "%s: could not create: %s\n", + pbuf, errstring(st)); + exit(EX_CANTCREAT); + } + break; +#endif + default: + fprintf(stderr, + "%s: internal error: type %d\n", + progname, + type); + exit(EX_SOFTWARE); + } /* ** Create the database. */ mode = O_RDWR; if (!notrunc) mode |= O_CREAT|O_TRUNC; -#ifdef O_EXLOCK +#if O_EXLOCK mode |= O_EXLOCK; #else /* pre-lock the database */ - fd = open(lbuf, mode & ~O_TRUNC, 0644); + if (ignoresafeties) + fd = dfopen(dbuf, mode & ~O_TRUNC, 0644, sff); + else + fd = safeopen(dbuf, mode & ~O_TRUNC, 0644, sff); if (fd < 0) { fprintf(stderr, "%s: cannot create type %s map %s\n", progname, typename, mapname); exit(EX_CANTCREAT); } - (void) lockfile(fd); #endif switch (type) { #ifdef NDBM case T_DBM: dbp.dbm = dbm_open(mapname, mode, 0644); + if (!ignoresafeties && dbp.dbm != NULL && + (filechanged(dbuf, dbm_dirfno(dbp.dbm), &std, sff) || + filechanged(pbuf, dbm_pagfno(dbp.dbm), &stp, sff))) + { + fprintf(stderr, + "dbm map %s: file changed after open\n", + mapname); + dbm_close(dbp.dbm); + exit(EX_CANTCREAT); + } break; #endif #ifdef NEWDB case T_HASH: /* tweak some parameters for performance */ hinfo.nelem = 4096; hinfo.cachesize = dbcachesize; - + dbp.db = dbopen(mapname, mode, 0644, DB_HASH, &hinfo); if (dbp.db != NULL) { + if (!ignoresafeties && + filechanged(dbuf, dbp.db->fd(dbp.db), &std, sff)) + { + fprintf(stderr, + "db map %s: file changed after open\n", + mapname); + dbp.db->close(dbp.db); + exit(EX_CANTCREAT); + } # if OLD_NEWDB (void) (*dbp.db->sync)(dbp.db); # else (void) (*dbp.db->sync)(dbp.db, 0); # endif } break; case T_BTREE: /* tweak some parameters for performance */ bti.cachesize = dbcachesize; dbp.db = dbopen(mapname, mode, 0644, DB_BTREE, &bti); if (dbp.db != NULL) { + if (!ignoresafeties && + filechanged(dbuf, dbp.db->fd(dbp.db), &std, sff)) + { + fprintf(stderr, + "db map %s: file changed after open\n", + mapname); + dbp.db->close(dbp.db); + exit(EX_CANTCREAT); + } # if OLD_NEWDB (void) (*dbp.db->sync)(dbp.db); # else (void) (*dbp.db->sync)(dbp.db, 0); # endif } break; #endif default: - fprintf(stderr, "%s: internal error: type %d\n", progname, type); + fprintf(stderr, "%s: internal error: type %d\n", + progname, type); exit(EX_SOFTWARE); } if (dbp.dbx == NULL) { fprintf(stderr, "%s: cannot open type %s map %s\n", progname, typename, mapname); exit(EX_CANTCREAT); } /* ** Copy the data */ lineno = 0; exitstat = EX_OK; while (fgets(ibuf, sizeof ibuf, stdin) != NULL) { register char *p; lineno++; /* ** Parse the line. */ p = strchr(ibuf, '\n'); if (p != NULL) *p = '\0'; else if (!feof(stdin)) { fprintf(stderr, "%s: %s: line %d: line too long (%d bytes max)\n", progname, mapname, lineno, sizeof ibuf); continue; } if (ibuf[0] == '\0' || ibuf[0] == '#') continue; if (isspace(ibuf[0])) { fprintf(stderr, "%s: %s: line %d: syntax error (leading space)\n", progname, mapname, lineno); continue; } key.xx.data = ibuf; for (p = ibuf; *p != '\0' && !isspace(*p); p++) { if (foldcase && isupper(*p)) *p = tolower(*p); } key.xx.size = p - key.xx.data; if (inclnull) key.xx.size++; if (*p != '\0') *p++ = '\0'; while (isspace(*p)) p++; if (*p == '\0') { fprintf(stderr, "%s: %s: line %d: no RHS for LHS %s\n", progname, mapname, lineno, key.xx.data); continue; } val.xx.data = p; val.xx.size = strlen(p); if (inclnull) val.xx.size++; /* ** Do the database insert. */ if (verbose) { printf("key=`%s', val=`%s'\n", key.xx.data, val.xx.data); } switch (type) { #ifdef NDBM case T_DBM: st = dbm_store(dbp.dbm, key.dbm, val.dbm, putflags); break; #endif #ifdef NEWDB case T_BTREE: case T_HASH: st = (*dbp.db->put)(dbp.db, &key.db, &val.db, putflags); break; #endif } if (st < 0) { fprintf(stderr, "%s: %s: line %d: key %s: put error\n", progname, mapname, lineno, key.xx.data); perror(mapname); exitstat = EX_IOERR; } else if (st > 0) { - fprintf(stderr, "%s: %s: line %d: key %s: duplicate key\n", + fprintf(stderr, + "%s: %s: line %d: key %s: duplicate key\n", progname, mapname, lineno, key.xx.data); } } /* ** Now close the database. */ switch (type) { #ifdef NDBM case T_DBM: dbm_close(dbp.dbm); break; #endif #ifdef NEWDB case T_HASH: case T_BTREE: if ((*dbp.db->close)(dbp.db) < 0) { fprintf(stderr, "%s: %s: error on close\n", progname, mapname); perror(mapname); exitstat = EX_IOERR; } #endif } -#ifndef O_EXLOCK +#if !O_EXLOCK /* release locks */ close(fd); #endif exit (exitstat); } /* ** 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) +lockfile(fd, filename, ext, type) int fd; + char *filename; + char *ext; + int type; { # if !HASFLOCK int action; struct flock lfd; extern int errno; bzero(&lfd, sizeof lfd); - lfd.l_type = F_WRLCK; - action = F_SETLKW; + 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 (fcntl(fd, action, &lfd) >= 0) return TRUE; /* ** 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 (errno == EINVAL) return TRUE; # else /* HASFLOCK */ - if (flock(fd, LOCK_EX) >= 0) + if (flock(fd, type) >= 0) return TRUE; # endif return FALSE; +} + +/*VARARGS2*/ +void +#ifdef __STDC__ +message(const char *msg, ...) +#else +message(msg, va_alist) + const char *msg; + va_dcl +#endif +{ + const char *m; + VA_LOCAL_DECL + + m = msg; + if (isdigit(m[0]) && isdigit(m[1]) && isdigit(m[2]) && m[3] == ' ') + m += 4; + VA_START(msg); + vfprintf(stderr, m, ap); + VA_END; + fprintf(stderr, "\n"); +} + +/*VARARGS2*/ +void +#ifdef __STDC__ +syserr(const char *msg, ...) +#else +syserr(msg, va_alist) + const char *msg; + va_dcl +#endif +{ + const char *m; + VA_LOCAL_DECL + + m = msg; + if (isdigit(m[0]) && isdigit(m[1]) && isdigit(m[2]) && m[3] == ' ') + m += 4; + VA_START(msg); + vfprintf(stderr, m, ap); + VA_END; + fprintf(stderr, "\n"); +} + +const char * +errstring(err) + int err; +{ + static char errstr[64]; +#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) + extern char *sys_errlist[]; + extern int sys_nerr; +#endif + + /* handle pseudo-errors internal to sendmail */ + switch (err) + { + 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 HASSTRERROR + return strerror(err); +#else + if (err < 0 || err > sys_nerr) + { + sprintf(errstr, "Error %d", err); + return errstr; + } + return sys_errlist[err]; +#endif } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/praliases/praliases.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/praliases/praliases.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/praliases/praliases.c (revision 26986) @@ -1,133 +1,138 @@ /* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)praliases.c 8.4 (Berkeley) 9/25/96"; +static char sccsid[] = "@(#)praliases.c 8.5 (Berkeley) 5/28/97"; #endif /* not lint */ #include #define NOT_SENDMAIL #include #ifdef NEWDB #include #endif int main(argc, argv) int argc; char **argv; { extern char *optarg; extern int optind; DBM *dbp; datum content, key; char *filename; int ch; #ifdef NEWDB const DB *db; DBT newdbkey, newdbcontent; char buf[MAXNAME]; #endif filename = "/etc/aliases"; while ((ch = getopt(argc, argv, "f:")) != EOF) switch((char)ch) { case 'f': filename = optarg; break; case '?': default: (void)fprintf(stderr, "usage: praliases [-f file]\n"); exit(EX_USAGE); } argc -= optind; argv += optind; #ifdef NEWDB + if (strlen(filename) + 4 >= sizeof buf) + { + fprintf(stderr, "Alias filename too long: %.30s...\n", filename); + exit(EX_USAGE); + } (void) strcpy(buf, filename); (void) strcat(buf, ".db"); if (db = dbopen(buf, O_RDONLY, 0444 , DB_HASH, NULL)) { if (!argc) { while(!db->seq(db, &newdbkey, &newdbcontent, R_NEXT)) printf("%.*s:%.*s\n", newdbkey.size, newdbkey.data, newdbcontent.size, newdbcontent.data); } else for (; *argv; ++argv) { newdbkey.data = *argv; newdbkey.size = strlen(*argv) + 1; if (!db->get(db, &newdbkey, &newdbcontent, 0)) printf("%s:%.*s\n", newdbkey.data, newdbcontent.size, newdbcontent.data); else printf("%s: No such key\n", newdbkey.data); } } else { #endif if ((dbp = dbm_open(filename, O_RDONLY, 0)) == NULL) { (void)fprintf(stderr, "praliases: %s: %s\n", filename, strerror(errno)); exit(EX_OSFILE); } if (!argc) for (key = dbm_firstkey(dbp); key.dptr != NULL; key = dbm_nextkey(dbp)) { content = dbm_fetch(dbp, key); (void)printf("%.*s:%.*s\n", key.dsize, key.dptr, content.dsize, content.dptr); } else for (; *argv; ++argv) { key.dptr = *argv; key.dsize = strlen(*argv) + 1; content = dbm_fetch(dbp, key); if (!content.dptr) (void)printf("%s: No such key\n", key.dptr); else (void)printf("%s:%.*s\n", key.dptr, content.dsize, content.dptr); } #ifdef NEWDB } #endif exit(EX_OK); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/Makefile =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/Makefile (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/Makefile (revision 26986) @@ -1,47 +1,47 @@ -# @(#)Makefile 8.7 (Berkeley) 10/31/95 +# @(#)Makefile 8.8 (Berkeley) 3/28/97 ######################################################################### # This Makefile is for 4.4BSD only!!! For all other systems, use # # the "makesendmail" script. # ######################################################################### PROG= sendmail # define the database format to use for aliases et al. Can be -DNEWDB (for # the new BSD database package -- this is preferred) or -DNDBM for the NDBM # database package. The old putrescent V7 DBM package is no longer # supported. # You can define both NEWDB and NDBM during a transition period; old # databases are read, but the new format will be used on any rebuilds. On # really gnarly systems, you can set this to null; it will crawl like a high # spiral snail, but it will work. DBMDEF= -DNEWDB CFLAGS+=-I${.CURDIR} ${DBMDEF} -DNETISO SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \ deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \ - mci.c mime.c parseaddr.c queue.c readcf.c recipient.c savemail.c \ - srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \ - util.c version.c + mci.c mime.c parseaddr.c queue.c readcf.c recipient.c safefile.c \ + savemail.c srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c \ + usersmtp.c util.c version.c DPADD= LDADD= MAN1= mailq.0 newaliases.0 MAN5= aliases.0 MAN8= sendmail.0 LINKS= /usr/sbin/sendmail /usr/bin/newaliases \ /usr/sbin/sendmail /usr/bin/mailq BINDIR= /usr/sbin BINOWN= root BINGRP= kmem BINMODE=6555 beforeinstall: # install -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ # ${DESTDIR}/etc/sendmail.fc install -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ ${DESTDIR}/var/log/sendmail.st install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \ ${DESTDIR}/usr/share/misc .include Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/READ_ME =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/READ_ME (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/READ_ME (revision 26986) @@ -1,1410 +1,1419 @@ -# Copyright (c) 1983, 1995, 1996 Eric P. Allman +# Copyright (c) 1983, 1995-1997 Eric P. Allman # Copyright (c) 1988 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. # -# @(#)READ_ME 8.135 (Berkeley) 1/21/97 +# @(#)READ_ME 8.142 (Berkeley) 6/3/97 # This directory contains the source files for sendmail. ********************* !! DO NOT USE MAKE !! to compile sendmail -- instead, use the ********************* "makesendmail" script located in the src directory. It will find an appropriate Makefile, and create an appropriate obj.* subdirectory so that multiplatform support works easily. The Makefile is for the new (4.4BSD) Berkeley make and uses syntax that is not recognized by older makes. It also has assumptions about the 4.4 file system layout built in. See below for details about other Makefiles. If you are porting to a new architecture for which there is no existing Makefile, you might start with Makefile.dist. This works on the old traditional make, but isn't customized for any particular architecture. ************************************************** ** Read below for more details of Makefiles. ** ************************************************** ************************************************************************** ** 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. ************************************************************************** ** IMPORTANT: Read the appropriate paragraphs in the section on ** ** ``Operating System and Compile Quirks''. ** ************************************************************************** For detailed instructions, please read the document ../doc/op.me: eqn ../doc/op.me | pic | ditroff -me +-----------+ | MAKEFILES | +-----------+ By far, the easiest way to compile sendmail is to use the "makesendmail" script: sh makesendmail This uses the "uname" command to figure out what architecture you are on and selects 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. However, if for some reason this doesn't work (e.g., NeXT systems don't have the "uname" command) you may have to set up your compile environment by hand. The "Makefile"s in these directories are from 4.4 BSD, and hence really only work properly if you are on a 4.4 system. In particular, they use new syntax that will not be recognized on old make programs, and some of them do things like ``.include ../../Makefile.inc'' to pick up some system defines. If you are getting sendmail separately, these files won't be included in the distribution, as they are outside of the sendmail tree. Instead, you should use one of the other Makefiles, such as Makefile.SunOS for a SunOS system, and so forth. These should work with the version of make that is appropriate for that system. All other Makefiles are in the "src/Makefiles" subdirectory. They use the version of make that is native for that system. These are the Makefiles that I use, and they have "Berkeley quirks" in them. I can't guarantee that they will work unmodified in your environment. In particular, Many of them include -I/usr/sww/include/db and -L/usr/sww/lib -- these are Berkeley's locations in the ``Software Warehouse'' for the new database libraries, described below. You don't have to remove these definitions if you don't have these directories, but you may have to remove -DNEWDB from the DBMDEF definition. Please look for an appropriate Makefile before you start trying to compile with Makefile or Makefile.dist. If you want to port the new Berkeley make, you can get it from ftp.uu.net in the directory /systems/unix/bsd-sources/usr.bin/make. Diffs and instructions for building this version of make under SunOS 4.1.x are available on ftp.css.itd.umich.edu in /pub/systems/sun/Net2-make-sun4.diff.Z. Diffs and instructions for building this version of make under IBM AIX 3.2.4 are available on ftp.uni-stuttgart.de in /sw/src/patches/bsd-make-rus-patches. For Ultrix, try ftp.vix.com:~ftp/pub/patches/pmake-for-ultrix.Z. Paul Southworth published a description of porting this make in comp.unix.bsd. The complete text of the Makefile.inc that is in the parent of the sendmail directory is: # @(#)Makefile.inc 8.1 (Berkeley) 6/6/93 BINDIR?= /usr/sbin +----------------------+ | 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 back compatible. The options are: NEWDB The new Berkeley DB package. Some systems (e.g., BSD/OS and Digital UNIX 4.0) have this package pre-installed. If your system does not have NEWDB installed, get the latest version - from FTP.CS.Berkeley.EDU in /ucb/4bsd/db.tar.gz (or db.tar.Z). + from FTP://ftp.sleepycat.com/db/packages/db.1.85.tar.gz. DO NOT use the version from the Net2 distribution. If you are still running BSD/386 1.x, you will also need to define OLD_NEWDB. 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. >>> NOTE WELL for NEWDB support: it is CRITICAL that you remove ndbm.o >>> from libdb.a before you install it and DO NOT install ndbm.h if >>> you want to get ndbm support. 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. >>> >>> 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. +---------------+ | COMPILE FLAGS | +---------------+ Whereever 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 Makefiles 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 seteuid(2) if you have a seteuid - 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. +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. +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 *". 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 hou can also set SFS_BAVAIL to the field name +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. +-----------------------+ | 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. OLD_NEWDB If non-zero, 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. If you use -DOLD_NEWDB=0 it forces you to use the new interface. 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. Not yet - implemented. +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 2.5.x problems *** IMPORTANT *** Date: Mon, 29 Nov 93 19:08:44 PST From: wilson@cygnus.com (Jim Wilson) Message-Id: <9311300308.AA04608@cygnus.com> To: kenner@vlsi1.ultra.nyu.edu Subject: [cattelan@thebarn.com: gcc 2.5.4-2.5.5 -O bug] Cc: cattelan@thebarn.com, rms@gnu.ai.mit.edu, sendmail@cs.berkeley.edu This fixes a problem that occurs when gcc 2.5.5 is used to compile sendmail 8.6.4 with optimization on a sparc. Mon Nov 29 19:00:14 1993 Jim Wilson (wilson@sphagnum.cygnus.com) * reload.c (find_reloads_toplev): Replace obsolete reference to BYTE_LOADS_*_EXTEND with LOAD_EXTEND_OP. *** clean-ss-931128/reload.c Sun Nov 14 16:20:01 1993 --- ss-931128/reload.c Mon Nov 29 18:52:55 1993 *************** find_reloads_toplev (x, opnum, type, ind *** 3888,3894 **** force a reload in that case. So we should not do anything here. */ else if (regno >= FIRST_PSEUDO_REGISTER ! #if defined(BYTE_LOADS_ZERO_EXTEND) || defined(BYTE_LOADS_SIGN_EXTEND) && (GET_MODE_SIZE (GET_MODE (x)) <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) #endif --- 3888,3894 ---- force a reload in that case. So we should not do anything here. */ else if (regno >= FIRST_PSEUDO_REGISTER ! #ifdef LOAD_EXTEND_OP && (GET_MODE_SIZE (GET_MODE (x)) <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))) #endif GCC 2.7.x problems 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. 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. 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. ld: fatal: library -l44bsd: not found Most of the Makefiles include -l44bsd in the LIBS= definition; this is because several versions of BIND (4.9.0, 4.9.1, 4.9.2) require this library. If you are running one of these versions, install this library. Otherwise, just delete "-l44bsd" from the LIBS= line in the Makefile. 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 the Makefile.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 is fixed in patch 101790-01 (SunOS 4.1.3_U1: TCP socket and reset problems). Solaris 2.x (SunOS 5.x) To compile for Solaris, be sure you use -DSOLARIS. 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. -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. - Solaris 2.5.1 (SunOS 5.5.1) Apparently 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 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 If you are running with this patch, create a resolv.h file in the obj.SunOS.5.5.1.* directory that reads: #undef __P #include "/usr/include/resolv.h" ... And then file a bug report with Sun. +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. + 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. 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/db-1.85-irix.tar.Z . NeXT or NEXTSTEP NEXTSTEP 3.3 and earlier ship with the old DBM library. You will need to acquire the new Berkeley DB from ftp.cs.berkeley.edu. Install it in /usr/local/{lib,include}. 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 (The Makefile.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 Makefile.FreeBSD). NetBSD-current may have it too but it has not been verified. You cannot port the latest version of the Berkeley db library and use it with sendmail without recompiling the world. This is because C library routines use the older version which have incompatible header files -- the result is that it can't read other system files, such as /etc/passwd, unless you use the new db format throughout your system. You should normally just use the version of db supplied in your release. You may need to use -DOLD_NEWDB=1 to make this work -- this turns off some new interface calls (for file locking) that are not in older versions of db. You'll get compile errors if you need this flag and don't have it set. 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. 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. 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 - 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" and create a file "dirent.h" containing: #include #define dirent direct (The Makefile.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 Makefiles for linux include 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. 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 the Makefile.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 the Makefile.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 Makefile that is intended for all SVR4-based systems (called Makefile.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 Makefile. 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 2.0 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. 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 . Non-DNS based sites This version of sendmail always tries to connect to the Domain Name System (DNS) to resolve names, regardless of the setting of the `I' option. On most systems that are not running DNS, this will fail quickly and sendmail will continue, but on some systems it has a long timeout. If you have this problem, you will have to recompile without NAMED_BIND. Some people have claimed that they have successfully used "OI+USEVC" to force sendmail to use a virtual circuit -- this will always time out quickly, but also tells sendmail that a failed connection should requeue the message (probably not what you intended). A future release of sendmail will correct this problem. Both NEWDB and NDBM If you use both -DNDBM and -DNEWDB, you must delete the module ndbm.o from libdb.a and delete the file "ndbm.h" from the files that get installed (that is, use the OLD ndbm.h, not the new ndbm.h). This compatibility module maps ndbm calls into DB calls, and breaks things rather badly. 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. This project is discussed at http://www-leland.stanford.edu/group/networking/project/sunetid.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. - - KNOWN BUGS: It does not work under Digital Unix 3.2c, with gcc and - ldap3.2 or ldap3.3. It dumps core after attempting to take strlen - of a garbage string pointer in the lber libraries routine - ber_printf. - - The string pointer in question is set to 0x50000000, when the - program crashes. If anyone recognizes where this magic number comes - from that would be really helpful. - I've tested the software on Solaris.2.4 with gcc and on NeXTStep3.2 and it runs without problems. If you have any questions, please send them along. TCP Wrappers If you are using -DTCPWRAPPERS to get TCP Wrappers support you will also need to install libwrap.a and modify the 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. +--------------+ | 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 directory /systems/unix/bsd-sources/share/tmac. +-----------------+ | 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 The makefile used here; this version only works with the new Berkeley make. Makefile.dist A trimmed down version of the makefile that works with the old make. READ_ME 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. 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.135, last update 1/21/97 07:47:02) +(Version 8.142, last update 6/3/97 11:34:09) Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/TRACEFLAGS =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/TRACEFLAGS (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/TRACEFLAGS (revision 26986) @@ -1,73 +1,74 @@ 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 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 util.c safefile +44 safefile.c safefile, safedirpath, filechanged 45 envelope.c setsender 46 envelope.c openxscript 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) Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/alias.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/alias.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/alias.c (revision 26986) @@ -1,869 +1,871 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ # include "sendmail.h" #ifndef lint -static char sccsid[] = "@(#)alias.c 8.67 (Berkeley) 1/18/97"; +static char sccsid[] = "@(#)alias.c 8.73 (Berkeley) 5/8/97"; #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; - int naliases; char *owner; auto int stat = EX_OK; char obuf[MAXNAME + 7]; extern char *aliaslookup(); 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 = "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, 203)); -#ifdef LOG if (LogLevel > 9) - syslog(LOG_INFO, "%s: alias %.100s => %s", - e->e_id == NULL ? "NOQUEUE" : e->e_id, + sm_syslog(LOG_INFO, e->e_id, + "alias %.100s => %s", a->q_paddr, shortenstring(p, 203)); -#endif a->q_flags &= ~QSELFREF; if (tTd(27, 5)) { printf("alias: QDONTSEND "); printaddr(a, FALSE); } a->q_flags |= QDONTSEND; - naliases = sendtolist(p, a, sendq, aliaslevel + 1, e); + (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 (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_OPTIONAL|MF_INCLNULL; + 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()) { 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); } rebuildaliases(map, TRUE); isopen = map->map_class->map_open(map, O_RDONLY); SuprErrs = oldSuprErrs; } else { -#ifdef LOG if (LogLevel > 3) - syslog(LOG_INFO, "alias database %s out of date", + sm_syslog(LOG_INFO, NOQID, + "alias database %s out of date", buf); -#endif /* LOG */ 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: ** none. ** ** Side Effects: ** Reads the text version of the database, builds the ** DBM or DB version. */ void rebuildaliases(map, automatic) register MAP *map; bool automatic; { FILE *af; bool nolock = FALSE; + int sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK|SFF_NOWLINK|SFF_NOWFILES; sigfunc_t oldsigint, oldsigquit; #ifdef SIGTSTP sigfunc_t oldsigtstp; #endif if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) return; /* try to lock the source file */ - if ((af = fopen(map->map_file, "r+")) == NULL) + if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL) { struct stat stb; if ((errno != EACCES && errno != EROFS) || automatic || - (af = fopen(map->map_file, "r")) == NULL) + (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; } 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; } 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)) { -#ifdef LOG if (LogLevel > 7) { - syslog(LOG_NOTICE, "alias database %s %srebuilt by %s", + sm_syslog(LOG_NOTICE, NOQID, + "alias database %s %srebuilt by %s", map->map_file, automatic ? "auto" : "", username()); } -#endif /* LOG */ map->map_mflags |= MF_OPEN|MF_WRITABLE; readaliases(map, af, !automatic, 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 } /* ** 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; + 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); -# ifdef LOG if (LogLevel > 7 && logstats) - syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total", + sm_syslog(LOG_INFO, NOQID, + "%s: %d aliases, longest %d bytes, %d bytes total", map->map_file, naliases, longest, bytes); -# endif /* LOG */ } /* ** 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); -#ifdef LOG if (LogLevel > 2) - syslog(LOG_ERR, "%s: forward %s: transient error: %s", - e->e_id == NULL ? "NOQUEUE" : e->e_id, + sm_syslog(LOG_ERR, e->e_id, + "forward %s: transient error: %s", buf, errstring(err)); -#endif } } 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; } } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/aliases.5 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/aliases.5 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/aliases.5 (revision 26986) @@ -1,106 +1,107 @@ +.\" Copyright (c) 1983, 1997 Eric P. Allman .\" Copyright (c) 1985, 1991, 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. .\" -.\" @(#)aliases.5 8.2 (Berkeley) 12/11/93 +.\" @(#)aliases.5 8.3 (Berkeley) 2/1/97 .\" -.Dd December 11, 1993 +.Dd February 1, 1997 .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 . Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/arpadate.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/arpadate.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/arpadate.c (revision 26986) @@ -1,219 +1,219 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)arpadate.c 8.6 (Berkeley) 9/16/96"; +static char sccsid[] = "@(#)arpadate.c 8.7 (Berkeley) 2/1/97"; #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); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/clock.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/clock.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/clock.c (revision 26986) @@ -1,272 +1,287 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)clock.c 8.18 (Berkeley) 12/31/96"; +static char sccsid[] = "@(#)clock.c 8.24 (Berkeley) 4/19/97"; #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. */ -static SIGFUNC_DECL tick __P((int)); +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); } - (void) setsignal(SIGALRM, SIG_IGN); + 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 = (EVENT *) xalloc(sizeof *ev); + 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", intvl, now + intvl, (u_long) func, arg, (u_long) ev); - tick(0); + 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 */ - (void) setsignal(SIGALRM, SIG_IGN); + 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; - free((char *) ev); + ev->ev_link = FreeEventList; + FreeEventList = ev; } /* restore clocks and pick up anything spare */ - tick(0); + 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. */ static SIGFUNC_DECL tick(arg) int arg; { register time_t now; register EVENT *ev; int mypid = getpid(); int olderrno = errno; - (void) setsignal(SIGALRM, SIG_IGN); (void) alarm(0); now = curtime(); if (tTd(5, 4)) printf("tick: now=%ld\n", 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; - free((char *) ev); + 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); } - /* restore signals so that we can take ticks while in ev_func */ - (void) setsignal(SIGALRM, tick); - (void) releasesignal(SIGALRM); - /* call ev_func */ errno = olderrno; (*f)(arg); (void) alarm(0); now = curtime(); } - (void) setsignal(SIGALRM, tick); 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(); #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; } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/collect.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/collect.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/collect.c (revision 26986) @@ -1,767 +1,760 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)collect.c 8.62 (Berkeley) 12/11/96"; +static char sccsid[] = "@(#)collect.c 8.69 (Berkeley) 5/29/97"; #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. -** requeueflag -- this message will be requeued later, so -** don't do final processing on it. ** 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(); 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, requeueflag, hdrp, e) +collect(fp, smtpmode, hdrp, e) FILE *fp; bool smtpmode; - bool requeueflag; 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 = '\0'; + 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[20]; + char dfname[MAXQFNAME]; char bufbuf[MAXLINE]; extern bool isheader(); extern void eatheader(); extern void tferror(); headeronly = hdrp != NULL; /* ** Create the temp file name and create the file. */ if (!headeronly) { + int tfd; struct stat stbuf; strcpy(dfname, queuename(e, 'd')); - if ((tf = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL) + 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) { -#ifdef LOG if (LogLevel > 2) - syslog(LOG_NOTICE, + sm_syslog(LOG_NOTICE, e->e_id, "timeout waiting for input from %s during message collect", CurHostName ? CurHostName : ""); -#endif errno = 0; usrerr("451 timeout waiting for input during message collect"); goto readerr; } CollectTimeout = setevent(dbto, collecttimeout, dbto); } for (;;) { 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) { extern int chompheader(); case MS_UFROM: mstate = MS_HEADER; #ifndef NOTUNIX if (strncmp(buf, "From ", 5) == 0) { extern void eatfrom(); 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; 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); -#ifdef LOG if (LogLevel >= 2) - syslog(LOG_WARNING, "collect: premature EOM: %s", errmsg); -#endif + 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) || 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"; -# ifdef LOG if (LogLevel > 0 && feof(fp)) - syslog(LOG_NOTICE, + sm_syslog(LOG_NOTICE, e->e_id, "collect: %s on connection from %.100s, sender=%s: %s", problem, host, shortenstring(e->e_from.q_paddr, 203), errstring(errno)); -# endif if (feof(fp)) usrerr("451 collect: %s on connection from %s, from=%s", problem, host, shortenstring(e->e_from.q_paddr, 203)); else syserr("451 collect: %s on connection from %s, from=%s", problem, host, shortenstring(e->e_from.q_paddr, 203)); /* 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) { extern void markstats(); markstats(e, (ADDRESS *) NULL); } -#ifdef _FFR_DSN_RRT +#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(); /* 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; e->e_status = "5.2.3"; usrerr("552 Message exceeds maximum fixed size (%ld)", MaxMessageSize); -# ifdef LOG if (LogLevel > 6) - syslog(LOG_NOTICE, "%s: message size (%ld) exceeds maximum (%ld)", - e->e_id, e->e_msgsize, MaxMessageSize); -# endif + 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 *tf; register ENVELOPE *e; { setstat(EX_IOERR); if (errno == ENOSPC) { struct stat st; long avail; long bsize; extern long freediskspace __P((char *, long *)); e->e_flags |= EF_NO_BODY_RETN; if (fstat(fileno(tf), &st) < 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 %qd bytes could not be accepted\n", st.st_size); else fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", (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); (void) freopen("/dev/null", "w", tf); } /* ** 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 *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 */ Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/conf.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/conf.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/conf.c (revision 26986) @@ -1,4783 +1,5032 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)conf.c 8.333 (Berkeley) 1/21/97"; +static char sccsid[] = "@(#)conf.c 8.362 (Berkeley) 6/14/97"; #endif /* not lint */ # include "sendmail.h" # include "pathnames.h" # 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 }, +#if _FFR_PRIVACY_NOETRN + { "noetrn", PRIV_NOETRN }, +#endif { "authwarnings", PRIV_AUTHWARNINGS }, { "noreceipts", PRIV_NORECEIPTS }, { "goaway", PRIV_GOAWAY }, { 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 MAXRULERECURSION # define MAXRULERECURSION 50 /* max ruleset recursion depth */ #endif void setdefaults(e) register ENVELOPE *e; { int i; extern void inittimeouts(); extern void setdefuser(); extern void setupmaps(); extern void setupmailers(); + extern void setupheaders(); 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 */ DefUid = 1; /* option u */ DefGid = 1; /* option g */ 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 = 0; /* option p */ #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; DoubleBounceAddr = "postmaster"; setdefuser(); 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); } /* -** HOST_MAP_INIT -- initialize host class structures -*/ - -bool host_map_init __P((MAP *map, char *args)); - -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 '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); - return TRUE; -} - /* ** SETUPMAILERS -- initialize default mailers */ void setupmailers() { char buf[100]; extern void makemailer(); 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); + 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); #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); } #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; if (ConfigFileRead) ServiceCacheTime = now; fp = fopen(ServiceSwitchFile, "r"); 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 the service file doesn't work, use an absolute fallback */ punt: 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) && !defined(_USE_SUN_NSSWITCH_) /* SunOS */ maptype[svcno++] = "nis"; # endif # endif maptype[svcno++] = "files"; return svcno; } return -1; } /* ** 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(02, 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 #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 #ifdef IRIX64 # define nlist nlist64 #endif /* _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)); + 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)); + 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 #include #include #define X_AVENRUN 0 struct nlist Nl32[] = { { LA_AVENRUN }, { 0 }, }; struct nlist64 Nl64[] = { { LA_AVENRUN }, { 0 }, }; int getla(void) { static int kmem = -1; static enum { getla_none, getla_32, getla_64 } kernel_type = getla_none; - uint32_t avenrun32[3]; - uint64_t avenrun64[3]; + uint32_t avenrun[3]; if (kernel_type == getla_none) { /* Try 32 bit kernel ... */ errno = 0; if (nlist(_PATH_UNIX, Nl32) == 0) { if (tTd(3, 20)) printf("getla: Kernel is 32bit\n"); if (Nl32[X_AVENRUN].n_value == 0) { if (tTd(3, 1)) printf("getla: nlist(%s, %s) ==> 0\n", _PATH_UNIX, LA_AVENRUN); } else kernel_type = getla_32; } else if (errno != 0) { if (tTd(3, 1)) printf("getla: nlist(%s): %s\n", _PATH_UNIX, errstring(errno)); } else { if (tTd(3, 20)) printf("getla: Kernel is not 32bit\n"); } /* Try 64 bit kernel ... */ errno = 0; if (nlist64(_PATH_UNIX, Nl64) == 0) { if (tTd(3, 20)) printf("getla: Kernel is 64bit\n"); if (Nl64[X_AVENRUN].n_value == 0) { if (tTd(3, 1)) printf("getla: nlist(%s, %s) ==> 0\n", _PATH_UNIX, LA_AVENRUN); } else kernel_type = getla_64; } else if (errno != 0) { if (tTd(3, 1)) printf("getla: nlist64(%s): %s\n", _PATH_UNIX, errstring(errno)); } else { if (tTd(3, 20)) printf("getla: Kernel is not 64bit\n"); } } if (kernel_type == getla_none) { if (tTd(3, 1)) printf("getla: Failed to determine kernel type\n"); return -1; } if (kmem < 0) { 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); } switch (kernel_type) { + case getla_none: + return -1; + case getla_32: if (lseek(kmem, (off_t) Nl32[X_AVENRUN].n_value, SEEK_SET) == -1 || - read(kmem, (char *) avenrun32, sizeof(avenrun32)) < sizeof(avenrun32)) + 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{32} = %ld", - (long int) avenrun32[0]); - if (tTd(3, 15)) - printf(", %ld, %ld", - (long int)avenrun32[1], - (long int)avenrun32[2]); - printf("\n"); - } - if (tTd(3, 1)) - printf("getla: %d\n", - (int) (avenrun32[0] + FSCALE/2) >> FSHIFT); - return ((int) (avenrun32[0] + FSCALE/2) >> FSHIFT); + break; case getla_64: /* Using of lseek64 is perhaps overkill ... */ if (lseek64(kmem, (off64_t) Nl64[X_AVENRUN].n_value, SEEK_SET) == -1 || - read(kmem, (char *) avenrun64, sizeof(avenrun64)) < - sizeof(avenrun64)) + read(kmem, (char *) avenrun, sizeof(avenrun)) < + sizeof(avenrun)) { if (tTd(3, 1)) printf("getla: lseek64 or read: %s\n", errstring(errno)); return -1; } - if (tTd(3, 5)) - { - printf("getla: avenrun{64} = %lld", - (long long int) avenrun64[0]); - if (tTd(3, 15)) - printf(", %lld, %lld", - (long long int) avenrun64[1], - (long long int) avenrun64[2]); - printf("\n"); - } - if (tTd(3, 1)) - printf("getla: %d\n", - (int) (avenrun64[0] + FSCALE/2) >> FSHIFT); - return ((int) (avenrun64[0] + FSCALE/2) >> FSHIFT); + break; } - 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) { - syslog(LOG_ERR, "can't open %s: %m", _PATH_AVENRUN); + 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 # 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 (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)); + 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(); extern void setproctitle __P((const char *, ...)); #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); -#ifdef LOG if (LogLevel >= 14) - syslog(LOG_INFO, "deferring connections on port %d: %d per second", + sm_syslog(LOG_INFO, NOQID, + "deferring connections on port %d: %d per second", port, ConnRateThrottle); -#endif sleep(1); } CurrentLA = getla(); - if (CurrentLA >= RefuseLA) + if (CurrentLA >= refusela) { setproctitle("rejecting connections on port %d: load average: %d", port, CurrentLA); -#ifdef LOG if (LogLevel >= 14) - syslog(LOG_INFO, "rejecting connections on port %d: load average: %d", + sm_syslog(LOG_INFO, NOQID, + "rejecting connections on port %d: load average: %d", port, CurrentLA); -#endif return TRUE; } if (!enoughdiskspace(MinBlocksFree + 1)) { setproctitle("rejecting connections on port %d: min free: %d", port, MinBlocksFree); -#ifdef LOG if (LogLevel >= 14) - syslog(LOG_INFO, "rejecting connections on port %d: min free: %d", + sm_syslog(LOG_INFO, NOQID, + "rejecting connections on port %d: min free: %d", port, MinBlocksFree); -#endif 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); -#ifdef LOG if (LogLevel >= 14) - syslog(LOG_INFO, "rejecting connections on port %d: %d children, max %d", + sm_syslog(LOG_INFO, NOQID, + "rejecting connections on port %d: %d children, max %d", port, CurChildren, MaxChildren); -#endif 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 +# 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; extern char **environ; /* - ** Move the environment so setproctitle can use the space at + ** Move the environment so setproctitle can use the space at ** the top of memory. */ for (i = 0; envp[i] != NULL; i++) continue; environ = (char **) xalloc(sizeof (char *) * (i + 1)); for (i = 0; envp[i] != NULL; i++) environ[i] = newstr(envp[i]); environ[i] = NULL; /* ** Save start and extent of argv for setproctitle. */ Argv = argv; if (i > 0) LastArgv = envp[i - 1] + strlen(envp[i - 1]); else LastArgv = argv[argc - 1] + strlen(argv[argc - 1]); } #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) { -#ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, + sm_syslog(LOG_ALERT, NOQID, "reapchild: waitpid loop: pid=%d, status=%x", pid, status); -#endif 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; - while ((pid = wait(&status)) > 0) + /* + ** 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 /* ** 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);} 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; /* 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 /* ** 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. */ -#if !HASSNPRINTF - /************************************************************** * Original: * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 - * A bombproof version of doprnt (dopr) included. + * 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 $";*/ -static void dopr(); -static char *end; +static void sm_dopr(); +static char *DoprEnd; static 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; - end = str + count - 1; + DoprEnd = str + count - 1; SnprfOverflow = 0; - dopr( str, fmt, args ); + sm_dopr( str, fmt, args ); if (count > 0) - end[0] = 0; + DoprEnd[0] = 0; if (SnprfOverflow && tTd(57, 2)) printf("\nvsnprintf overflow, len = %d, str = %s", count, shortenstring(str, 203)); return strlen(str); } +# endif /* !luna2 */ +#endif /* !HASSNPRINTF */ + /* - * dopr(): poor man's version of doprintf + * sm_dopr(): poor man's version of doprintf */ static void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth)); static void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad)); static void dostr __P(( char * , int )); static char *output; static void dopr_outch __P(( int c )); +static int SyslogErrno; static void -dopr( buffer, format, args ) +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++) ){ - 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': + 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; + 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 *); + 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 '%': dopr_outch( ch ); continue; - default: - dostr( "???????" , 0); - } - break; - default: - dopr_outch( ch ); - 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(sys_errlist[SyslogErrno], 0); +#endif + break; + + case '%': dopr_outch( ch ); continue; + default: + dostr( "???????" , 0); + } + break; + default: + dopr_outch( ch ); + break; + } } *output = 0; } static void fmtstr( value, ljust, len, zpad, maxwidth ) char *value; int ljust, len, zpad, maxwidth; { int padlen, strlen; /* amount to pad */ if( value == 0 ){ - value = ""; + 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; + dopr_outch( ' ' ); + --padlen; } dostr( value, maxwidth ); while( padlen < 0 ) { - dopr_outch( ' ' ); - ++padlen; + dopr_outch( ' ' ); + ++padlen; } } static 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 )); */ + value, base, dosign, ljust, len, zpad )); */ uvalue = value; if( dosign ){ - if( value < 0 ) { - signvalue = '-'; - uvalue = -value; - } + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } } if( base < 0 ){ - caps = 1; - base = -base; + caps = 1; + base = -base; } do{ - convert[place++] = - (caps? "0123456789ABCDEF":"0123456789abcdef") - [uvalue % (unsigned)base ]; - uvalue = (uvalue / (unsigned)base ); + 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)); */ + convert,place,signvalue,padlen)); */ if( zpad && padlen > 0 ){ - if( signvalue ){ - dopr_outch( signvalue ); - --padlen; - signvalue = 0; - } - while( padlen > 0 ){ - dopr_outch( zpad ); - --padlen; - } + if( signvalue ){ + dopr_outch( signvalue ); + --padlen; + signvalue = 0; + } + while( padlen > 0 ){ + dopr_outch( zpad ); + --padlen; + } } while( padlen > 0 ) { - dopr_outch( ' ' ); - --padlen; + dopr_outch( ' ' ); + --padlen; } if( signvalue ) dopr_outch( signvalue ); while( place > 0 ) dopr_outch( convert[--place] ); while( padlen < 0 ){ - dopr_outch( ' ' ); - ++padlen; + dopr_outch( ' ' ); + ++padlen; } } static void dostr( str , cut) char *str; int cut; { if (cut) { while(*str && cut-- > 0) dopr_outch(*str++); } else { while(*str) dopr_outch(*str++); } } static void dopr_outch( c ) int c; { #if 0 if( iscntrl(c) && c != '\n' && c != '\t' ){ - c = '@' + (c & 0x1F); - if( end == 0 || output < end ) - *output++ = '^'; + c = '@' + (c & 0x1F); + if( DoprEnd == 0 || output < DoprEnd ) + *output++ = '^'; } #endif - if( end == 0 || output < end ) - *output++ = c; + if( DoprEnd == 0 || output < DoprEnd ) + *output++ = c; else SnprfOverflow++; } - -# endif /* !luna2 */ - -#endif /* !HASSNPRINTF */ /* ** 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 # 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 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')) 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; 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 != '#' && !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) + if (fs.SFS_BAVAIL <= 0) return 0; else return 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) { -#ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, - "%s: low on space (have %ld, %s needs %ld in %s)", - CurEnv->e_id == NULL ? "[NOQUEUE]" : CurEnv->e_id, + 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); -#endif 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 EOPENTIMEOUT: /* PSEUDO: open timed out */ + 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; # 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); - if (fcntl(fd, action, &lfd) >= 0) + while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR) + continue; + if (i >= 0) { if (tTd(55, 60)) printf("SUCCESS\n"); return TRUE; } if (tTd(55, 60)) printf("(%s) ", errstring(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 (errno == EINVAL) { if (tTd(55, 60)) printf("SUCCESS\n"); return TRUE; } if (!bitset(LOCK_NB, type) || (errno != EACCES && errno != EAGAIN)) { int omode = -1; # ifdef F_GETFL int oerrno = errno; (void) fcntl(fd, F_GETFL, &omode); errno = oerrno; # 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); - if (flock(fd, type) >= 0) + while ((i = flock(fd, type)) < 0 && errno == EINTR) + continue; + if (i >= 0) { if (tTd(55, 60)) printf("SUCCESS\n"); return TRUE; } if (tTd(55, 60)) printf("(%s) ", errstring(errno)); if (!bitset(LOCK_NB, type) || errno != EWOULDBLOCK) { int omode = -1; # ifdef F_GETFL int oerrno = errno; (void) fcntl(fd, F_GETFL, &omode); errno = oerrno; # 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"); 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 only root can chown the file to an arbitrary -** user. +** 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) +chownsafe(fd, safedir) int fd; + bool safedir; { -#ifdef __hpux - char *s; - int tfd; - uid_t o_uid, o_euid; - gid_t o_gid, o_egid; - bool rval; - struct stat stbuf; - - o_uid = getuid(); - o_euid = geteuid(); - o_gid = getgid(); - o_egid = getegid(); - fstat(fd, &stbuf); - setresuid(stbuf.st_uid, stbuf.st_uid, -1); - setresgid(stbuf.st_gid, stbuf.st_gid, -1); - s = tmpnam(NULL); - tfd = open(s, O_RDONLY|O_CREAT, 0600); - rval = fchown(tfd, DefUid, DefGid) != 0; - close(tfd); - setresuid(o_uid, o_euid, -1); - setresgid(o_gid, o_egid, -1); - unlink(s); - return rval; -#else -# ifdef _POSIX_CHOWN_RESTRICTED -# if _POSIX_CHOWN_RESTRICTED == -1 - return FALSE; -# else - return TRUE; -# endif -# else -# ifdef _PC_CHOWN_RESTRICTED +#if !defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1 +# if defined(_PC_CHOWN_RESTRICTED) int rval; + /* give the system administrator a chance to override */ + if (ChownAlwaysSafe) + 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 (errno == 0) - return rval > 0; -# endif -# ifdef BSD - return TRUE; +# if SAFENFSPATHCONF + return errno == 0 && rval IS_SAFE_CHOWN; # else - return FALSE; + return safedir && errno == 0 && rval IS_SAFE_CHOWN; # endif # endif +#else + return ChownAlwaysSafe; #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(); 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 #ifdef SUN_EXTENSIONS 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 SUN_EXTENSIONS 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: ** TRUE -- if the connection should be accepted. ** FALSE -- if it should be 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 bool validate_connection(sap, hostname, e) SOCKADDR *sap; char *hostname; ENVELOPE *e; { if (rscheck("check_relay", hostname, anynet_ntoa(sap), e) != EX_OK) return FALSE; #if TCPWRAPPERS if (!hosts_ctl("sendmail", hostname, anynet_ntoa(sap), STRING_UNKNOWN)) { -# ifdef LOG if (LogLevel >= 4) - syslog(LOG_NOTICE, "tcpwrappers (%s, %s) rejection", + sm_syslog(LOG_NOTICE, NOQID, + "tcpwrappers (%s, %s) rejection", hostname, anynet_ntoa(sap)); -# endif return FALSE; } #endif return TRUE; } #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 */ #include /* * 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. */ -#ifdef SIOCGIFCONF +#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN struct rtentry; struct mbuf; # include # ifndef SUNOS403 # include # endif # include #endif void load_if_names() { -#ifdef SIOCGIFCONF +#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN int s; int i; - struct ifconf ifc; - char interfacebuf[10240]; + 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 */ - ifc.ifc_buf = interfacebuf; - ifc.ifc_len = sizeof interfacebuf; +# ifdef SIOCGIFNUM + 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(); - extern struct hostent *gethostbyaddr(); #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); - if (!bitset(IFF_UP, ifrf.ifr_flags)) - continue; +# define IFRFREF ifrf #else - if (!bitset(IFF_UP, ifr->ifr_flags)) - continue; +# 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 (strcmp("lo0", ifr->ifr_name) == 0) + 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) { -#ifdef LOG if (LogLevel > 3) - syslog(LOG_WARNING, - "gethostbyaddr() failed for %.100s\n", - inet_ntoa(ia)); + 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; + VA_LOCAL_DECL + + 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. */ #ifdef __hpux # 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 * 2]; + 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[] = { #if HESIOD "HESIOD", #endif #if HES_GETMAILHOST "HES_GETMAILHOST", #endif #if LDAPMAP "LDAPMAP", #endif -#ifdef LOG +#if LOG "LOG", #endif #if MATCHGECOS "MATCHGECOS", #endif #if MIME7TO8 "MIME7TO8", #endif #if MIME8TO7 "MIME8TO7", #endif #if NAMED_BIND "NAMED_BIND", #endif #if 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 #if NEWDB "NEWDB", #endif #if NIS "NIS", #endif #if NISPLUS "NISPLUS", #endif #if QUEUE "QUEUE", #endif #if SCANF "SCANF", #endif #if SMTP "SMTP", #endif #if SMTPDEBUG "SMTPDEBUG", #endif #if SUID_ROOT_FILES_OK "SUID_ROOT_FILES_OK", #endif #if TCPWRAPPERS "TCPWRAPPERS", #endif #if USERDB "USERDB", #endif #if XDEBUG "XDEBUG", #endif #if 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 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 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 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 }; Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/conf.h =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/conf.h (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/conf.h (revision 26986) @@ -1,2184 +1,2281 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. * - * @(#)conf.h 8.288 (Berkeley) 1/17/97 + * @(#)conf.h 8.313 (Berkeley) 6/11/97 */ /* ** 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 # include # 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 */ /********************************************************************** ** 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 /* enable logging -- don't turn off */ +# 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. */ #ifdef __hpux /* common definitions for HP-UX 9.x and 10.x */ # undef m_flags /* conflict between 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 # define syslog hard_syslog +# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */ # ifdef V4FS /* HP-UX 10.x */ # define _PATH_UNIX "/stand/vmunix" # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # 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" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # ifdef __STDC__ extern void hard_syslog(int, char *, ...); # endif # endif #endif /* ** IBM AIX 4.x */ #ifdef _AIX4 +# include # define _AIX3 1 /* pull in AIX3 stuff */ # define USESETEUID 1 /* seteuid(2) works */ # define TZ_TYPE TZ_NAME /* use tzname[] vector */ # if _AIX4 >= 40200 # define HASSETREUID 1 /* setreuid(2) works as of AIX 4.2 */ # endif #endif /* ** IBM AIX 3.x -- actually tested for 3.2.3 */ #ifdef _AIX3 # include # include /* to get byte order */ # 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 */ # define _PATH_VENDORCF "/usr/local/newmail/sendmail.cf" # 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 LA_TYPE LA_IRIX6 /* figure out at run time */ +# define SAFENFSPATHCONF 0 /* pathconf(2) lies on NFS filesystems */ # else # define LA_TYPE LA_INT # ifdef IRIX64 # define NAMELISTMASK 0x7fffffffffffffff /* mask for nlist() values */ # else # define NAMELISTMASK 0x7fffffff /* mask for nlist() values */ # endif # endif # if defined(IRIX64) || defined(IRIX5) # include # include # define ARGV_T char *const * # define HASSETRLIMIT 1 /* has setrlimit(2) syscall */ # define HASGETDTABLESIZE 1 /* has getdtablesize(2) syscall */ # 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 HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */ # 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 # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # 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 */ # ifndef LA_TYPE # define LA_TYPE LA_KSTAT /* use kstat(3k) -- may work in < 2.5 */ # endif # endif # if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) # define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ # endif # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ # endif # else /* SunOS 4.0.3 or 4.1.x */ # 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 # 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 */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define LA_TYPE LA_ALPHAOSF # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # define _PATH_VENDOR_CF "/var/adm/sendmail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/var/run/sendmail.pid" # endif #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 sleep sleepX # 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 # define _PATH_VENDOR_CF "/etc/sendmail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail/sendmail.pid" # endif #endif /* ** 4.4 BSD ** ** See also BSD defines. */ -#if defined(BSD4_4) && !defined(__bsdi__) +#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) */ # include # define ERRLIST_PREDEFINED /* don't declare sys_errlist */ # define BSD4_4_SOCKADDR /* has sa_len */ # 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) */ # 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 # 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 */ # ifndef OLD_NEWDB # define OLD_NEWDB 1 /* old version of newdb library */ # endif # define SPT_PADCHAR '\0' /* pad process title with nulls */ # endif #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) */ # 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 # 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) +#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 # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # 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 # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # 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 _SCO_unix_4_2 # 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" #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" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # 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" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # 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 */ # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # 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(); # 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 */ # 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 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 /* we have setvbuf(3) in libc */ +# define HASSETVBUF 1 /* has setvbuf(3) in libc */ +# define HASSTRERROR 1 /* has strerror(3) */ # define SIGFUNC_DEFINED /* sigfunc_t already defined */ -# define SIGFUNC_RETURN (0) /* XXX this is a guess */ -# define SIGFUNC_DECL int /* XXX this is a guess */ +# 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 # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # 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 # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" #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 # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # 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 # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # 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) */ #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" # define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" # 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 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 # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # 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 db.h & sys/sysmacros.h on HIUX 3050 */ # undef m_flags # ifdef __STDC__ extern int syslog(int, char *, ...); # 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" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" #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" # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # 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; # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" #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" # define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" # 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 # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # 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 /********************************************************************** ** 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 #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 /* ** 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 _NET_PROHIB. If +** 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 OLD_NEWDB # define OLD_NEWDB 0 /* assume newer version of newdb */ #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 /* ** 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 /********************************************************************** ** 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 EOPENTIMEOUT (E_PSEUDOBASE + 0) /* timeout on open */ +#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 NAMED_BIND -# include -# ifdef __svr4__ -# ifdef NOERROR -# undef NOERROR /* avoid compiler conflict with stream.h */ -# endif -# endif -# ifndef __ksr__ -extern int h_errno; -# endif +#if NAMED_BIND && !defined(__ksr__) +extern int h_errno; #endif /* ** The size of an IP address -- can't use sizeof because of problems ** on Crays, where everything is 64 bits. This will break if/when ** IP addresses are expanded to eight bytes. */ #ifndef INADDRSZ # define INADDRSZ 4 #endif /* ** The size of various known types -- for reading network protocols. ** Again, we can't use sizeof because of compiler randomness. */ #ifndef INT16SZ # define INT16SZ 2 #endif #ifndef INT32SZ # define INT32SZ 4 #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 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 tobuf (deliver.c) ** Tweak this to match your syslog implementation. It will have to ** allow for the extra information printed. */ #ifndef TOBUFSIZE # if (SYSLOG_BUFSIZE) > 768 # define TOBUFSIZE (SYSLOG_BUFSIZE - 512) # else -# define TOBUFSIZE 256 +# define TOBUFSIZE (SYSLOG_BUFSIZE / 2) # endif #endif /* TOBUFSIZE must never be permitted to exceed MAXLINE - 128 */ #if TOBUFSIZE > (MAXLINE - 128) # undef TOBUFSIZE # define TOBUFSIZE (MAXLINE - 128) #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 /* Silly SCO /usr/include/setjmp.h file has #define setjmp(env) setjmp(env) */ # ifdef setjmp # undef setjmp # endif # define jmp_buf sigjmp_buf # define setjmp(env) sigsetjmp(env, 1) # 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 Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/convtime.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/convtime.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/convtime.c (revision 26986) @@ -1,205 +1,205 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)convtime.c 8.8 (Berkeley) 11/24/96"; +static char sccsid[] = "@(#)convtime.c 8.9 (Berkeley) 2/1/97"; #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); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/daemon.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/daemon.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/daemon.c (revision 26986) @@ -1,1845 +1,1974 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #include #include "sendmail.h" #ifndef lint #ifdef DAEMON -static char sccsid[] = "@(#)daemon.c 8.159 (Berkeley) 1/14/97 (with daemon mode)"; +static char sccsid[] = "@(#)daemon.c 8.175 (Berkeley) 6/1/97 (with daemon mode)"; #else -static char sccsid[] = "@(#)daemon.c 8.159 (Berkeley) 1/14/97 (without daemon mode)"; +static char sccsid[] = "@(#)daemon.c 8.175 (Berkeley) 6/1/97 (without daemon mode)"; #endif #endif /* not lint */ -#if DAEMON || defined(SOCK_STREAM) +#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: ** TRUE -- if a "null server" should be used -- that is, one ** that rejects all commands. ** FALSE -- to use a normal server. ** ** 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 */ bool getrequests(e) ENVELOPE *e; { int t; bool refusingconnections = TRUE; FILE *pidf; int socksize; #if XDEBUG bool j_has_dot; #endif extern void reapchild(); extern int opendaemonsocket __P((bool)); /* ** Set up the address for the mailer. */ if (DaemonAddr.sin.sin_family == 0) DaemonAddr.sin.sin_family = AF_INET; if (DaemonAddr.sin.sin_addr.s_addr == 0) DaemonAddr.sin.sin_addr.s_addr = INADDR_ANY; if (DaemonAddr.sin.sin_port == 0) { register struct servent *sp; sp = getservbyname("smtp", "tcp"); if (sp == NULL) { syserr("554 service \"smtp\" unknown"); DaemonAddr.sin.sin_port = htons(25); } else DaemonAddr.sin.sin_port = sp->s_port; } /* ** Try to actually open the connection. */ if (tTd(15, 1)) printf("getrequests: port 0x%x\n", DaemonAddr.sin.sin_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_CREAT|O_TRUNC, 0644, - SFF_NOSLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT); - if (pidf != NULL) + 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 int lotherend; int savederrno; int pipefd[2]; extern bool refuseconnections(); - extern int getla(); /* see if we are rejecting connections */ (void) blocksignal(SIGALRM); if (refuseconnections(ntohs(DaemonAddr.sin.sin_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"); - syslog(LOG_ALERT, "daemon process doesn't have $j in $=w; see syslog"); + 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"); - syslog(LOG_ALERT, "daemon process $j lost dot; see syslog"); + 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(DaemonAddr.sin.sin_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); - do + 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, &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); - } while (t < 0 && errno == EINTR); + 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; bool nullconn; /* ** 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(0); } InChannel = inchannel; OutChannel = outchannel; DisConnected = FALSE; + /* open maps for check_relay ruleset */ + initmaps(FALSE, e); + /* validate the connection */ HoldErrs = TRUE; nullconn = !validate_connection(&RealHostAddr, RealHostName, e); HoldErrs = FALSE; if (nullconn) break; #ifdef XLA if (!xla_host_ok(RealHostName)) { message("421 Too many SMTP sessions for this host"); exit(0); } #endif if (tTd(15, 2)) printf("getreq: returning (normal server)\n"); return FALSE; } /* 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 (null server)\n"); return TRUE; } /* ** 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: -# ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, "problem creating SMTP socket"); -# endif /* LOG */ + 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 i = 0; + 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] == '[') { long hid; 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: bcopy(hp->h_addr, addr.sa.sa_data, hp->h_length); break; } - i = 1; + addrno = 1; } /* ** Determine the port number. */ if (port == 0) { register struct servent *sp = getservbyname("smtp", "tcp"); if (sp == NULL) { -#ifdef LOG if (LogLevel > 2) - syslog(LOG_ERR, "makeconnection: service \"smtp\" unknown"); -#endif + 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(AF_INET, 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 (connect(s, (struct sockaddr *) &addr, addrlen) >= 0) - { - if (ev != NULL) - clrevent(ev); + i = connect(s, (struct sockaddr *) &addr, addrlen); + sav_errno = errno; + if (ev != NULL) + clrevent(ev); + if (i >= 0) break; - } } - sav_errno = errno; - if (ev != NULL) - clrevent(ev); + 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 (hp != NULL && hp->h_addr_list[i]) + if (hp != NULL && hp->h_addr_list[addrno]) { 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[i++], + bcopy(hp->h_addr_list[addrno++], &addr.sin.sin_addr, INADDRSZ); break; #endif default: - bcopy(hp->h_addr_list[i++], + 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)) { -#ifdef LOG - syslog(LOG_CRIT, "My unqualified host name (%s) unknown; sleeping for retry", + sm_syslog(LOG_CRIT, NOQID, + "My unqualified host name (%s) unknown; sleeping for retry", hostbuf); -#endif message("My unqualified host name (%s) unknown; sleeping for retry", hostbuf); sleep(60); if (!getcanonname(hostbuf, size, TRUE)) { -#ifdef LOG - syslog(LOG_ALERT, "unable to qualify my own domain name (%s) -- using short name", + sm_syslog(LOG_ALERT, NOQID, + "unable to qualify my own domain name (%s) -- using short name", hostbuf); -#endif message("WARNING: unable to qualify my own domain name (%s) -- using short name", hostbuf); } } return (hp); } /* ** 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 ** ** Returns: ** The user@host information associated with this descriptor. */ static jmp_buf CtxAuthTimeout; static void authtimeout() { longjmp(CtxAuthTimeout, 1); } char * getauthinfo(fd) int fd; { int falen; register char *volatile p = NULL; SOCKADDR la; int lalen; register struct servent *sp; volatile int s; int i; EVENT *ev; int nleft; + struct hostent *hp; + char **ha; + bool may_be_forged; char ibuf[MAXNAME + 1]; static char hbuf[MAXNAME * 2 + 2]; falen = sizeof RealHostAddr; if (isatty(fd) || getpeername(fd, &RealHostAddr.sa, &falen) < 0 || falen <= 0 || RealHostAddr.sa.sa_family == 0) { (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] == '[') + { + /* address is not a socket */ + may_be_forged = FALSE; + } + else + { + /* try to match the reverse against the forward lookup */ + hp = gethostbyname(RealHostName); + + if (hp == NULL) + may_be_forged = TRUE; + else + { + for (ha = hp->h_addr_list; *ha != NULL; ha++) + if (bcmp(*ha, + (char *) &RealHostAddr.sin.sin_addr, + hp->h_length) == 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 */ p = strchr(p, ':'); if (p == NULL) { /* malformed response */ goto noident; } /* 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 */ 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) { int ipoptlen, 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(ipopt.ipopt_dst)); + inet_ntoa(GET_IPOPT_DST(ipopt.ipopt_dst))); i = strlen(p); p += i; l -= strlen(p); - /* o[1] is option length */ - j = *++o / sizeof(struct in_addr) - 1; + j = o[1] / sizeof(struct in_addr) - 1; /* q skips length and router pointer to data */ - q = o + 2; + 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; + o += o[1]; break; default: /* Skip over option */ o += o[1]; break; } } snprintf(p, SPACELEFT(hbuf, p), "]"); goto postipsr; } #endif noipsr: 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)"); + } postipsr: 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 (unused). +** 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. +** 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)); } - return s->s_namecanon.nc_cname; + if (*statp != EX_OK) + 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), av); - s->s_namecanon.nc_cname = newstr(hbuf); - } + cp = map_rewrite(map, name, strlen(name), NULL); else - { cp = map_rewrite(map, hbuf, strlen(hbuf), av); - s->s_namecanon.nc_cname = newstr(cp); - } 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 */ - cp = map_rewrite(map, (char *) hp->h_name, strlen(hp->h_name), av); s->s_namecanon.nc_stat = *statp = EX_OK; - s->s_namecanon.nc_cname = newstr(cp); + 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 ** ** Returns: ** The host name associated with this descriptor, if it can ** be determined. ** NULL otherwise. ** ** Side Effects: ** none */ char * getauthinfo(fd) int fd; { 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) - return hp->h_name; - *statp = EX_NOHOST; - return NULL; + 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), av); + 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 '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); + 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 SOCK_STREAM +#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) return (char *) hp->h_name; else { /* produce a dotted quad */ static char buf[203]; (void) snprintf(buf, sizeof buf, "[%.200s]", anynet_ntoa(sap)); return buf; } } #endif /* SOCK_STREAM */ Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/deliver.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/deliver.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/deliver.c (revision 26986) @@ -1,3411 +1,3465 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)deliver.c 8.266 (Berkeley) 1/17/97"; +static char sccsid[] = "@(#)deliver.c 8.282 (Berkeley) 6/11/97"; #endif /* not lint */ #include "sendmail.h" #include #if NAMED_BIND #include extern int h_errno; #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; - bool oldverbose = Verbose; + int oldverbose = Verbose; bool somedeliveries = FALSE; pid_t pid; extern void sendenvelope(); /* ** 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 */ - CurrentLA = getla(); 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(); 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; } else { if (tTd(13, 30)) printf(" ... deliverable\n"); somedeliveries = TRUE; } } if (owner != NULL && otherowners > 0) { extern HDR *copyheader(); extern ADDRESS *copyqueue(); + 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)) - { - char df1buf[20], df2buf[20]; - - ee->e_dfp = NULL; - snprintf(df1buf, sizeof df1buf, "%s", - queuename(e, 'd')); - snprintf(df2buf, sizeof df2buf, "%s", - queuename(ee, 'd')); - if (link(df1buf, df2buf) < 0) - { - int saverrno = errno; - - syserr("sendall: link(%s, %s)", - df1buf, df2buf); - if (saverrno == EEXIST) - { - if (unlink(df2buf) < 0) - { - syserr("!sendall: unlink(%s): permanent", - df2buf); - /*NOTREACHED*/ - } - if (link(df1buf, df2buf) < 0) - { - syserr("!sendall: link(%s, %s): permanent", - df1buf, df2buf); - /*NOTREACHED*/ - } - } - } - } -#ifdef LOG + dup_queue_file(e, ee, 'd'); + openxscript(ee); if (LogLevel > 4) - syslog(LOG_INFO, "%s: clone %s, owner=%s", - ee->e_id, e->e_id, owner); -#endif + 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(); + e->e_ntries++; + for (ee = splitenv; ee != NULL; ee = ee->e_sibling) + { + ee->e_dtime = curtime(); + 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 = TRUE; + Verbose = 2; break; case SM_QUEUE: case SM_DEFER: queueonly: if (e->e_nrcpts > 0) e->e_flags |= EF_INQUEUE; dropenvelope(e, FALSE); 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, FALSE); /* arrange to reacquire lock after fork */ e->e_id = qid; } for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { /* save id for future use */ char *qid = ee->e_id; /* drop envelope in parent */ ee->e_flags |= EF_INQUEUE; dropenvelope(ee, FALSE); /* and save qid for reacquisition */ ee->e_id = qid; } # endif /* !HASFLOCK */ pid = fork(); if (pid < 0) { goto queueonly; } else if (pid > 0) { # if HASFLOCK /* be sure we leave the temp files to our child */ /* 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; /* 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; # 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) { e->e_flags |= EF_INQUEUE; finis(); } + /* be sure to give error messages in child */ + QuickAbort = OnlyOneError = 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 = e->e_sibling) (void) dowork(ee->e_id, FALSE, FALSE, ee); (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; char 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); -#ifdef LOG if (LogLevel > 80) - syslog(LOG_DEBUG, "%s: sendenvelope, flags=0x%x", - e->e_id == NULL ? "[NOQUEUE]" : e->e_id, + sm_syslog(LOG_DEBUG, e->e_id, + "sendenvelope, flags=0x%x", e->e_flags); -#endif /* ** 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 */ char *firstsig; /* signature of firstto */ pid_t pid = -1; char *volatile curhost; register volatile 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(); 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); /* ** 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, 203); 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; to->q_status = "5.2.3"; 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) { rcode = mailfile(user, ctladdr, SFF_CREAT, e); giveresponse(rcode, m, NULL, ctladdr, xstart, e); e->e_nsent++; if (rcode == EX_OK) { to->q_flags |= QSENT; markstats(e, to); 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(); 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, 203), 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') { register char *p; 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; } - strncpy(hostbuf, curhost, p - curhost); - hostbuf[p - curhost] = '\0'; + 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, 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); /* 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, 203), 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, 203), 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, 203), m->m_name, mpvect[0], mpvect[1]); else syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", shortenstring(e->e_to, 203), 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, 203), 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); # ifdef SIGCHLD (void) setsignal(SIGCHLD, SIG_DFL); # endif /* SIGCHLD */ DOFORK(FORK); /* pid is set by DOFORK */ if (pid < 0) { /* failure */ syserr("%s... openmailer(%s): cannot fork", shortenstring(e->e_to, 203), 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->m_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) (void) initgroups(ctladdr->q_ruser != NULL ? ctladdr->q_ruser : ctladdr->q_user, ctladdr->q_gid); new_gid = ctladdr->q_gid; } else { if (!DontInitGroups) (void) initgroups(DefUser, DefGid); 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; 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 if (!bitnset(M_SPECIFIC_UID, m->m_flags)) 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 *p, *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, 203), m->m_name, rpvect[1]); _exit(EX_OSERR); } (void) close(rpvect[1]); } else if (OpMode == MD_SMTP || OpMode == MD_DAEMON || HoldErrs || DisConnected) { /* 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, 203), 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, 203), 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, 203), 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. */ mci = (MCI *) xalloc(sizeof *mci); bzero((char *) mci, sizeof *mci); mci->mci_mailer = m; mci->mci_state = clever ? MCIS_OPENING : 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 /* 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. */ 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 *)); extern int smtpgetstat __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 ((i = smtprcpt(to, m, mci, e)) != EX_OK) + 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 _FFR_LMTP if (bitnset(M_LMTP, m->m_flags)) { tobuf[0] = '\0'; anyok = FALSE; } else # endif #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 _FFR_LMTP /* if running LMTP, get the status for each address */ if (bitnset(M_LMTP, m->m_flags)) { rcode = smtpgetstat(m, mci, e); if (rcode == EX_OK) { - strcat(tobuf, ","); - strcat(tobuf, to->q_paddr); + 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 #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 _FFR_LMTP 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 #endif if (tobuf[0] != '\0') giveresponse(rcode, m, mci, ctladdr, xstart, e); if (anyok) markstats(e, tochain); 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, 203), 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; q->q_rstatus = mci->mci_rstatus; } 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; /* 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); -#ifdef _FFR_TIMEOUT_WAIT +#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]; /* ** 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++ == ':') { (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 (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; { -# ifdef LOG 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) { char *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; - syslog(LOG_INFO, "%s: to=%.*s [more]%s", - e->e_id, ++q - p, p, buf); + sm_syslog(LOG_INFO, e->e_id, + "to=%.*s [more]%s", + ++q - p, p, buf); p = q; } - syslog(LOG_INFO, "%s: to=%s%s", e->e_id, p, buf); + 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; - syslog(LOG_INFO, "%s: to=%.*s [more]", - e->e_id, ++q - p, p); + sm_syslog(LOG_INFO, e->e_id, + "to=%.*s [more]", + ++q - p, p); p = q; } - syslog(LOG_INFO, "%s: to=%s", e->e_id, p); + 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); } - syslog(LOG_INFO, "%s: %s", e->e_id, buf); + 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); } - syslog(LOG_INFO, "%s: %.1000s", e->e_id, buf); + 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) { char *p = macvalue('h', e); if (p != NULL && p[0] != '\0') snprintf(buf, sizeof buf, "relay=%.100s", p); } if (buf[0] != '\0') - syslog(LOG_INFO, "%s: %.1000s", e->e_id, buf); + sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); - syslog(LOG_INFO, "%s: stat=%s", e->e_id, shortenstring(stat, 63)); + sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(stat, 63)); # endif /* short log buffer */ -# endif /* LOG */ } /* ** 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; 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, mci, PXLF_NOTHINGSPECIAL); + putxline(buf, strlen(buf), mci, PXLF_NOTHINGSPECIAL); } /* ** 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; 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; 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]; /* 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: -#ifdef _FFR_NONULLS +#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); pos++; } for (xp = buf; xp < bp; xp++) putc(*xp, mci->mci_out); if (c == '\n') { fputs(mci->mci_mailer->m_eol, mci->mci_out); 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); 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; } -#ifdef _FFR_NONULLS +#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); fputs(mci->mci_mailer->m_eol, mci->mci_out); 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); pos = 0; ostate = OS_HEAD; } else { if (TrafficLogFile != NULL) putc(c, TrafficLogFile); putc(c, mci->mci_out); 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); 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); } } 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. ** 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. */ int mailfile(filename, ctladdr, sfflags, e) char *filename; ADDRESS *ctladdr; int sfflags; register ENVELOPE *e; { register FILE *f; register pid_t pid = -1; int mode; bool suidwarn = geteuid() == 0; if (tTd(11, 1)) { printf("mailfile %s\n ctladdr=", filename); printaddr(ctladdr, FALSE); } 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; /* ** 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; 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; #ifdef HASLSTAT if ((SafeFileEnv != NULL ? lstat(filename, &stb) : stat(filename, &stb)) < 0) #else if (stat(filename, &stb) < 0) #endif { stb.st_mode = FileMode; oflags |= O_CREAT|O_EXCL; } else if (bitset(0111, stb.st_mode) || stb.st_nlink != 1 || (SafeFileEnv != NULL && !S_ISREG(stb.st_mode))) exit(EX_CANTCREAT); 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 (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 (FileMailer != NULL && FileMailer->m_uid != 0) { RealUserName = DefUser; RealUid = FileMailer->m_uid; } else { RealUserName = DefUser; RealUid = DefUid; } /* select a new group to run as */ if (bitset(S_ISGID, mode)) RealGid = stb.st_gid; else if (ctladdr != NULL && ctladdr->q_uid != 0) RealGid = ctladdr->q_gid; else if (FileMailer != NULL && FileMailer->m_gid != 0) RealGid = FileMailer->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) (void) initgroups(RealUserName, RealGid); /* 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); - sfflags |= SFF_NOPATHCHECK; + sfflags |= SFF_NOPATHCHECK|SFF_NOLINK; sfflags &= ~SFF_OPENASROOT; f = safefopen(filename, oflags, FileMode, sfflags); if (f == NULL) { message("554 cannot open: %s", errstring(errno)); exit(EX_CANTCREAT); } if (fstat(fileno(f), &stb) < 0) { message("554 cannot fstat %s", errstring(errno)); exit(EX_CANTCREAT); } bzero(&mcibuf, sizeof mcibuf); mcibuf.mci_mailer = FileMailer; mcibuf.mci_out = f; if (bitnset(M_7BITS, FileMailer->m_flags)) mcibuf.mci_flags |= MCIF_7BIT; 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 (WIFEXITED(st)) return (WEXITSTATUS(st)); else { syserr("child died on signal %d", st); return (EX_UNAVAILABLE); } /*NOTREACHED*/ } return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ } /* ** 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; } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/domain.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/domain.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/domain.c (revision 26986) @@ -1,896 +1,898 @@ /* - * Copyright (c) 1986, 1995, 1996 Eric P. Allman + * Copyright (c) 1986, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #include "sendmail.h" #ifndef lint #if NAMED_BIND -static char sccsid[] = "@(#)domain.c 8.64 (Berkeley) 10/30/96 (with name server)"; +static char sccsid[] = "@(#)domain.c 8.67 (Berkeley) 4/9/97 (with name server)"; #else -static char sccsid[] = "@(#)domain.c 8.64 (Berkeley) 10/30/96 (without name server)"; +static char sccsid[] = "@(#)domain.c 8.67 (Berkeley) 4/9/97 (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, 203)); 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. */ 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; char *mxhosts[MAXMXHOSTS + 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); else return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), 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(); 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; char buf[MAXLINE]; static char hbuf[MAXDNAME]; fname = getenv("HOSTALIASES"); if (fname == NULL || (fp = safefopen(fname, O_RDONLY, 0, SFF_REGONLY)) == 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 */ Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/envelope.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/envelope.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/envelope.c (revision 26986) @@ -1,954 +1,959 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)envelope.c 8.101 (Berkeley) 12/16/96"; +static char sccsid[] = "@(#)envelope.c 8.104 (Berkeley) 6/3/97"; #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(); 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); } } -#ifdef LOG if (LogLevel > 84) - syslog(LOG_DEBUG, "%s: dropenvelope, e_flags=0x%x, OpMode=%c, pid=%d", - id == NULL ? "[NOQUEUE]" : id, + sm_syslog(LOG_DEBUG, id, + "dropenvelope, e_flags=0x%x, OpMode=%c, pid=%d", e->e_flags, OpMode, getpid()); -#endif /* 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)) - syslog(LOG_DEBUG, "dropenvelope: %s: q_flags = %x, paddr = %s", - e->e_id, q->q_flags, q->q_paddr); + 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, NULL, &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(); printf("\n===== Dropping [dq]f%s... queueit=%d, e_flags=", e->e_id, queueit); printenvflags(e); } xunlink(queuename(e, 'd')); xunlink(queuename(e, 'q')); -#ifdef LOG if (LogLevel > 10) - syslog(LOG_INFO, "%s: done", id); -#endif + 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); 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; #endif /* TTYNAME */ extern char *ttyname(); extern void settime(); /* ** 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 */ -# ifdef LOG 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; } - syslog(LOG_NOTICE, + sm_syslog(LOG_NOTICE, e->e_id, "setsender: %s: invalid or unparseable, received from %s", shortenstring(from, 83), p); } -# endif /* LOG */ 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 = 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 */ -# ifdef LOG if (LogLevel > 2) - syslog(LOG_NOTICE, "cannot prescan from (%s)", + sm_syslog(LOG_NOTICE, e->e_id, + "cannot prescan from (%s)", shortenstring(from, 203)); -# endif 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(); /* 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"); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/err.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/err.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/err.c (revision 26986) @@ -1,739 +1,768 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)err.c 8.52 (Berkeley) 12/1/96"; +static char sccsid[] = "@(#)err.c 8.62 (Berkeley) 6/5/97"; #endif /* not lint */ # include "sendmail.h" # include /* ** SYSERR -- Print error message. ** -** Prints an error message via printf to the diagnostic -** output. If LOG is defined, it logs it also. +** 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; -#ifdef LOG char *uname; struct passwd *pw; char ubuf[80]; -#endif 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) { 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); } -# ifdef LOG pw = sm_getpwuid(getuid()); if (pw != NULL) uname = pw->pw_name; else { uname = ubuf; snprintf(ubuf, sizeof ubuf, "UID%d", getuid()); } if (LogLevel > 0) - syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %.900s", - CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, + sm_syslog(panic ? LOG_ALERT : LOG_CRIT, CurEnv->e_id, + "SYSERR(%s): %.900s", uname, &MsgBuf[4]); -# endif /* LOG */ 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) + if (QuickAbort || (OnlyOneError && !HoldErrs)) 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); -# ifdef LOG if (LogLevel > 3 && LogUsrErrs) - syslog(LOG_NOTICE, "%s: %.900s", - CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, + sm_syslog(LOG_NOTICE, CurEnv->e_id, + "%.900s", &MsgBuf[4]); -# endif /* LOG */ - if (QuickAbort) + if (QuickAbort || (OnlyOneError && !HoldErrs)) 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. */ /*VARARGS2*/ 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. */ /*VARARGS2*/ 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->e_xfp != NULL && strchr("45", msg[0]) != NULL) fprintf(CurEnv->e_xfp, "%s\n", msg); -#ifdef LOG if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) - syslog(LOG_INFO, "--> %s%s", msg, holdmsg ? " (held)" : ""); -#endif + 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 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 (feof(InChannel) || ferror(InChannel) || strncmp(msg, "221", 3) == 0) return; /* can't call syserr, 'cause we are using MsgBuf */ HoldErrs = TRUE; -#ifdef LOG if (LogLevel > 0) - syslog(LOG_CRIT, - "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", - CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, + sm_syslog(LOG_CRIT, CurEnv->e_id, + "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s", CurHostName == NULL ? "NO-HOST" : CurHostName, shortenstring(msg, 203), errstring(errno)); -#endif } /* ** 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); /* signal the error */ Errors++; 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; - char *meb; int l; int spaceleft = sizeof MsgBuf; /* output the reply code */ if (isdigit(fmt[0]) && isdigit(fmt[1]) && 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') { (void) snprintf(eb, spaceleft, "%s... ", shortenstring(to, 203)); spaceleft -= strlen(eb); while (*eb != '\0') *eb++ &= 0177; } - meb = eb; - /* 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]; -# ifndef ERRLIST_PREDEFINED +# 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 snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[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, 203)); 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, 203)); return (buf); case ECONNREFUSED: if (CurHostName == NULL) break; (void) snprintf(buf, sizeof buf, "Connection refused by %s", shortenstring(CurHostName, 203)); return (buf); # endif - case EOPENTIMEOUT: - return "Timeout on file open"; - # 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, 203)); 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 } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/headers.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/headers.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/headers.c (revision 26986) @@ -1,1499 +1,1556 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)headers.c 8.103 (Berkeley) 12/11/96"; +static char sccsid[] = "@(#)headers.c 8.110 (Berkeley) 6/14/97"; #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; - struct hdrinfo *hi; 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); + 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; - /* see if it is a known type */ - for (hi = HdrInfo; hi->hi_field != NULL; hi++) +#if _FFR_HEADER_RSCHECK + /* check to see if it represents a ruleset call */ + if (def) { - if (strcasecmp(hi->hi_field, fname) == 0) - break; + 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; + } } +#endif + /* 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 (hi->hi_field == NULL) - printf("no header match\n"); + if (s == NULL) + printf("no header flags match\n"); else - printf("header match, hi_flags=%x\n", hi->hi_flags); + printf("header match, flags=%x, ruleset=%s\n", + hi->hi_flags, 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 (bitset(H_EOH, hi->hi_flags)) - return (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); + 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; 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); + 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; - register struct hdrinfo *hi; + STAB *s; HDR **hp; /* find info struct */ - for (hi = HdrInfo; hi->hi_field != NULL; hi++) - { - if (strcasecmp(field, hi->hi_field) == 0) - break; - } + 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 = hi->hi_flags | H_DEFAULT; + 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) 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(); 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))) { int saveflags = e->e_flags; (void) sendtolist(h->h_value, NULLADDR, &e->e_sendqueue, 0, e); /* delete fatal errors generated by this address */ if (!GrabTo && !bitset(EF_FATALERRS, saveflags)) e->e_flags &= ~EF_FATALERRS; } /* 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. */ -# ifdef LOG if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) logsender(e, msgid); -# endif /* LOG */ 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; { -# ifdef LOG 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); - syslog(LOG_INFO, "%s: %.850s, relay=%.100s", - e->e_id, sbuf, name); + sm_syslog(LOG_INFO, e->e_id, + "%.850s, relay=%.100s", + sbuf, name); # else /* short syslog buffer */ - syslog(LOG_INFO, "%s: from=%s", - e->e_id, e->e_from.q_paddr == NULL ? "" : - shortenstring(e->e_from.q_paddr, 83)); - syslog(LOG_INFO, "%s: size=%ld, class=%ld, pri=%ld, nrcpts=%d", - e->e_id, e->e_msgsize, e->e_class, - e->e_msgpriority, e->e_nrcpts); + 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) - syslog(LOG_INFO, "%s: msgid=%s", - e->e_id, shortenstring(mbuf, 83)); + sm_syslog(LOG_INFO, e->e_id, + "msgid=%s", + shortenstring(mbuf, 83)); sbp = sbuf; - snprintf(sbp, SPACELEFT(sbuf, sbp), "%s:", e->e_id); - sbp += strlen(sbp); + *sbp = '\0'; if (e->e_bodytype != NULL) { - snprintf(sbp, SPACELEFT(sbuf, sbp), " bodytype=%.20s,", e->e_bodytype); + 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); + snprintf(sbp, SPACELEFT(sbuf, sbp), "proto=%.20s, ", p); sbp += strlen(sbp); } - syslog(LOG_INFO, "%.400s relay=%.100s", sbuf, name); + sm_syslog(LOG_INFO, e->e_id, + "%.400srelay=%.100s", sbuf, name); # endif -# 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 - 6]; + 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); mci->mci_flags |= MCIF_INHEADER; for (h = hdr; h != NULL; h = h->h_link) { register char *p = h->h_value; extern bool bitintersect(); 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 +#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 == NULL || *p == '\0') + 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 = 0; -#ifdef _FFR_7BITHDRS +#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 (sizeof obuf - (obp - obuf) < l) l = sizeof obuf - (obp - obuf); snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v); - putxline(obuf, mci, putflags); + 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, mci, putflags); + 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 = 0; 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); -#ifdef _FFR_7BITHDRS +#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(); 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; } /* 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, mci, putflags); + 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, mci, putflags); + 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; } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/ldap_map.h =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/ldap_map.h (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/ldap_map.h (revision 26986) @@ -1,60 +1,62 @@ /* ** 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.2 (Berkeley) 5/22/96 +** @(#)ldap_map.h 8.4 (Berkeley) 6/3/97 */ #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; + 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 */ Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/macro.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/macro.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/macro.c (revision 26986) @@ -1,457 +1,457 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)macro.c 8.17 (Berkeley) 5/13/96"; +static char sccsid[] = "@(#)macro.c 8.18 (Berkeley) 2/1/97"; #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[BUFSIZ]; 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+ ** $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); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/mailq.1 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/mailq.1 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/mailq.1 (revision 26986) @@ -1,88 +1,89 @@ +.\" Copyright (c) 1983, 1997 Eric P. Allman .\" Copyright (c) 1985, 1990, 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. .\" -.\" @(#)mailq.1 8.4 (Berkeley) 2/22/94 +.\" @(#)mailq.1 8.5 (Berkeley) 2/1/97 .\" -.Dd February 22, 1994 +.Dd February 1, 1997 .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 . Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/main.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/main.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/main.c (revision 26986) @@ -1,2445 +1,2484 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint static char copyright[] = "@(#) 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.230 (Berkeley) 1/17/97"; +static char sccsid[] = "@(#)main.c 8.246 (Berkeley) 6/11/97"; #endif /* not lint */ #define _DEFINE #include "sendmail.h" #include #if NAMED_BIND #include #endif # ifdef lint char edata, end; # endif /* lint */ /* ** 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 tables read in from /usr/lib/sendmail.cf -** (read by readcf.c). Some more static configuration info, -** including some code that you may want to tailor for your -** installation, is in conf.c. You may also want to touch -** daemon.c (if you have some other IPC mechanism), acct.c -** (to change your accounting), names.c (to adjust the name -** server mechanism). +** 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 - present). +** InReference, Inc. (8/95 - 1/97). ** 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 */ +#ifdef NGROUPS_MAX +GIDSET_T InitialGidSet[NGROUPS_MAX]; +#endif + static void obsolete(); 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 7 /* 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; typedef int (*fnptr)(); 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 stat stb; struct hostent *hp; bool nullserver = FALSE; char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ static char rnamebuf[MAXNAME]; /* holds RealUserName */ char *emptyenviron[1]; extern int DtableSize; extern int optind; extern int opterr; extern char *optarg; extern char **environ; extern time_t convtime(); extern SIGFUNC_DECL intsig __P((int)); extern struct hostent *myhostname(); extern char *getauthinfo(); extern char *getcfname(); 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)); extern void drop_privileges __P((void)); /* ** 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); #ifdef SIGUSR1 /* arrange to dump state on user-1 signal */ setsignal(SIGUSR1, sigusr1); #endif /* 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. */ i = open("/dev/null", O_RDWR, 0); if (fstat(STDIN_FILENO, &stb) < 0 && errno != EOPNOTSUPP) (void) dup2(i, STDIN_FILENO); if (fstat(STDOUT_FILENO, &stb) < 0 && errno != EOPNOTSUPP) (void) dup2(i, STDOUT_FILENO); if (fstat(STDERR_FILENO, &stb) < 0 && errno != EOPNOTSUPP) (void) dup2(i, STDERR_FILENO); if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) (void) close(i); i = DtableSize; while (--i > 0) { if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) (void) close(i); } errno = 0; -#ifdef LOG +#if LOG # ifdef LOG_MAIL openlog("sendmail", LOG_PID, LOG_MAIL); # else openlog("sendmail", LOG_PID); # endif #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) */ drop_privileges(); /* 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; /* 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; /* initialize for setproctitle */ initsetproctitle(argc, argv, envp); /* clear sendmail's environment */ ExternalEnviron = environ; emptyenviron[0] = NULL; environ = emptyenviron; /* 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; # 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) { register int i; 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 } - /* probe interfaces and locate any additional names */ - load_if_names(); - /* current time */ define('b', arpadate((char *) NULL), CurEnv); /* ** 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; endpwent(); (void) setgid(RealGid); (void) setuid(RealUid); 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': QueueLimitId = newstr(&optarg[1]); break; case 'R': QueueLimitRecipient = newstr(&optarg[1]); break; case 'S': QueueLimitSender = newstr(&optarg[1]); 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 */ endpwent(); setgid(RealGid); setuid(RealUid); 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); /* avoid denial-of-service attacks */ resetlimits(); if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) { /* drop privileges -- daemon mode done after socket/bind */ drop_privileges(); } /* ** Find our real host name for future logging. */ p = getauthinfo(STDIN_FILENO); 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; /* 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(); + /* 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_PURGESTAT) && RealUid != 0) { -#ifdef LOG if (LogLevel > 1) - syslog(LOG_ALERT, "user %d attempted to %s", + sm_syslog(LOG_ALERT, NOQID, + "user %d attempted to %s", RealUid, OpMode == MD_DAEMON ? "run daemon" : "purge host status"); -#endif 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; + Verbose = 2; + CurEnv->e_errormode = EM_PRINT; 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 */ -#ifdef LOG if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/') - syslog(LOG_WARNING, "daemon invoked without full pathname; kill -1 won't work"); -#endif + 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 = TRUE; + Verbose = 2; + CurEnv->e_errormode = EM_PRINT; /* 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; } /* full names can't have newlines */ if (FullName != NULL && strchr(FullName, '\n') != NULL) FullName = newstr(denlstring(FullName, TRUE, TRUE)); /* 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); } 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 (OpMode == MD_TEST) - /* nothing -- just avoid further if clauses */ ; - else if (QueueDir == NULL) + if (QueueDir == NULL) { - syserr("QueueDirectory (Q) option must be set"); - ExitStat = EX_CONFIG; + if (OpMode != MD_TEST) + { + syserr("QueueDirectory (Q) option must be set"); + ExitStat = EX_CONFIG; + } } - else if (chdir(QueueDir) < 0) + else { - syserr("cannot chdir(%s)", QueueDir); - ExitStat = EX_CONFIG; + /* 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: nullserver = FALSE; /* fall through... */ 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 = TRUE; + 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) printf("> "); (void) fflush(stdout); if (fgets(buf, sizeof buf, stdin) == NULL) finis(); p = strchr(buf, '\n'); if (p != NULL) *p = '\0'; if (!Verbose) 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) unsetenv("HOSTALIASES"); (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 bool 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(0); /* 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"); -#ifdef LOG - syslog(LOG_INFO, "starting daemon (%s): %s", Version, dtype + 1); -#endif + 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 nullserver = getrequests(CurEnv); /* drop privileges */ drop_privileges(); /* at this point we are in a child: reset state */ (void) newenvelope(CurEnv, CurEnv); /* ** Get authentication data */ p = getauthinfo(fileno(InChannel)); 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((bool, ENVELOPE *)); /* ** Save some macros for check_* rulesets. */ 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); smtp(nullserver, CurEnv); } # endif /* SMTP */ clearenvelope(CurEnv, FALSE); if (OpMode == MD_VERIFY) { CurEnv->e_sendmode = SM_VERIFY; CurEnv->e_errormode = EM_PRINT; PostMasterCopy = NULL; HoldErrs = FALSE; } 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, FALSE, NULL, CurEnv); + 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; /* ** 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; - collect(InChannel, FALSE, FALSE, NULL, CurEnv); + CurEnv->e_flags &= ~EF_FATALERRS; + collect(InChannel, FALSE, NULL, CurEnv); + + /* bail out if there were fatal errors in collect */ + if (OpMode != MD_VERIFY && bitset(EF_FATALERRS, CurEnv->e_flags)) + { + CurEnv->e_flags |= EF_CLRQUEUE; + 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(); sendall(CurEnv, SM_DEFAULT); /* ** All done. ** Don't send return error message if in VERIFY mode. */ finis(); /*NOTREACHED*/ return -1; } 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(); 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 */ -# ifdef LOG + forceexit: if (LogLevel > 78) - syslog(LOG_DEBUG, "finis, pid=%d", getpid()); -# endif /* LOG */ + 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. */ SIGFUNC_DECL intsig(sig) int sig; { -#ifdef LOG if (LogLevel > 79) - syslog(LOG_DEBUG, "%s: interrupt", - CurEnv->e_id == NULL ? "[NOQUEUE]" : CurEnv->e_id); -#endif + 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; } -#ifdef LOG if (LogLevel > 93) - syslog(LOG_DEBUG, "%s: disconnect level %d", - e->e_id == NULL ? "[NOQUEUE]" : e->e_id, droplev); -#endif + 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 = FALSE; + Verbose = 0; DisConnected = TRUE; /* all input from /dev/null */ if (InChannel != stdin) { (void) fclose(InChannel); InChannel = stdin; } (void) freopen("/dev/null", "r", stdin); /* 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); else fd = fileno(e->e_xfp); (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 -# ifdef LOG if (LogLevel > 71) - syslog(LOG_DEBUG, "in background, pid=%d", getpid()); -# endif /* LOG */ + 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(); 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); -#ifdef LOG if (LogLevel > 3) - syslog(LOG_INFO, "%s: Authentication-Warning: %.400s", - e->e_id == NULL ? "[NOQUEUE]" : e->e_id, buf); -#endif + 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; { -#ifdef LOG register char *j = macvalue('j', CurEnv); int rs; - syslog(LOG_DEBUG, "--- dumping state on %s: $j = %s ---", + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "--- dumping state on %s: $j = %s ---", when, j == NULL ? "" : j); if (j != NULL) { if (!wordinclass(j, 'w')) - syslog(LOG_DEBUG, "*** $j not in $=w ***"); + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "*** $j not in $=w ***"); } - syslog(LOG_DEBUG, "CurChildren = %d", CurChildren); - syslog(LOG_DEBUG, "--- open file descriptors: ---"); + sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren); + sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---"); printopenfds(TRUE); - syslog(LOG_DEBUG, "--- connection cache: ---"); + 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); - syslog(LOG_DEBUG, + sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- ruleset debug_dumpstate returns stat %d, pv: ---", stat); for (pvp = pv; *pvp != NULL; pvp++) - syslog(LOG_DEBUG, "%s", *pvp); + sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp); } - syslog(LOG_DEBUG, "--- end of state dump ---"); -#endif + sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---"); } SIGFUNC_DECL sigusr1(sig) int sig; { dumpstate("user signal"); return SIGFUNC_RETURN; } SIGFUNC_DECL sighup(sig) int sig; { if (SaveArgv[0][0] != '/') { -#ifdef LOG if (LogLevel > 3) - syslog(LOG_INFO, "could not restart: need full path"); -#endif + sm_syslog(LOG_INFO, NOQID, "could not restart: need full path"); exit(EX_OSFILE); } -#ifdef LOG if (LogLevel > 3) - syslog(LOG_INFO, "restarting %s on signal", SaveArgv[0]); -#endif + sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]); + alarm(0); releasesignal(SIGHUP); if (setgid(RealGid) < 0 || setuid(RealUid) < 0) { -#ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, "could not set[ug]id(%d, %d): %m", + sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m", RealUid, RealGid); -#endif exit(EX_OSERR); } execv(SaveArgv[0], (ARGV_T) SaveArgv); -#ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, "could not exec %s: %m", SaveArgv[0]); -#endif + 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: ** none. ** ** Returns: ** none. */ void drop_privileges() { #ifdef NGROUPS_MAX /* reset group permissions; these can be set later */ GIDSET_T emptygidset[NGROUPS_MAX]; emptygidset[0] = RunAsGid == 0 ? getegid() : RunAsGid; (void) setgroups(1, emptygidset); #endif if (RunAsGid != 0) (void) setgid(RunAsGid); if (RunAsUid != 0) (void) setuid(RunAsUid); } /* ** 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; if (*p == '\0') { printf("Usage: /map mapname key\n"); return; } for (q = p; *q != '\0' && !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); p = (*map->s_map.map_class->map_lookup) (&map->s_map, q, NULL, &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; int 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); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/makesendmail =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/makesendmail (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/makesendmail (revision 26986) @@ -1,320 +1,331 @@ #!/bin/sh -# Copyright (c) 1993, 1996 Eric P. Allman +# Copyright (c) 1993, 1996-1997 Eric P. Allman # Copyright (c) 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. # -# @(#)makesendmail 8.42 (Berkeley) 11/10/96 +# @(#)makesendmail 8.45 (Berkeley) 4/12/97 # # # A quick-and-dirty script to compile sendmail in the presence of # multiple architectures and Makefiles. # if [ "x${1-""}" = "x-m" ] then # show Makefile name only mflag=1 else mflag="" fi # # 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 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;; 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 [ "$os" = "$node" -a "$rel" = 4.0 -a "$arch" = "3360,3430-R" ] +then + # AT&T/NCR Machines also set uname -s == uname -n + if [ -d /usr/sadm/sysadm/add-ons/WIN-TCP ] + then + os=NCR.MP-RAS.2.x + else + os=NCR.MP-RAS.3.x + fi +fi case $os in DYNIX-ptx) os=PTX;; Paragon*) os=Paragon;; HP-UX) rel=`echo $rel | sed -e 's/^[^.]*\.0*//'`;; AIX) rel=`uname -v` if [ "$rel" = "2" ] then arch="" fi;; 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;; 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" # 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 else # no existing obj directory -- try to create one if Makefile found obj=obj.$os.$rel.$arch$sfx if [ -r Makefiles/Makefile.$os.$rel.$arch$sfx ]; then makefile=Makefile.$os.$rel.$arch$sfx elif [ -r Makefiles/Makefile.$os.$rel.$arch ]; then makefile=Makefile.$os.$rel.$arch elif [ -r Makefiles/Makefile.$os.$rroot.$arch$sfx ]; then makefile=Makefile.$os.$rroot.$arch$sfx elif [ -r Makefiles/Makefile.$os.$rroot.$arch ]; then makefile=Makefile.$os.$rroot.$arch elif [ -r Makefiles/Makefile.$os.$rbase.x.$arch$sfx ]; then makefile=Makefile.$os.$rbase.x.$arch$sfx elif [ -r Makefiles/Makefile.$os.$rbase.x.$arch ]; then makefile=Makefile.$os.$rbase.x.$arch elif [ -r Makefiles/Makefile.$os.$rel$sfx ]; then makefile=Makefile.$os.$rel$sfx elif [ -r Makefiles/Makefile.$os.$rel ]; then makefile=Makefile.$os.$rel elif [ -r Makefiles/Makefile.$os.$rroot$sfx ]; then makefile=Makefile.$os.$rroot$sfx elif [ -r Makefiles/Makefile.$os.$rroot ]; then makefile=Makefile.$os.$rroot elif [ -r Makefiles/Makefile.$os.$rbase.x$sfx ]; then makefile=Makefile.$os.$rbase.x$sfx elif [ -r Makefiles/Makefile.$os.$rbase.x ]; then makefile=Makefile.$os.$rbase.x elif [ -r Makefiles/Makefile.$os.$arch$sfx ]; then makefile=Makefile.$os.$arch$sfx elif [ -r Makefiles/Makefile.$os.$arch ]; then makefile=Makefile.$os.$arch elif [ -r Makefiles/Makefile.$rel.$arch$sfx ]; then makefile=Makefile.$rel.$arch$sfx elif [ -r Makefiles/Makefile.$rel.$arch ]; then makefile=Makefile.$rel.$arch elif [ -r Makefiles/Makefile.$rroot.$arch$sfx ]; then makefile=Makefile.$rroot.$arch$sfx elif [ -r Makefiles/Makefile.$rroot.$arch ]; then makefile=Makefile.$rroot.$arch elif [ -r Makefiles/Makefile.$rbase.x.$arch$sfx ]; then makefile=Makefile.$rbase.x.$arch$sfx elif [ -r Makefiles/Makefile.$rbase.x.$arch ]; then makefile=Makefile.$rbase.x.$arch elif [ -r Makefiles/Makefile.$os$sfx ]; then makefile=Makefile.$os$sfx elif [ -r Makefiles/Makefile.$os ]; then makefile=Makefile.$os elif [ -r Makefiles/Makefile.$arch$sfx ]; then makefile=Makefile.$arch$sfx elif [ -r Makefiles/Makefile.$arch ]; then makefile=Makefile.$arch elif [ -r Makefiles/Makefile.$rel$sfx ]; then makefile=Makefile.$rel$sfx elif [ -r Makefiles/Makefile.$rel ]; then makefile=Makefile.$rel elif [ -r Makefiles/Makefile.$rel$sfx ]; then makefile=Makefile.$rel$sfx else echo "Cannot determine how to support $arch.$os.$rel" exit 1 fi if [ "$mflag" ] then echo "Will run in virgin $obj using $makefile" exit 0 fi echo "Creating $obj using $makefile" mkdir $obj (cd $obj; ln -s ../*.[ch158] ../sendmail.hf .; ln -s ../Makefiles/$makefile Makefile) 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" exit 1 fi case $makefile in ../Makefiles/*) makefile=`echo $makefile | sed 's/...Makefiles.//'` echo "Will run in existing $obj using $makefile" ;; *) echo "Will run in existing $obj using custom $makefile" ;; esac exit 0 fi echo "Making in $obj" cd $obj if [ $# = 0 ] then exec ${MAKE-make} else exec ${MAKE-make} "$@" fi Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/map.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/map.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/map.c (revision 26986) @@ -1,3978 +1,4161 @@ /* - * Copyright (c) 1992, 1995, 1996 Eric P. Allman. + * Copyright (c) 1992, 1995-1997 Eric P. Allman. * Copyright (c) 1992, 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. */ #ifndef lint -static char sccsid[] = "@(#)map.c 8.147 (Berkeley) 1/17/97"; +static char sccsid[] = "@(#)map.c 8.168 (Berkeley) 6/14/97"; #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 READ_ME 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 #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 +#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_LEAVELOCKED +# if O_SHLOCK +# define O_LEAVELOCKED O_SHLOCK +# else +# define O_LEAVELOCKED 0x1000 +# endif +#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 '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_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 char *s; + register const char *s; int slen; char **av; { register char *bp; register char c; char **avp; register char *ap; int i; int len; static int buflen = -1; static char *buf = NULL; if (tTd(39, 1)) { printf("map_rewrite(%.*s), av =", 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) */ i = len = slen; if (av != NULL) { - bp = s; - for (i = slen; --i >= 0 && (c = *bp++) != 0; ) + const char *sp = s; + + for (i = slen; --i >= 0 && (c = *sp++) != 0; ) { if (c != '%') continue; if (--i < 0) break; - c = *bp++; + 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 NDBM: opens the database. ** if ~NDBM: reads the aliases into the symbol table. */ void initmaps(rebuild, e) bool rebuild; register ENVELOPE *e; { extern void map_init(); #if XDEBUG checkfd012("entering initmaps"); #endif CurEnv = e; if (rebuild) { stabapply(map_init, 1); stabapply(map_init, 2); } else { stabapply(map_init, 0); } #if XDEBUG checkfd012("exiting initmaps"); #endif } void map_init(s, rebuild) register STAB *s; int rebuild; { 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, rebuild); if (rebuild == (bitset(MF_ALIAS, map->map_mflags) && bitset(MCF_REBUILDABLE, map->map_class->map_cflags) ? 1 : 2)) { if (tTd(38, 3)) printf("\twrong pass\n"); 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 (rebuild == 2) { rebuildaliases(map, FALSE); } else { 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; - int l; extern char *get_column __P((char *, int, char, char *, int)); cbuf[0] = '\0'; - l = cbuflen; 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 /* ** DBM_MAP_OPEN -- DBM-style map open */ bool ndbm_map_open(map, mode) MAP *map; int mode; { register DBM *dbm; struct stat st; int fd; + 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|SFF_CREAT; + if (mode == O_RDWR) + { + sff |= SFF_NOLINK; + smode = S_IWRITE; + } + else + { + sff |= SFF_NOWLINK; + } + if (FatalWritableDirs) + sff |= SFF_SAFEDIRPATH; + if ((ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName, + sff, smode, &std)) != 0 || + (ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName, + sff, smode, &stp)) != 0) + { + /* cannot open this map */ + if (tTd(38, 2)) + printf("\tunsafe map file: %d\n", ret); + if (!bitset(MF_OPTIONAL, map->map_mflags)) + syserr("dbm map \"%s\": unsafe map file %s", + map->map_mname, map->map_file); + return FALSE; + } + if (std.st_mode == ST_MODE_NOFILE) + mode |= O_EXCL; + #if LOCK_ON_OPEN if (mode == O_RDONLY) mode |= O_SHLOCK; else mode |= O_CREAT|O_TRUNC|O_EXLOCK; #else - if (mode == O_RDWR) + 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_CREAT|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; - char dirfile[MAXNAME + 1]; - char pagfile[MAXNAME + 1]; - snprintf(dirfile, sizeof dirfile, "%s.dir", map->map_file); - snprintf(pagfile, sizeof pagfile, "%s.pag", map->map_file); - dirfd = open(dirfile, mode|O_CREAT, DBMMODE); - pagfd = open(pagfile, mode|O_CREAT, DBMMODE); + dirfd = safeopen(dirfile, mode|O_CREAT, DBMMODE, + SFF_NOLINK|SFF_CREAT|SFF_OPENASROOT); + pagfd = safeopen(pagfile, mode|O_CREAT, DBMMODE, + SFF_NOLINK|SFF_CREAT|SFF_OPENASROOT); if (dirfd < 0 || pagfd < 0) { syserr("ndbm_map_open: cannot create database %s", map->map_file); close(dirfd); close(pagfd); return FALSE; } - if (!lockfile(dirfd, map->map_file, ".dir", LOCK_EX)) - syserr("ndbm_map_open: cannot lock %s.dir", - map->map_file); if (ftruncate(dirfd, (off_t) 0) < 0) syserr("ndbm_map_open: cannot truncate %s.dir", map->map_file); if (ftruncate(pagfd, (off_t) 0) < 0) syserr("ndbm_map_open: cannot truncate %s.pag", map->map_file); /* have to save the lock for the duration (bletch) */ map->map_lockfd = dirfd; close(pagfd); # endif } #endif /* open the database */ dbm = dbm_open(map->map_file, mode, DBMMODE); if (dbm == NULL) { if (bitset(MF_ALIAS, map->map_mflags) && aliaswait(map, ".pag", FALSE)) return TRUE; if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("Cannot open DBM database %s", map->map_file); +#if !LOCK_ON_OPEN && !NOFTRUNCATE + if (map->map_lockfd >= 0) + close(map->map_lockfd); +#endif return FALSE; } + if (filechanged(dirfile, dbm_dirfno(dbm), &std, sff) || + filechanged(pagfile, dbm_pagfno(dbm), &stp, sff)) + { + syserr("ndbm_map_open(%s): file changed after open", + map->map_file); + dbm_close(dbm); +#if !LOCK_ON_OPEN && !NOFTRUNCATE + if (map->map_lockfd >= 0) + close(map->map_lockfd); +#endif + return FALSE; + } + map->map_db1 = (void *) dbm; fd = dbm_dirfno((DBM *) map->map_db1); if (mode == O_RDONLY) { #if LOCK_ON_OPEN if (fd >= 0) (void) lockfile(fd, 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 (fstat(dbm_dirfno((DBM *) map->map_db1), &st) >= 0) map->map_mtime = st.st_mtime; return TRUE; } /* ** DBM_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]; 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; } 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); 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); } /* ** DBM_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)) usrerr("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, 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, %x)\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. */ -extern bool db_map_open __P((MAP *, int, DBTYPE, const void *)); +extern bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *)); /* these should be K line arguments */ #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 bool bt_map_open(map, mode) MAP *map; int mode; { BTREEINFO btinfo; if (tTd(38, 2)) printf("bt_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); bzero(&btinfo, sizeof btinfo); btinfo.cachesize = DB_CACHE_SIZE; - return db_map_open(map, mode, DB_BTREE, &btinfo); + return db_map_open(map, mode, "btree", DB_BTREE, &btinfo); } bool hash_map_open(map, mode) MAP *map; int mode; { HASHINFO hinfo; if (tTd(38, 2)) printf("hash_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); bzero(&hinfo, sizeof hinfo); hinfo.nelem = DB_HASH_NELEM; hinfo.cachesize = DB_CACHE_SIZE; - return db_map_open(map, mode, DB_HASH, &hinfo); + return db_map_open(map, mode, "hash", DB_HASH, &hinfo); } bool -db_map_open(map, mode, dbtype, openinfo) +db_map_open(map, mode, mapclassname, dbtype, openinfo) MAP *map; int mode; + char *mapclassname; DBTYPE dbtype; const void *openinfo; { DB *db; int i; int omode; + int smode = S_IREAD; int fd; + int sff; int saveerrno; + bool leavelocked = bitset(O_LEAVELOCKED, mode); 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"); - map->map_lockfd = -1; + mode &= O_ACCMODE; omode = mode; + sff = SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; + if (mode == O_RDWR) + { + sff |= SFF_NOLINK; + smode = S_IWRITE; + } + else + { + sff |= SFF_NOWLINK; + } + if (FatalWritableDirs) + sff |= SFF_SAFEDIRPATH; + if ((i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, + sff, smode, &st)) != 0) + { + /* cannot open this map */ + if (tTd(38, 2)) + printf("\tunsafe map file: %d\n", i); + if (!bitset(MF_OPTIONAL, map->map_mflags)) + syserr("%s map \"%s\": unsafe map file %s", + mapclassname, map->map_mname, map->map_file); + return FALSE; + } + if (st.st_mode == ST_MODE_NOFILE) + omode |= O_EXCL; + + map->map_lockfd = -1; + #if LOCK_ON_OPEN if (mode == O_RDWR) omode |= O_CREAT|O_TRUNC|O_EXLOCK; # if !OLD_NEWDB else omode |= O_SHLOCK; # endif #else if (mode == O_RDWR) omode |= O_CREAT; /* ** 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); close(fd); return FALSE; } if (!lockfile(fd, map->map_file, ".db", mode == O_RDONLY ? LOCK_SH : LOCK_EX)) syserr("db_map_open: cannot lock %s", buf); if (mode == O_RDWR) omode |= O_TRUNC; #endif db = dbopen(buf, omode, DBMMODE, dbtype, openinfo); saveerrno = errno; #if !LOCK_ON_OPEN - if (mode == O_RDWR) + if (leavelocked || 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; errno = saveerrno; if (!bitset(MF_OPTIONAL, map->map_mflags)) - syserr("Cannot open DB database %s", map->map_file); + syserr("Cannot open %s database %s", + mapclassname, map->map_file); +#if !LOCK_ON_OPEN + if (map->map_lockfd >= 0) + (void) close(map->map_lockfd); +#endif return FALSE; } + if (filechanged(buf, db->fd(db), &st, sff)) + { + syserr("db_map_open(%s): file changed after open", buf); + db->close(db); +#if !LOCK_ON_OPEN + if (map->map_lockfd >= 0) + close(map->map_lockfd); +#endif + return FALSE; + } + if (mode == O_RDWR) map->map_mflags |= MF_LOCKED; #if !OLD_NEWDB fd = db->fd(db); # if LOCK_ON_OPEN - if (fd >= 0 && mode == O_RDONLY) + if (fd >= 0 && mode == O_RDONLY && !leavelocked) { (void) lockfile(fd, map->map_file, ".db", LOCK_UN); } # endif #endif /* try to make sure that at least the database header is on disk */ if (mode == O_RDWR) #if OLD_NEWDB (void) db->sync(db); #else (void) db->sync(db, 0); if (fd >= 0 && fstat(fd, &st) >= 0) map->map_mtime = st.st_mtime; #endif map->map_db2 = (void *) 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 st; int saveerrno; int fd; + struct stat stbuf; char keybuf[MAXNAME + 1]; if (tTd(38, 20)) printf("db_map_lookup(%s, %s)\n", map->map_mname, name); 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); #if !OLD_NEWDB fd = db->fd(db); if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) - (void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH); + (void) lockfile(fd, map->map_file, ".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); + omode |= O_LEAVELOCKED; + 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; + fd = db->fd(db); + } + 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; + } + } #endif + st = 1; if (bitset(MF_TRY0NULL, map->map_mflags)) { st = db->get(db, &key, &val, 0); if (st == 0) map->map_mflags &= ~MF_TRY1NULL; } if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags)) { key.size++; st = db->get(db, &key, &val, 0); if (st == 0) map->map_mflags &= ~MF_TRY0NULL; } saveerrno = errno; #if !OLD_NEWDB if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) (void) lockfile(fd, map->map_file, ".db", LOCK_UN); #endif 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]; 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++; } stat = db->put(db, &key, &data, R_NOOVERWRITE); if (stat > 0) { if (!bitset(MF_APPEND, map->map_mflags)) usrerr("050 Warning: duplicate alias name %s", lhs); else { static char *buf = NULL; static int bufsiz = 0; DBT old; old.data = db_map_lookup(map, key.data, 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", data.data, 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); } } stat = db->put(db, &key, &data, 0); } 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, "@", "@"); } if (db->close(db) != 0) syserr("readaliases: db close failure"); #if !LOCK_ON_OPEN if (map->map_lockfd >= 0) (void) close(map->map_lockfd); #endif } #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 */ 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 = ENODEV; 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(); 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(""); - - /* check to see if this map actually exists */ - if (PARTIAL_NAME(map->map_file)) + 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); - else - strcpy(qbuf, map->map_file); - + } + 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 = EBADR; 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 = EBADR; 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 = EBADR; 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 = EBADR; 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 = EBADR; 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 *vp; + char *p; auto int vsize; - int buflen; - char search_key[MAXNAME + 1]; + 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; } } - buflen = strlen(name); - if (buflen > sizeof search_key - 1) - buflen = sizeof search_key - 1; - bcopy(name, search_key, buflen); - search_key[buflen] = '\0'; + /* + ** 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) - syslog(LOG_WARNING, + 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); } - vp = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno)); + p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno)); /* set the length of the result */ - if (vp == NULL) - vp = ""; - vsize = strlen(vp); + if (p == NULL) + p = ""; + vsize = strlen(p); if (tTd(38, 20)) printf("nisplus_map_lookup(%s), found %s\n", - name, vp); + name, p); if (bitset(MF_MATCHONLY, map->map_mflags)) str = map_rewrite(map, name, strlen(name), NULL); else - str = map_rewrite(map, vp, vsize, av); + 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) { -#ifdef LOG if (LogLevel > 10) - syslog(LOG_WARNING, + sm_syslog(LOG_WARNING, CurEnv->e_id, "nisplus_getcanonname: lookup error, expected 1 entry, got %d", count); -#endif /* 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. */ bool ldap_map_start(map) MAP *map; { LDAP_MAP_STRUCT *lmap; LDAP *ld; 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); 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; } 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->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->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_UNAVAILABLE; 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); + 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); -# ifdef LOG if (LogLevel > 9) - syslog(LOG_INFO, "%s: ldap %.100s => %s", - CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id, + sm_syslog(LOG_INFO, CurEnv->e_id, + "ldap %.100s => %s", name, vp); -# endif 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; 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; /* 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 = p; + 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)) { syserr("Scope must be [base|one|sub] not %s in map %s", p, map->map_mname); 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); 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_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 != NULL) - lmap->attr = newstr(ldap_map_dequote(lmap->attr)); + 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 */ /* ** HESIOD Modules */ #ifdef HESIOD #include 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; } 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; } 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); hp = hes_resolve(np, map->map_file); if (np != nbuf) free(np); } else { hp = hes_resolve(name, map->map_file); } 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; } 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 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", '\0'); if (vptr == NULL) { *statp = EX_NOHOST; return FALSE; } 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; { - struct stat sbuf; + 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 = ENODEV; 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; } - /* check to see if this map actually accessable */ - if (access(map->map_file, R_OK) <0) + + sff = SFF_ROOTOK|SFF_REGONLY|SFF_NOWLINK; + if (FatalWritableDirs) + 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("text_map_open(%s, %s): cannot access: %s\n", - map->map_mname, map->map_file, errstring(errno)); + printf("\tunsafe map file: %d\n", i); if (!bitset(MF_OPTIONAL, map->map_mflags)) - syserr("text map \"%s\": cannot access file %s", + syserr("text map \"%s\": unsafe map file %s", map->map_mname, map->map_file); return FALSE; } - /* check to see if this map actually exist */ - if (stat(map->map_file, &sbuf) <0) - { - syserr("text_map_open(%s, %s): cannot stat", - map->map_mname, map->map_file); - return FALSE; - } - - if (!S_ISREG(sbuf.st_mode)) - { - syserr("text map \"%s\": %s is not a regular file", - map->map_mname, map->map_file); - return FALSE; - } - if (map->map_keycolnm == NULL) map->map_keycolno = 0; else { if (!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 (!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; - char search_key[MAXNAME + 1]; - char linebuf[MAXLINE]; FILE *f; - char buf[MAXNAME + 1]; 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 = fopen(map->map_file, "r"); + 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]; - extern char *get_column __P((char *, int, char, char *, int)); 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 */ 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 = ENODEV; return FALSE; } - af = fopen(map->map_file, "r"); + sff = SFF_ROOTOK|SFF_REGONLY|SFF_NOWLINK; + if (FatalWritableDirs) + 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", map->map_file); #else if (mode != O_RDONLY) usrerr("Cannot rebuild aliases: no database format defined"); #endif return stab_map_open(map, mode); } /* ** 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. */ 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 */ bool null_map_open(map, mode) MAP *map; int mode; { return TRUE; } 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; } 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, }; Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/mci.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/mci.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/mci.c (revision 26986) @@ -1,1270 +1,1286 @@ /* - * Copyright (c) 1995, 1996 Eric P. Allman + * Copyright (c) 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)mci.c 8.54 (Berkeley) 12/1/96"; +static char sccsid[] = "@(#)mci.c 8.62 (Berkeley) 5/29/97"; #endif /* not lint */ #include "sendmail.h" #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 void 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 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, mcislot - MciCache); -#ifdef LOG if (tTd(91, 100)) - syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%.100s) in slot %d", - CurEnv->e_id ? CurEnv->e_id : "NOQUEUE", + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "mci_cache: caching %x (%.100s) in slot %d", mci, mci->mci_host, mcislot - MciCache); -#endif *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; 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, mcislot - MciCache, doquit); -#ifdef LOG if (tTd(91, 100)) - syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%.100s) from slot %d (%d)", - CurEnv->e_id ? CurEnv->e_id : "NOQUEUE", + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "mci_uncache: uncaching %x (%.100s) from slot %d (%d)", mci, mci->mci_host, mcislot - MciCache, doquit); -#endif #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; mci_load_persistent(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) */ int socksize = sizeof CurHostAddr; (void) getpeername(fileno(mci->mci_in), (struct sockaddr *) &CurHostAddr, &socksize); } # 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: -#ifdef LOG if (logit) - syslog(LOG_DEBUG, "%.1000s", buf); + sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf); else -#endif 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; } - if ((mci->mci_statfile = fopen(fname, "r+")) == NULL) - mci->mci_statfile = fopen(fname, "w"); + mci->mci_statfile = safefopen(fname, O_RDWR|O_CREAT, FileMode, + SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|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: ** none. */ void mci_load_persistent(mci) MCI *mci; { int saveErrno = errno; FILE *fp; char fname[MAXPATHLEN+1]; if (mci == NULL) { if (tTd(56, 1)) printf("mci_load_persistent: NULL mci\n"); return; } if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL) return; 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 = fopen(fname, "r"); + fp = safefopen(fname, O_RDONLY, FileMode, + SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY); 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; (void) mci_read_persistent(fp, mci); FileName = NULL; fclose(fp); cleanup: errno = saveErrno; return; } /* ** 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: syserr("Unknown host status line \"%s\"", 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", 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 = fopen(pathname, "r+"); + fp = safefopen(pathname, O_RDWR, FileMode, + SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY); 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') + 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] == '.' || elem[-1] == ']')) + while (elem > t_host && + (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']'))) *--elem = '\0'; /* check for what will be the final length of the path */ len = strlen(HostStatDir) + 2; for (p = (char *) host; *p != '\0'; p++) { - if (*p == '|' || *p != '.') + if (*p == '|' || *p == '.') len++; len++; + if (p[0] == '.' && p[1] == '.') + return -1; } if (len > pathlen) 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 (isupper(ch)) ch = tolower(ch); if (ch == '|') *p++ = '|'; /* | -> || */ else 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); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/mime.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/mime.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/mime.c (revision 26986) @@ -1,1185 +1,1187 @@ /* - * Copyright (c) 1994, 1996 Eric P. Allman + * Copyright (c) 1994, 1996-1997 Eric P. Allman * Copyright (c) 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ # include "sendmail.h" # include #ifndef lint -static char sccsid[] = "@(#)mime.c 8.54 (Berkeley) 1/14/97"; +static char sccsid[] = "@(#)mime.c 8.59 (Berkeley) 5/6/97"; #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) + if (i >= argc || argv[i].value == NULL) { - syserr("mime8to7: Content-Type: \"%s\": missing boundary", - p); + 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); while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { bt = mimeboundary(buf, boundaries); if (bt != MBT_NOTSEP) break; - putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); + 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, FALSE, &hdr, e); + 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, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); + 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, FALSE, &hdr, e); + 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); 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++; - *fbufp = '\0'; - putline((char *) fbuf, mci); + 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++; - *fbufp = '\0'; - putline((char *) fbuf, mci); + 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++; - *fbufp = '\0'; - putline((char *) fbuf, mci); + 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; - putline((char *) fbuf, mci); + 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'; - putline((char *) fbuf, mci); + 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') /* ignore it */ { if (state == 0) return 0; } else { if ((c2 = *infile++) == '\0') break; c1 = HEXCHAR(c1); c2 = HEXCHAR(c2); if (++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 */ Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/newaliases.1 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/newaliases.1 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/newaliases.1 (revision 26986) @@ -1,68 +1,69 @@ +.\" Copyright (c) 1983, 1997 Eric P. Allman .\" Copyright (c) 1985, 1990, 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. .\" -.\" @(#)newaliases.1 8.4 (Berkeley) 2/22/94 +.\" @(#)newaliases.1 8.5 (Berkeley) 2/1/97 .\" -.Dd February 22, 1994 +.Dd February 1, 1997 .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 . Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/parseaddr.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/parseaddr.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/parseaddr.c (revision 26986) @@ -1,2403 +1,2437 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)parseaddr.c 8.115 (Berkeley) 11/24/96"; +static char sccsid[] = "@(#)parseaddr.c 8.128 (Berkeley) 6/14/97"; #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(); extern bool invalidaddr(); 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]) { syserr("553 prescan: too many tokens"); goto returnnull; } if (q - tok > MAXNAME) { syserr("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 */ 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) { char buf[MAXLINE]; 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 (strcasecmp(rp, ap)) + 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 { /* vanilla replacement */ if (avp >= &npvp[MAXATOM]) { toolong: syserr("554 rewrite: expansion too long"); return EX_DATAERR; } if ((*rp & 0377) != MACRODEXPAND) *avp++ = rp; else { *avp = macvalue(rp[1], e); if (tTd(21, 2)) printf("rewrite: RHS $&%s => \"%s\"\n", macname(rp[1]), *avp == NULL ? "(NULL)" : *avp); if (*avp != NULL) avp++; } } } *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 */ for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) if (avp >= &npvp[MAXATOM]) goto toolong; break; } /* ** 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) { 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[300]; + char mbuf[320]; snprintf(mbuf, sizeof mbuf, "%.80s map: lookup (%s): deferred", map->s_name, shortenstring(key, 203)); e->e_message = newstr(mbuf); } } 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 errormailer; 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; /* 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 }, { 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(); 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) 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) { a->q_flags |= QQUEUEUP; a->q_status = "4.4.3"; return; } if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) return; /* if non-null, mailer destination specified -- has it changed? */ a1 = buildaddr(pvp, NULL, 0, e); if (a1 == NULL || sameaddr(a, a1)) + { + 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. */ 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; char **pvp; int rsno; auto ADDRESS a1; bool saveQuickAbort = QuickAbort; + bool saveSuprErrs = SuprErrs; + bool saveOnlyOneError = OnlyOneError; 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; + OnlyOneError = QuickAbort = FALSE; pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL); + SuprErrs = saveSuprErrs; if (pvp == NULL) { + syserr("rscheck: cannot prescan input: \"%s\"", + shortenstring(buf, 203)); 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) - return EX_OK; + { + rstat = EX_OK; + goto finis; + } /* got an error -- process it */ saveexitstat = ExitStat; - QuickAbort = FALSE; (void) buildaddr(pvp, &a1, 0, e); - QuickAbort = saveQuickAbort; rstat = ExitStat; ExitStat = saveexitstat; -#ifdef LOG if (LogLevel >= 4) { if (p2 == NULL) - syslog(LOG_NOTICE, "Ruleset %s (%s) rejection: %s", + sm_syslog(LOG_NOTICE, e->e_id, + "Ruleset %s (%s) rejection: %s", rwset, p1, MsgBuf); else - syslog(LOG_NOTICE, "Ruleset %s (%s, %s) rejection: %s", + sm_syslog(LOG_NOTICE, e->e_id, + "Ruleset %s (%s, %s) rejection: %s", rwset, p1, p2, MsgBuf); } -#endif - if (QuickAbort) - longjmp(TopFrame, 2); - - /* clean up */ finis: + /* clean up */ + QuickAbort = saveQuickAbort; + OnlyOneError = saveOnlyOneError; setstat(rstat); if (buf != buf0) free(buf); + + if (rstat != EX_OK && (QuickAbort || (OnlyOneError && !HoldErrs))) + longjmp(TopFrame, 2); return rstat; } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/queue.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/queue.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/queue.c (revision 26986) @@ -1,2269 +1,2341 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ # include "sendmail.h" #ifndef lint #if QUEUE -static char sccsid[] = "@(#)queue.c 8.153 (Berkeley) 1/14/97 (with queueing)"; +static char sccsid[] = "@(#)queue.c 8.169 (Berkeley) 6/14/97 (with queueing)"; #else -static char sccsid[] = "@(#)queue.c 8.153 (Berkeley) 1/14/97 (without queueing)"; +static char sccsid[] = "@(#)queue.c 8.169 (Berkeley) 6/14/97 (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 buf[MAXLINE], tf[MAXLINE]; + 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; -#ifdef LOG if (LogLevel > 0 && (i % 32) == 0) - syslog(LOG_ALERT, "queueup: cannot create %s, uid=%d: %s", + sm_syslog(LOG_ALERT, e->e_id, + "queueup: cannot create %s, uid=%d: %s", tf, geteuid(), errstring(errno)); -#endif } else { if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB)) break; -#ifdef LOG else if (LogLevel > 0 && (i % 32) == 0) - syslog(LOG_ALERT, "queueup: cannot lock %s: %s", + sm_syslog(LOG_ALERT, e->e_id, + "queueup: cannot lock %s: %s", tf, errstring(errno)); -#endif 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(); 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[20]; + 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", e->e_ctime); /* output last delivery time */ fprintf(tfp, "K%ld\n", 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) fprintf(tfp, "I%d/%d/%ld\n", major(e->e_dfdev), minor(e->e_dfdev), 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)) - syslog(LOG_DEBUG, - "dropenvelope: %s: q_flags = %x, paddr = %s", - e->e_id, q->q_flags, q->q_paddr); + 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(); /* 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 || 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; -# ifdef LOG /* save log info */ if (LogLevel > 79) - syslog(LOG_DEBUG, "%s: queueup, qf=%s", e->e_id, qf); -# endif /* LOG */ + 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; char *paddr; register ADDRESS *q; uid_t uid; gid_t gid; static ADDRESS *lastctladdr; 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; paddr = denlstring(a->q_paddr, TRUE, FALSE); if (uid == 0 || uname == NULL || uname[0] == '\0') fprintf(tfp, "C:%s\n", paddr); else fprintf(tfp, "C%s:%ld:%ld:%s\n", uname, (long) uid, (long) gid, paddr); } /* ** 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((bool)); + extern void runqueueevent __P((void)); extern void drop_privileges __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 (CurrentLA >= QueueLA) + if (shouldqueue(WkRecipFact, current_la_time)) { char *msg = "Skipping queue run -- load average too high"; if (verbose) message("458 %s\n", msg); -#ifdef LOG if (LogLevel > 8) - syslog(LOG_INFO, "runqueue: %s", msg); -#endif + sm_syslog(LOG_INFO, NOQID, + "runqueue: %s", + msg); if (forkflag && QueueIntvl != 0) - (void) setevent(QueueIntvl, runqueueevent, TRUE); + (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)); #ifdef SIGCHLD extern SIGFUNC_DECL reapchild __P((int)); blocksignal(SIGCHLD); (void) setsignal(SIGCHLD, reapchild); #endif 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); -#ifdef LOG if (LogLevel > 8) - syslog(LOG_INFO, "runqueue: %s: %s", msg, err); -#endif + sm_syslog(LOG_INFO, NOQID, + "runqueue: %s: %s", + msg, err); if (QueueIntvl != 0) - (void) setevent(QueueIntvl, runqueueevent, TRUE); + (void) setevent(QueueIntvl, runqueueevent, 0); (void) releasesignal(SIGCHLD); return FALSE; } if (pid != 0) { /* parent -- pick up intermediate zombie */ #ifndef SIGCHLD (void) waitfor(pid); #else (void) blocksignal(SIGALRM); proc_list_add(pid); (void) releasesignal(SIGALRM); releasesignal(SIGCHLD); #endif /* SIGCHLD */ if (QueueIntvl != 0) - (void) setevent(QueueIntvl, runqueueevent, TRUE); + (void) setevent(QueueIntvl, runqueueevent, 0); return TRUE; } /* child -- double fork and clean up signals */ proc_list_clear(); #ifndef SIGCHLD if (fork() != 0) exit(EX_OK); #else /* SIGCHLD */ releasesignal(SIGCHLD); (void) setsignal(SIGCHLD, SIG_DFL); #endif /* SIGCHLD */ (void) setsignal(SIGHUP, intsig); } setproctitle("running queue: %s", QueueDir); -# ifdef LOG if (LogLevel > 69) - syslog(LOG_DEBUG, "runqueue %s, pid=%d, forkflag=%d", + sm_syslog(LOG_DEBUG, NOQID, + "runqueue %s, pid=%d, forkflag=%d", QueueDir, getpid(), forkflag); -# endif /* LOG */ /* ** 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) drop_privileges(); /* ** 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); + OnlyOneError = 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(); 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(forkflag) - bool forkflag; +runqueueevent() { - (void) runqueue(forkflag, FALSE); + 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; DIR *f; register int i; int wn = -1; int wc; if (tTd(41, 1)) { printf("orderq:\n"); if (QueueLimitId != NULL) printf("\tQueueLimitId = %s\n", QueueLimitId); if (QueueLimitSender != NULL) printf("\tQueueLimitSender = %s\n", QueueLimitSender); if (QueueLimitRecipient != NULL) printf("\tQueueLimitRecipient = %s\n", QueueLimitRecipient); } /* 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; register char *p; char lbuf[MAXNAME + 1]; extern bool strcontainedin(); 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; + if (QueueLimitId != NULL && !strcontainedin(QueueLimitId, d->d_name)) 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); -# ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, "orderq: bogus qf name %s", + sm_syslog(LOG_ALERT, NOQID, + "orderq: bogus qf name %s", d->d_name); -# endif 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) { -# ifdef LOG if (wn == MaxQueueRun && LogLevel > 0) - syslog(LOG_ALERT, "WorkList for %s maxed out at %d", - QueueDir, MaxQueueRun); -# endif + 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 qfver = 0; + char *p; + int c; extern bool strcontainedin(); + 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]; if (strcontainedin(QueueLimitRecipient, p)) i &= ~NEED_R; break; case 'S': if (QueueLimitSender != NULL && strcontainedin(QueueLimitSender, &lbuf[1])) i &= ~NEED_S; break; case 'K': if ((curtime() - (time_t) atol(&lbuf[1])) < 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 workcmpf1(); extern 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) { 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 && strcmp(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 workcmpf3(); /* ** Simple sort based on submission time only. */ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3); } else { extern 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; 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; -# ifdef LOG if (LogLevel > 1) { - syslog(LOG_NOTICE, "grew WorkList for %s to %d", - QueueDir, WorkListSize); + sm_syslog(LOG_NOTICE, NOQID, + "grew WorkList for %s to %d", + QueueDir, WorkListSize); } } else if (LogLevel > 0) { - syslog(LOG_ALERT, "FAILED to grow WorkList for %s to %d", - QueueDir, newsize); -# endif + 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; /* 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 = strcmp(a->w_host, b->w_host))) 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; /* 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 = strcmp(a->w_host, b->w_host))) 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(); 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); -# ifdef LOG if (LogLevel > 76) - syslog(LOG_DEBUG, "%s: dowork, pid=%d", e->e_id, - getpid()); -# endif /* LOG */ + 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)) printf("readqf(%s) failed\n", e->e_id); 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[20]; + 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); -# ifdef LOG if (LogLevel > 19) - syslog(LOG_DEBUG, "%s: locked", e->e_id); -# endif /* LOG */ + 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)) { -# ifdef LOG if (LogLevel > 0) { - syslog(LOG_ALERT, "%s: bogus queue file, uid=%d, mode=%o", - e->e_id, st.st_uid, st.st_mode); + sm_syslog(LOG_ALERT, e->e_id, + "bogus queue file, uid=%d, mode=%o", + st.st_uid, st.st_mode); } -# endif /* LOG */ 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 -- just remove it */ - qf[0] = 'd'; - (void) unlink(qf); - qf[0] = 'q'; - (void) unlink(qf); + /* 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 && - (curtime() - e->e_dtime) < MinQueueAge) + curtime() < e->e_dtime + MinQueueAge) { char *howlong = pintvl(curtime() - e->e_dtime, TRUE); extern void unlockqueue(); if (Verbose || tTd(40, 8)) printf("%s: too young (%s)\n", e->e_id, howlong); -#ifdef LOG if (LogLevel > 19) - syslog(LOG_DEBUG, "%s: too young (%s)", - e->e_id, howlong); -#endif + 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, 203)); 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; - GIDSET_T gidset[NGROUPS_MAX]; + extern GIDSET_T InitialGidSet[NGROUPS_MAX]; # endif if (stat(QueueDir, &st) < 0) { syserr("Cannot stat %s", QueueDir); return; } # ifdef NGROUPS_MAX - n = getgroups(NGROUPS_MAX, gidset); + n = NGROUPS_MAX; while (--n >= 0) { - if (gidset[n] == st.st_gid) + 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. */ 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[20]; + 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; 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); } if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB)) { e->e_lockfp = fdopen(i, "w"); break; } /* 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); } -# ifdef LOG if (LogLevel > 93) - syslog(LOG_DEBUG, "%s: assigned id", e->e_id); -# endif /* LOG */ + 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 */ -# ifdef LOG if (LogLevel > 87) - syslog(LOG_DEBUG, "%s: unlock", e->e_id); -# endif /* LOG */ + 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[40]; + char buf[MAXQFNAME]; if (e == NULL || e->e_id == NULL) return; if (strlen(e->e_id) > (SIZE_T) sizeof buf - 4) return; strcpy(buf, queuename(e, 'q')); p = queuename(e, 'Q'); if (rename(buf, p) < 0) syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); -#ifdef LOG else if (LogLevel > 0) - syslog(LOG_ALERT, "Losing %s: %s", buf, why); -#endif + sm_syslog(LOG_ALERT, e->e_id, + "Losing %s: %s", buf, why); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/readcf.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/readcf.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/readcf.c (revision 26986) @@ -1,2725 +1,2766 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)readcf.c 8.184 (Berkeley) 1/14/97"; +static char sccsid[] = "@(#)readcf.c 8.196 (Berkeley) 5/29/97"; #endif /* not lint */ # include "sendmail.h" # include #if NAMED_BIND # include #endif /* ** READCF -- read control file. ** ** This routine reads the control 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 -- control 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; char buf[MAXLINE]; register char *p; extern char **copyplist(); struct stat statb; char exbuf[MAXLINE]; char pvpbuf[MAXLINE + MAXATOM]; static char *null_list[1] = { NULL }; 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; - cf = fopen(cfname, "r"); + cf = safefopen(cfname, O_RDONLY, 0444, SFF_OPENASROOT|SFF_NOLOCK); 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); -#ifdef LOG if (LogLevel > 0) - syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", + sm_syslog(LOG_CRIT, NOQID, + "%s: WARNING: dangerous write permissions", FileName); -#endif } #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 CANONNET: botch = "$#"; 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 #ifdef SUN_EXTENSIONS case 'L': /* lookup macro */ case 'G': /* lookup class */ /* reserved for Sun -- NIS+ database lookup */ goto badline; #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 control 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; + sff = SFF_REGONLY|SFF_NOWLINK; if (safe) sff |= SFF_OPENASROOT; 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[MAXNAME+1]; + 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(); 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 (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 }, -#ifdef _FFR_DSN_RRT +#if _FFR_DSN_RRT_OPTION #define O_DSN_RRT 0x9e { "RrtImpliesDsn", O_DSN_RRT, FALSE }, #endif -#ifdef _FFR_PIDFILE_OPT +#if _FFR_PIDFILE_OPTION #define O_PIDFILE 0x9f { "PidFile", O_PIDFILE, FALSE }, #endif +#if _FFR_WRITABLE_DIRECTORIES_ARE_FATAL_OPTION +#define O_WDAF 0xa0 + { "WritableDirectoriesAreFatal", O_WDAF, FALSE }, +#endif +#if _FFR_CHOWN_IS_ALWAYS_SAFE_OPTION +#define O_CIAS 0xa1 + { "ChownIsAlwaysSafe", O_CIAS, FALSE }, +#endif +#if _FFR_DONT_PROBE_INTERFACES_OPTION +#define O_DPI 0xa2 + { "DontProbeInterfaces", O_DPI, FALSE }, +#endif +#if _FFR_MAXRCPT_OPTION +#define O_MAXRCPT 0xa3 + { "MaxRecipientPerMessage", O_MAXRCPT, 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; auto char *ep; char buf[50]; extern bool atobool(); extern time_t convtime(); 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)) { - bool oldVerbose = Verbose; + int oldVerbose = Verbose; - Verbose = TRUE; + 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)"); if (RealUid != geteuid()) { if (tTd(37, 1)) printf("(Resetting uid)"); endpwent(); (void) setgid(RealGid); (void) setuid(RealUid); } } } 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); } 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); 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; } } #ifdef UID_MAX if (DefUid > UID_MAX) { syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored", DefUid, UID_MAX); } #endif setdefuser(); /* 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); + 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 */ UnsafeGroupWrites = atobool(val); 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)) 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 (*p == '\0') + RunAsUserName = newstr(val); RunAsUid = pw->pw_uid; RunAsGid = pw->pw_gid; } } if (*p == '\0') break; if (isascii(*p) && isdigit(*p)) - DefGid = atoi(p); + RunAsGid = atoi(p); else { register struct group *gr; gr = getgrnam(p); if (gr == NULL) syserr("readcf: option RunAsUser: unknown group %s", p); else RunAsGid = gr->gr_gid; } break; -#ifdef _FFR_DSN_RRT +#if _FFR_DSN_RRT_OPTION case O_DSN_RRT: - RrtImpliesDsn = atobool(p); + RrtImpliesDsn = atobool(val); break; #endif -#ifdef _FFR_PIDFILE_OPT +#if _FFR_PIDFILE_OPTION case O_PIDFILE: free(PidFile); - PidFile = newstr(p); + PidFile = newstr(val); + break; +#endif + +#if _FFR_WRITABLE_DIRECTORIES_ARE_FATAL_OPTION + case O_WDAF: + FatalWritableDirs = atobool(val); + break; +#endif + +#if _FFR_CHOWN_IS_ALWAYS_SAFE_OPTION + case O_CIAS: + ChownIsAlwaysSafe = atobool(val); + break; +#endif + +#if _FFR_DONT_PROBE_INTERFACES_OPTION + case O_DPI: + DontProbeInterfaces = atobool(val); + break; +#endif + +#if _FFR_MAXRCPT_OPTION + case O_MAXRCPT: + MaxRcptPerMsg = atoi(val); 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 || !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 (!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(); 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", TimeOuts.to_connect); printf(" initial = %ld\n", TimeOuts.to_initial); printf(" helo = %ld\n", TimeOuts.to_helo); printf(" mail = %ld\n", TimeOuts.to_mail); printf(" rcpt = %ld\n", TimeOuts.to_rcpt); printf(" datainit = %ld\n", TimeOuts.to_datainit); printf(" datablock = %ld\n", TimeOuts.to_datablock); printf(" datafinal = %ld\n", TimeOuts.to_datafinal); printf(" rset = %ld\n", TimeOuts.to_rset); printf(" quit = %ld\n", TimeOuts.to_quit); printf(" nextcommand = %ld\n", TimeOuts.to_nextcommand); printf(" miscshort = %ld\n", TimeOuts.to_miscshort); printf(" ident = %ld\n", TimeOuts.to_ident); printf(" fileopen = %ld\n", 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(); 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); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/recipient.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/recipient.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/recipient.c (revision 26986) @@ -1,1360 +1,1378 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)recipient.c 8.118 (Berkeley) 12/1/96"; +static char sccsid[] = "@(#)recipient.c 8.130 (Berkeley) 5/29/97"; #endif /* not lint */ # include "sendmail.h" /* ** 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 */ - bool firstone; /* set on first address sent */ 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 = ','; - firstone = TRUE; 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(); /* 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; - firstone = FALSE; } /* 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_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"; - usrerr("550 User %s@%s doesn't have a valid shell for mailing to programs", - a->q_alias->q_ruser, MyHostName); + 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"; 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)) { 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)) { -#ifdef LOG if (LogLevel > 2) - syslog(LOG_ERR, "%s: include %s: transient error: %s", - e->e_id == NULL ? "NOQUEUE" : e->e_id, + sm_syslog(LOG_ERR, e->e_id, + "include %s: transient error: %s", shortenstring(a->q_user, 203), errstring(ret)); -#endif a->q_flags |= QQUEUEUP; a->q_flags &= ~QDONTSEND; usrerr("451 Cannot open %s: %s", shortenstring(a->q_user, 203), 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, 203), errstring(ret)); } } } else if (m == FileMailer) { extern bool writable(); /* 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"; - usrerr("550 User %s@%s doesn't have a valid shell for mailing to files", - a->q_alias->q_ruser, MyHostName); + 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"; 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(); 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"); -# ifdef LOG if (LogLevel > 8) - syslog(LOG_INFO, "%s: deferred: udbexpand: %s", - e->e_id == NULL ? "NOQUEUE" : e->e_id, + sm_syslog(LOG_INFO, e->e_id, + "deferred: udbexpand: %s", errstring(errno)); -# endif 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); } } 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 (ctladdr != NULL && geteuid() == 0) + 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; + flags |= SFF_NOLINK; 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(); 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; 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; + bool safedir = FALSE; struct stat st; char buf[MAXLINE]; -#ifdef _POSIX_CHOWN_RESTRICTED -# if _POSIX_CHOWN_RESTRICTED == -1 -# define safechown FALSE -# else -# define safechown TRUE -# endif -#else -# ifdef _PC_CHOWN_RESTRICTED - bool safechown; -# else -# ifdef BSD -# define safechown TRUE -# else -# define safechown FALSE -# endif -# endif -#endif extern bool chownsafe(); 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) initgroups(uname, gid); if (gid != 0) (void) setgid(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 else sfflags |= SFF_NOPATHCHECK; } } #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 = EOPENTIMEOUT; + rval = E_SM_OPENTIMEOUT; goto resetuid; } if (TimeOuts.to_fileopen > 0) ev = setevent(TimeOuts.to_fileopen, includetimeout, 0); else ev = NULL; - /* the input file must be marked safe */ - rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD, NULL); + /* check for writable parent directory */ + p = strrchr(fname, '/'); + if (p != NULL) + { + *p = '\0'; + if (safedirpath(fname, uid, gid, uname, sfflags|SFF_SAFEDIRPATH) == 0) + { + /* in safe directory: relax chown & link rules */ + safedir = TRUE; + sfflags |= SFF_NOPATHCHECK; + } + *p = '/'; + } + + /* allow links only in unwritable directories */ + if (!safedir) + 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 + else if ((fp = fopen(fname, "r")) == NULL) { - fp = fopen(fname, "r"); - if (fp == NULL) - { - rval = errno; - if (tTd(27, 4)) - printf("include: open: %s\n", errstring(rval)); - } + rval = errno; + if (tTd(27, 4)) + printf("include: open: %s\n", errstring(rval)); } + else if (filechanged(fname, fileno(fp), &st, sfflags)) + { + 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 == EOPENTIMEOUT) + 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; } -#ifndef safechown - safechown = chownsafe(fileno(fp)); -#endif + /* 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)) { -#ifdef LOG if (LogLevel >= 12) - syslog(LOG_INFO, "%s: user %s has bad shell %s, marked %s", + sm_syslog(LOG_INFO, e->e_id, + "%s: user %s has bad shell %s, marked %s", shortenstring(fname, 203), pw->pw_name, sh, safechown ? "bogus" : "unsafe"); -#endif 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). ** Also, we don't check for writable ** directories in the path. We've got to leave ** something for the local sysad to do. */ if (bitset(S_IWOTH | (UnsafeGroupWrites ? S_IWGRP : 0), st.st_mode)) { -#ifdef LOG if (LogLevel >= 12) - syslog(LOG_INFO, "%s: %s writable %s file, marked unsafe", + sm_syslog(LOG_INFO, e->e_id, + "%s: %s writable %s file, marked unsafe", shortenstring(fname, 203), bitset(S_IWOTH, st.st_mode) ? "world" : "group", forwarding ? "forward" : ":include:"); -#endif 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); -#ifdef LOG if (forwarding && LogLevel > 9) - syslog(LOG_INFO, "%s: forward %.200s => %s", - e->e_id == NULL ? "NOQUEUE" : e->e_id, + sm_syslog(LOG_INFO, e->e_id, + "forward %.200s => %s", oldto, shortenstring(buf, 203)); -#endif 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; } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/safefile.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/safefile.c (nonexistent) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/safefile.c (revision 26986) @@ -0,0 +1,686 @@ +/* + * Copyright (c) 1983, 1995-1997 Eric P. Allman + * Copyright (c) 1988, 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)safefile.c 8.12 (Berkeley) 6/14/97"; +#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 + +#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 + +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; + + /* 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) +#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 + { + /* 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) + md >>= 6; + 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 %o]\tE_SM_NOSLINK\n", + 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 %o]\tE_SM_REGONLY\n", + st->st_mode); + return E_SM_REGONLY; + } + if (bitset(SFF_NOWFILES, flags) && + bitset(S_IWOTH | (UnsafeGroupWrites ? S_IWGRP : 0), st->st_mode)) + { + if (tTd(44, 4)) + printf("\t[write bits %o]\tE_SM_%cWFILE\n", + st->st_mode, + bitset(S_IWOTH, st->st_mode) ? 'W' : 'G'); + return bitset(S_IWOTH, st->st_mode) ? E_SM_WWFILE : E_SM_GWFILE; + } + 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 %o]\tE_SM_ISEXEC]\n", + 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", + 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) + { + 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 || + !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; + 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); + + 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(S_IWGRP|S_IWOTH, stbuf.st_mode)) + { + if (tTd(44, 4)) + printf("\t[dir %s] mode %o\n", + fn, 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: writable directory %s", fn); + } + if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)) + { + if (bitset(S_IXOTH, stbuf.st_mode)) + continue; + ret = EACCES; + break; + } + 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; + 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) + omode |= O_EXCL; + + fd = dfopen(fn, omode, cmode, sff); + if (fd < 0) + return fd; + if (filechanged(fn, fd, &stb, sff)) + { + 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) + return NULL; + fp = fdopen(fd, fmode); + if (fp != NULL) + return fp; + (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. +** sff -- safe file flags. +** +** Returns: +** TRUE -- if a problem was detected. +** FALSE -- if this file is still the same. +*/ + +bool +filechanged(fn, fd, stb, sff) + char *fn; + int fd; + struct stat *stb; + int sff; +{ + 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 || + 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); + printf(" ino = %ld/%ld\n", + (long) stb->st_ino, (long) sta.st_ino); + 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; +} Property changes on: vendor/sendmail/dist-old/usr.sbin/sendmail/src/safefile.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/savemail.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/savemail.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/savemail.c (revision 26986) @@ -1,1488 +1,1491 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)savemail.c 8.103 (Berkeley) 1/18/97"; +static char sccsid[] = "@(#)savemail.c 8.110 (Berkeley) 4/7/97"; #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 */ # ifndef _PATH_VARTMP # define _PATH_VARTMP "/usr/tmp/" # endif 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(); typedef int (*fnptr)(); extern bool writable(); 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)) { (void) sendtolist(e->e_from.q_paddr, 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, NULL, &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_NOSLINK|SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; + flags = SFF_NOLINK|SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; e->e_to = buf; if (mailfile(buf, NULL, flags, e) == EX_OK) { - bool oldverb = Verbose; + int oldverb = Verbose; - Verbose = TRUE; + 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') { state = ESM_PANIC; break; } snprintf(buf, sizeof buf, "%sdead.letter", _PATH_VARTMP); - flags = SFF_NOSLINK|SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT; + flags = SFF_NOLINK|SFF_CREAT|SFF_REGONLY|SFF_OPENASROOT|SFF_MUSTOWN; if (!writable(buf, NULL, flags) || (fp = safefopen(buf, O_WRONLY|O_CREAT|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; 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 { - bool oldverb = Verbose; + int oldverb = Verbose; - Verbose = TRUE; + Verbose = 1; message("Saved message in %s", buf); Verbose = oldverb; -#ifdef LOG if (LogLevel > 3) - syslog(LOG_NOTICE, "Saved message in %s", buf); -#endif + sm_syslog(LOG_NOTICE, e->e_id, + "Saved message in %s", + buf); state = ESM_DONE; } (void) xfclose(fp, "savemail", buf); 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; 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) { extern bool pruneroute __P((char *)); if (bitset(QBADADDR, q->q_flags)) continue; q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); q->q_flags |= QPINGONFAILURE; if (!DontPruneRoutes && pruneroute(q->q_paddr)) { register ADDRESS *p; parseaddr(q->q_paddr, q, RF_COPYPARSE, '\0', NULL, e); for (p = returnq; p != NULL; p = p->q_next) { if (p != q && sameaddr(p, q)) q->q_flags |= QDONTSEND; } } if (!bitset(QDONTSEND, q->q_flags)) ee->e_nrcpts++; if (q->q_alias == NULL) addheader("To", q->q_paddr, &ee->e_header); } -# ifdef LOG 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"; - syslog(LOG_INFO, "%s: %s: %s: %s", - e->e_id, ee->e_id, p, shortenstring(msg, 203)); + sm_syslog(LOG_INFO, e->e_id, + "%s: %s: %s", + ee->e_id, p, shortenstring(msg, 203)); } -# endif 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); /* 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(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; 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 == '/') { - xfile = fopen(ErrMsgFile, "r"); + xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, + SFF_ROOTOK|SFF_REGONLY); if (xfile != NULL) { while (fgets(buf, sizeof buf, xfile) != NULL) { +#if _FFR_BUG_FIX + translate_dollars(buf); +#endif 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, 203)); putline(buf, mci); if (q->q_alias != NULL) { snprintf(buf, sizeof buf, " (expanded from: %s)", shortenstring(q->q_alias->q_paddr, 203)); 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, 203)); putline(buf, mci); if (q->q_alias != NULL) { snprintf(buf, sizeof buf, " (expanded from: %s)", shortenstring(q->q_alias->q_paddr, 203)); 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, 203), p); putline(buf, mci); if (q->q_alias != NULL) { snprintf(buf, sizeof buf, " (expanded from: %s)", shortenstring(q->q_alias->q_paddr, 203)); 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; %.700s@%.100s", p, r->q_user, MyHostName); } else { (void) snprintf(buf, sizeof buf, "Final-Recipient: %s; %.800s", p, r->q_user); } 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.3"; + 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; } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/sendmail.8 =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/sendmail.8 (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/sendmail.8 (revision 26986) @@ -1,605 +1,606 @@ +.\" Copyright (c) 1983, 1997 Eric P. Allman .\" Copyright (c) 1988, 1991, 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. .\" -.\" @(#)sendmail.8 8.11 (Berkeley) 1/16/97 +.\" @(#)sendmail.8 8.12 (Berkeley) 2/1/97 .\" -.Dd January 16, 1997 +.Dd February 1, 1997 .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. Any addresses in the argument list will be suppressed, that is, they will .Em not receive copies even if listed in the message header. .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, 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 .It Pa /var/run/sendmail.pid The process id of the daemon .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 . Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/sendmail.h =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/sendmail.h (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/sendmail.h (revision 26986) @@ -1,1405 +1,1442 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. * - * @(#)sendmail.h 8.219 (Berkeley) 1/14/97 + * @(#)sendmail.h 8.236 (Berkeley) 6/5/97 */ /* ** SENDMAIL.H -- Global definitions for sendmail. */ # ifdef _DEFINE # define EXTERN # ifndef lint -static char SmailSccsId[] = "@(#)sendmail.h 8.219 1/14/97"; +static char SmailSccsId[] = "@(#)sendmail.h 8.236 6/5/97"; # 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 + /* 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 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 */ 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 *e, 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 */ 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_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 */ /* 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 *, char *, int, char **)); +extern char *map_rewrite __P((MAP *, const char *, int, 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 */ /* ** 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_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. +** 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_NOWFILES 0x0800 /* disallow world writable files */ -/* flags that are actually specific to safefopen */ +/* flags that are actually specific to safeopen/safefopen/dfopen */ #define SFF_OPENASROOT 0x1000 /* open as root instead of real user */ +#define SFF_NOLOCK 0x2000 /* 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 *, int)); /* ** 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 bool 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 *)); /* ** 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 Verbose; /* set if blow-by-blow desired */ 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 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 */ -#ifdef _FFR_DSN_RRT +EXTERN int MaxRcptPerMsg; /* max recipients per SMTP message */ +EXTERN bool DoQueueRun; /* non-interrupt time queue run needed */ +#if _FFR_DSN_RRT_OPTION EXTERN bool RrtImpliesDsn; /* turn Return-Receipt-To: into DSN */ #endif +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 UnsafeGroupWrites; /* group-writable files are unsafe */ EXTERN bool SingleLineFromHeader; /* force From: header to be one line */ 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 int DeliveryNiceness; /* how nice to be during delivery */ 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 char *QueueLimitRecipient; /* limit queue runs to this recipient */ EXTERN char *QueueLimitSender; /* limit queue runs to this sender */ EXTERN char *QueueLimitId; /* limit queue runs to this id */ EXTERN FILE *TrafficLogFile; /* file in which to log all traffic */ EXTERN char *DoubleBounceAddr; /* where to send double bounces */ +EXTERN bool FatalWritableDirs; /* no writable dirs in map paths */ EXTERN char **ExternalEnviron; /* input environment */ EXTERN char *UserEnviron[MAXUSERENVIRON + 1]; /* saved user environment */ extern int errno; /* ** 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 FILE *dfopen __P((char *, int, int)); extern char *sfgets __P((char *, int, FILE *, time_t, char *)); extern char *queuename __P((ENVELOPE *, int)); extern time_t curtime __P(()); extern bool transienterror __P((int)); extern char *fgetfolded __P((char *, int, FILE *)); extern char *username __P(()); 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 void rebuildaliases __P((MAP *, bool)); extern void readaliases __P((MAP *, FILE *, bool, bool)); extern void finis __P(()); extern void setsender __P((char *, ENVELOPE *, char **, int, bool)); -extern FILE *safefopen __P((char *, int, int, int)); 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 *, MCI *, int)); +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 void putxline __P((char *, MCI *, int)); 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, bool, HDR **, 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 *)); extern void poststats __P((char *)); extern char *arpadate __P((char *)); extern int mailfile __P((char *, ADDRESS *, 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 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 */ #ifdef __STDC__ extern void auth_warning(ENVELOPE *, const char *, ...); extern void syserr(const char *, ...); extern void usrerr(const char *, ...); extern void message(const char *, ...); extern void nmessage(const char *, ...); extern void setproctitle(const char *fmt, ...); +extern void sm_syslog(int, const char *, const char *, ...); #else extern void auth_warning(); extern void syserr(); extern void usrerr(); extern void message(); extern void nmessage(); extern void setproctitle(); +extern void sm_syslog(); #endif #if !HASSNPRINTF # ifdef __STDC__ extern int snprintf(char *, size_t, const char *, ...); extern int vsnprintf(char *, size_t, const char *, va_list); # else extern int snprintf(); extern int vsnprintf(); # endif #endif Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/sendmail.hf =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/sendmail.hf (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/sendmail.hf (revision 26986) @@ -1,113 +1,113 @@ cpyr -cpyr Copyright (c) 1983, 1995, 1996 Eric P. Allman +cpyr Copyright (c) 1983, 1995-1997 Eric P. Allman cpyr Copyright (c) 1988, 1993 cpyr The Regents of the University of California. All rights reserved. cpyr -cpyr @(#)sendmail.hf 8.11 (Berkeley) 9/11/96 +cpyr @(#)sendmail.hf 8.12 (Berkeley) 2/1/97 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. Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/srvrsmtp.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/srvrsmtp.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/srvrsmtp.c (revision 26986) @@ -1,1458 +1,1479 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ # include "sendmail.h" #ifndef lint #if SMTP -static char sccsid[] = "@(#)srvrsmtp.c 8.136 (Berkeley) 1/17/97 (with SMTP)"; +static char sccsid[] = "@(#)srvrsmtp.c 8.146 (Berkeley) 6/11/97 (with SMTP)"; #else -static char sccsid[] = "@(#)srvrsmtp.c 8.136 (Berkeley) 1/17/97 (without SMTP)"; +static char sccsid[] = "@(#)srvrsmtp.c 8.146 (Berkeley) 6/11/97 (without SMTP)"; #endif #endif /* not lint */ # include # if SMTP /* ** SMTP -- run the SMTP protocol. ** ** Parameters: ** none. ** ** 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(); #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) bool 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; 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; + int lognullconnection = TRUE; 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 *)); + 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; setproctitle("server %s startup", CurSmtpClient); -#if defined(LOG) && DAEMON +#if DAEMON if (LogLevel > 11) { /* log connection information */ - syslog(LOG_INFO, "SMTP connect from %.100s (%.100s)", + 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 */ if (setjmp(TopFrame) > 0) { /* if() nesting is necessary for Cray UNICOS */ if (InChild) { QuickAbort = FALSE; SuprErrs = TRUE; finis(); } } 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); -#ifdef LOG if (LogLevel > (gotmail ? 1 : 19)) - syslog(LOG_NOTICE, "lost input channel from %.100s", + sm_syslog(LOG_NOTICE, e->e_id, + "lost input channel from %.100s", CurSmtpClient); -#endif 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); -#ifdef LOG if (LogLevel >= 15) - syslog(LOG_INFO, "<-- %s", inp); -#endif + 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) { switch (c->cmdcode) { case CMDQUIT: case CMDHELO: case CMDEHLO: case CMDNOOP: /* process normally */ break; default: if (++badcommands > MAXBADCOMMANDS) sleep(1); - message("550 Access denied"); + usrerr("550 Access denied"); 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"); + checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO", e); /* check for duplicate HELO/EHLO per RFC 1651 4.2 */ if (gothello) { - message("503 %s Duplicate HELO/EHLO", + usrerr("503 %s Duplicate HELO/EHLO", MyHostName); break; } /* check for valid domain name (re 1123 5.2.5) */ if (*p == '\0' && !AllowBogusHELO) { - message("501 %s requires domain address", + usrerr("501 %s requires domain address", cmdbuf); break; } else { register char *q; 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') { if (!AllowBogusHELO) - message("501 Invalid domain name"); + usrerr("501 Invalid domain name"); else { message("250 %s Invalid domain name, accepting anyway", MyHostName); gothello = TRUE; } break; } } sendinghost = newstr(p); gothello = TRUE; if (c->cmdcode != CMDEHLO) { /* print old message and be done with it */ message("250 %s Hello %s, pleased to meet you", MyHostName, CurSmtpClient); break; } /* print extended message and brag */ message("250-%s Hello %s, pleased to meet you", MyHostName, CurSmtpClient); if (!bitset(PRIV_NOEXPN, PrivacyFlags)) { message("250-EXPN"); 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"); 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) { /* set sending host to our known value */ if (sendinghost == NULL) sendinghost = peerhostname; if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) { - message("503 Polite people say HELO first"); + usrerr("503 Polite people say HELO first"); break; } } if (gotmail) { - message("503 Sender already specified"); + usrerr("503 Sender already specified"); if (InChild) finis(); break; } if (InChild) { errno = 0; syserr("503 Nested MAIL command: MAIL %s", p); finis(); } p = skipword(p, "from"); if (p == NULL) break; /* fork a subprocess to process this command */ if (runinchild("SMTP-MAIL", e) > 0) break; 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); 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: 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'; /* do config file checking of the sender */ if (rscheck("check_mail", p, NULL, e) != EX_OK) goto undo_subproc; /* 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 (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) { usrerr("552 Message size exceeds fixed maximum message size (%ld)", MaxMessageSize); /* NOTREACHED */ } if (!enoughdiskspace(e->e_msgsize)) { - message("452 Insufficient disk space; try again later"); + usrerr("452 Insufficient disk space; try again later"); break; } 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("450 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) break; if (delimptr != NULL && *delimptr != '\0') *delimptr++ = '\0'; /* do config file checking of the recipient */ if (rscheck("check_rcpt", p, NULL, e) != EX_OK) 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); } /* 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.... */ - message("550 Addressee unknown"); + usrerr("550 Addressee unknown"); } - e->e_to = NULL; break; case CMDDATA: /* data -- text of mail */ SmtpPhase = "server DATA"; if (!gotmail) { - message("503 Need MAIL command"); + usrerr("503 Need MAIL command"); break; } else if (nrcpts <= 0) { - message("503 Need RCPT (recipient)"); + usrerr("503 Need RCPT (recipient)"); break; } /* 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)) { /* 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, doublequeue, NULL, e); + collect(InChannel, TRUE, NULL, e); flush_errors(TRUE); if (Errors != 0) 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) { extern pid_t dowork(); 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 */ - message("250 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"); + 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"); -#ifdef LOG if (LogLevel > 5) - syslog(LOG_INFO, "%.100s: %s [rejected]", + sm_syslog(LOG_INFO, e->e_id, + "%.100s: %s [rejected]", CurSmtpClient, shortenstring(inp, 203)); -#endif break; } else if (!gothello && bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, PrivacyFlags)) { - message("503 I demand that you introduce yourself first"); + usrerr("503 I demand that you introduce yourself first"); break; } if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) break; -#ifdef LOG if (LogLevel > 5) - syslog(LOG_INFO, "%.100s: %s", + sm_syslog(LOG_INFO, e->e_id, + "%.100s: %s", CurSmtpClient, shortenstring(inp, 203)); -#endif vrfyqueue = NULL; - QuickAbort = TRUE; if (vrfy) e->e_flags |= EF_VRFYONLY; while (*p != '\0' && isascii(*p) && isspace(*p)) p++; if (*p == '\0') { - message("501 Argument required"); - Errors++; + usrerr("501 Argument required"); } else { (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); } if (Errors != 0) { if (InChild) finis(); break; } if (vrfyqueue == NULL) { - message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); + 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 (strlen(p) <= 0) { - message("500 Parameter required"); + usrerr("500 Parameter required"); break; } /* crude way to avoid denial-of-service attacks */ - checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN"); + checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e); id = p; if (*id == '@') id++; else *--id = '@'; -#ifdef LOG if (LogLevel > 5) - syslog(LOG_INFO, "%.100s: ETRN %s", + sm_syslog(LOG_INFO, e->e_id, + "%.100s: ETRN %s", CurSmtpClient, shortenstring(id, 203)); -#endif QueueLimitRecipient = id; ok = runqueue(TRUE, TRUE); QueueLimitRecipient = NULL; if (ok) 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"); + 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)) { /* this would give out the same info */ message("502 Verbose unavailable"); break; } - checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB"); - Verbose = TRUE; + 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"); + checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e); OneXact = TRUE; message("250 Only one transaction"); break; case CMDXUSR: /* initial (user) submission */ - checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR"); + 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 */ -# ifdef LOG if (LogLevel > 0) - syslog(LOG_CRIT, + sm_syslog(LOG_CRIT, e->e_id, "\"%s\" command from %.100s (%.100s)", c->cmdname, CurSmtpClient, anynet_ntoa(&RealHostAddr)); -# endif /* FALL THROUGH */ case CMDERROR: /* unknown command */ if (++badcommands > MAXBADCOMMANDS) { message("421 %s Too many bad commands; closing connection", MyHostName); goto doquit; } - message("500 Command unrecognized"); + usrerr("500 Command unrecognized: \"%s\"", + shortenstring(inp, 203)); 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) +checksmtpattack(pcounter, maxcount, cname, e) volatile int *pcounter; int maxcount; char *cname; + ENVELOPE *e; { if (++(*pcounter) >= maxcount) { -#ifdef LOG if (*pcounter == maxcount && LogLevel > 5) { - syslog(LOG_INFO, "%.100s: %.40s attack?", + sm_syslog(LOG_INFO, e->e_id, + "%.100s: %.40s attack?", CurSmtpClient, cname); } -#endif 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 *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: - message("501 Syntax error in parameters scanning \"%s\"", + usrerr("501 Syntax error in parameters scanning \"%s\"", shortenstring(firstp, 203)); - Errors++; 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; char buf[MAXLINE]; extern char Version[]; - if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) + if (HelpFile == NULL || + (hf = safefopen(HelpFile, O_RDONLY, 0444, SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK)) == 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); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/stab.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/stab.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/stab.c (revision 26986) @@ -1,236 +1,241 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)stab.c 8.10 (Berkeley) 11/23/96"; +static char sccsid[] = "@(#)stab.c 8.13 (Berkeley) 4/19/97"; #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(); 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 */ -#ifdef _FFR_MEMORY_MISER - if (type >= ST_MCI) - len = sizeof s->s_mci; - else - len = -1; +#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; - } - if (len < 0) - { - syserr("stab: unknown symbol type %d", type); - len = sizeof s->s_value; + + 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); } } } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/stats.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/stats.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/stats.c (revision 26986) @@ -1,132 +1,131 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)stats.c 8.6 (Berkeley) 2/21/96"; +static char sccsid[] = "@(#)stats.c 8.11 (Berkeley) 4/9/97"; #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) register ENVELOPE *e; register ADDRESS *to; { 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; struct statistics stat; extern off_t lseek(); if (sfile == NULL || !GotStats) return; (void) time(&Stat.stat_itime); Stat.stat_size = sizeof Stat; - fd = open(sfile, O_RDWR); + fd = safeopen(sfile, O_RDWR, 0644, SFF_REGONLY|SFF_NOLINK|SFF_OPENASROOT); if (fd < 0) { errno = 0; return; } - (void) lockfile(fd, sfile, NULL, LOCK_EX); if (read(fd, (char *) &stat, sizeof stat) == sizeof stat && stat.stat_size == sizeof stat) { /* 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]; } } 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; } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/sysexits.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/sysexits.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/sysexits.c (revision 26986) @@ -1,184 +1,184 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)sysexits.c 8.6 (Berkeley) 2/21/96"; +static char sccsid[] = "@(#)sysexits.c 8.7 (Berkeley) 2/1/97"; #endif /* not lint */ #include /* ** 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; } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/trace.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/trace.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/trace.c (revision 26986) @@ -1,133 +1,133 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)trace.c 8.5 (Berkeley) 2/21/96"; +static char sccsid[] = "@(#)trace.c 8.6 (Berkeley) 2/1/97"; #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 (isdigit(*s)) i = i * 10 + (*s++ - '0'); first = i; /* find last flag to set */ if (*s == '-') { i = 0; while (isdigit(*++s)) i = i * 10 + (*s - '0'); } last = i; /* find the level to set it to */ i = 1; if (*s == '.') { i = 0; while (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; } } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/udb.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/udb.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/udb.c (revision 26986) @@ -1,1154 +1,1160 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #include "sendmail.h" #ifndef lint #if USERDB -static char sccsid [] = "@(#)udb.c 8.47 (Berkeley) 12/6/96 (with USERDB)"; +static char sccsid [] = "@(#)udb.c 8.51 (Berkeley) 5/29/97 (with USERDB)"; #else -static char sccsid [] = "@(#)udb.c 8.47 (Berkeley) 12/6/96 (without USERDB)"; +static char sccsid [] = "@(#)udb.c 8.51 (Berkeley) 5/29/97 (without USERDB)"; #endif #endif #if USERDB #include #ifdef NEWDB # include #else # define DBT struct _data_base_thang_ DBT { void *data; /* pointer to data */ size_t size; /* length of data */ }; #endif #ifdef HESIOD # include #endif /* HESIOD */ /* ** 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; }; -extern int _udbx_init __P((void)); +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]; 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() == EX_TEMPFAIL) + 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 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); i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); if (i > 0 || info.size <= 0) { if (tTd(28, 2)) printf("udbexpand: no match on %s (%d)\n", keybuf, keylen); 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; return EX_OK; } breakout = TRUE; if (info.size >= userleft - 1) { - char *nuser = xalloc(usersize + MEMCHUNKSIZE); + 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 += MEMCHUNKSIZE; - userleft += MEMCHUNKSIZE; + 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 */ i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); } /* if nothing ever matched, try next database */ if (!breakout) break; message("expanded to %s", user); -#ifdef LOG if (LogLevel >= 10) - syslog(LOG_INFO, "%s: expand %.100s => %s", - e->e_id, e->e_to, + sm_syslog(LOG_INFO, e->e_id, + "expand %.100s => %s", + e->e_to, shortenstring(user, 203)); -#endif 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", key.size, key.data, i); return EX_TEMPFAIL; } /* ** 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 = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 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", key.size, 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", keybuf, 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", key.size, key.data, info.size, 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); -#ifdef LOG if (LogLevel >= 10) - syslog(LOG_INFO, "%s: hesiod %.100s => %s", - e->e_id, e->e_to, + sm_syslog(LOG_INFO, e->e_id, + "hesiod %.100s => %s", + e->e_to, shortenstring(user, 203)); -#endif 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(); 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() == EX_TEMPFAIL) + 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 */ - if ((strlen(user) + strlen(field)) > sizeof keybuf - 4) + 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: key.data = keybuf; key.size = keylen; i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 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; break; #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) { key.data = ":default:mailname"; key.size = strlen(key.data); i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 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 = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 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 #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. */ 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: -** none. +** 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() +_udbx_init(e) + ENVELOPE *e; { 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 nopts; 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'; /* extract options */ nopts = _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; 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; up++; break; #ifdef HESIOD case 'h': /* use hesiod */ case 'H': if (strcasecmp(spec, "hesiod") != 0) goto badspec; up->udb_type = UDB_HESIOD; 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; up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY, 0644, DB_BTREE, NULL); if (up->udb_dbp == NULL) { if (tTd(28, 1)) { int saveerrno = errno; - printf("dbopen(%s): %s", + printf("dbopen(%s): %s\n", up->udb_dbname, errstring(errno)); errno = saveerrno; } if (errno != ENOENT && errno != EACCES) { -#ifdef LOG if (LogLevel > 2) - syslog(LOG_ERR, "dbopen(%s): %s", + sm_syslog(LOG_ERR, e->e_id, + "dbopen(%s): %s", up->udb_dbname, errstring(errno)); -#endif 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; 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) { (*up->udb_dbp->close)(up->udb_dbp); } } #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 *p, **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 */ hp = hes_resolve(name, type); *--type = ':'; if (hp == NULL || hp[0] == NULL) { /* network problem or timeout */ if (hes_error() == HES_ER_NET) return -1; return 1; } 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 */ Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/useful.h =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/useful.h (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/useful.h (revision 26986) @@ -1,78 +1,80 @@ /* - * Copyright (c) 1995, 1996 Eric P. Allman + * Copyright (c) 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. * - * @(#)useful.h 8.5 (Berkeley) 2/21/96 + * @(#)useful.h 8.7 (Berkeley) 5/29/97 */ # 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 Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/usersmtp.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/usersmtp.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/usersmtp.c (revision 26986) @@ -1,1213 +1,1212 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ # include "sendmail.h" #ifndef lint #if SMTP -static char sccsid[] = "@(#)usersmtp.c 8.80 (Berkeley) 1/18/97 (with SMTP)"; +static char sccsid[] = "@(#)usersmtp.c 8.87 (Berkeley) 6/3/97 (with SMTP)"; #else -static char sccsid[] = "@(#)usersmtp.c 8.80 (Berkeley) 1/18/97 (without SMTP)"; +static char sccsid[] = "@(#)usersmtp.c 8.87 (Berkeley) 6/3/97 (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 */ int SmtpPid; /* pid of mailer */ 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(); extern void helo_options(); 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 _FFR_LMTP if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags)) #else if (bitnset(M_ESMTP, m->m_flags)) #endif mci->mci_flags |= MCIF_ESMTP; tryhelo: #if _FFR_LMTP 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)) #else if (bitset(MCIF_ESMTP, mci->mci_flags)) #endif { 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 _FFR_LMTP if (bitset(MCIF_ESMTP, mci->mci_flags) && !bitnset(M_LMTP, m->m_flags)) #else if (bitset(MCIF_ESMTP, mci->mci_flags)) #endif { /* 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) && #if _FFR_LMTP !bitnset(M_LMTP, m->m_flags) && #endif strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) { syserr("553 %s config error: mail loops back to me (MX problem?)", mci->mci_host); 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) + 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", mci->mci_host); 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 (r == 452 && bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) { mci_setstat(mci, EX_NOTSTICKY, smtptodsn(r), SmtpReplyBuffer); return EX_TEMPFAIL; } else if (REPLYTYPE(r) == 4) { mci_setstat(mci, EX_TEMPFAIL, 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.2.2", 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; } -#ifdef LOG if (LogLevel > 1) { - syslog(LOG_CRIT, "%s: %.100s: SMTP MAIL protocol error: %s", - e->e_id, mci->mci_host, + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP MAIL protocol error: %s", + mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } -#endif /* 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; } -#ifdef LOG if (LogLevel > 1) { - syslog(LOG_CRIT, "%s: %.100s: SMTP RCPT protocol error: %s", - e->e_id, mci->mci_host, + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP RCPT protocol error: %s", + mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } -#endif 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(); 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) { -#ifdef LOG if (LogLevel > 1) { - syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-1 protocol error: %s", - e->e_id, mci->mci_host, + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP DATA-1 protocol error: %s", + mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } -#endif 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", mci->mci_host); 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 _FFR_LMTP if (bitnset(M_LMTP, m->m_flags)) return EX_OK; #endif 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 (r == 552) - rstat = EX_UNAVAILABLE; 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 = xstat = EX_UNAVAILABLE; + 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; -#ifdef LOG if (LogLevel > 1) { - syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-2 protocol error: %s", - e->e_id, mci->mci_host, + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP DATA-2 protocol error: %s", + mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } -#endif 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. */ #if _FFR_LMTP 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; mci_setstat(mci, stat, smtptodsn(r), SmtpReplyBuffer); -#ifdef LOG if (LogLevel > 1 && stat == EX_PROTOCOL) { - syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-3 protocol error: %s", - e->e_id, mci->mci_host, + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP DATA-3 protocol error: %s", + mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } -#endif return stat; } #endif /* ** 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) - { - SuprErrs = oldSuprErrs; 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. */ - for (bufp = SmtpReplyBuffer;; bufp = junkbuf) + bufp = SmtpReplyBuffer; + for (;;) { register char *p; extern time_t curtime(); /* 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", mci->mci_host); 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, 203)); plen = strlen(p); p += plen; wbufleft -= plen; } snprintf(p, wbufleft, "reply(%.100s) during %s", mci->mci_host, 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] == '-')) + continue; + /* process the line */ if (pfunc != NULL) (*pfunc)(bufp, firstline, m, mci, e); firstline = FALSE; - /* if continuation is required, we can go on */ - if (bufp[3] == '-') - continue; - - /* ignore improperly formated input */ - if (!(isascii(bufp[0]) && isdigit(bufp[0]))) - continue; - /* decode the reply code */ r = atoi(bufp); /* extra semantics: 0xx codes are "informational" */ - if (r >= 100) + 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' && 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 */ Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/util.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/util.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/util.c (revision 26986) @@ -1,2334 +1,2013 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)util.c 8.115 (Berkeley) 1/5/97"; +static char sccsid[] = "@(#)util.c 8.129 (Berkeley) 6/11/97"; #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'); } /* ** 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) { printf("%s$", TermEscape.te_rv_on); shiftout = TRUE; 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'; } /* -** 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 - -#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 - -#define ST_MODE_NOFILE 0171147 /* unlikely to occur */ - -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; - struct stat stbuf; - struct stat fstbuf; - - 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; - - /* 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) -#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; - } - - if (!bitset(SFF_NOPATHCHECK, flags) || - (uid == 0 && !bitset(SFF_ROOTOK, flags))) - { - /* check the path to the file for acceptability */ - for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/') - { - *p = '\0'; - if (stat(fn, &stbuf) < 0) - break; - if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode)) - message("051 WARNING: writable directory %s", - fn); - if (uid == 0 && !bitset(SFF_ROOTOK, flags)) - { - if (bitset(S_IXOTH, stbuf.st_mode)) - continue; - break; - } - 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)) - break; - } - if (p != NULL) - { - int ret = errno; - - if (ret == 0) - ret = EACCES; - if (tTd(44, 4)) - printf("\t[dir %s] %s\n", fn, errstring(ret)); - *p = '/'; - 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; - - if (tTd(44, 4)) - printf("\t%s\n", errstring(ret)); - - errno = 0; - if (!bitset(SFF_CREAT, flags)) - return ret; - - /* check to see if legal to create the file */ - p = strrchr(fn, '/'); - if (p == NULL) - return ENOTDIR; - *p = '\0'; - if (stat(fn, &stbuf) >= 0) - { - int md = S_IWRITE|S_IEXEC; - if (stbuf.st_uid != uid) - md >>= 6; - 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", - fn, (int) stbuf.st_uid, (u_long) stbuf.st_mode, - errstring(ret)); - *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 %o]\tEPERM\n", st->st_mode); - return EPERM; - } -#endif - if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) - { - if (tTd(44, 4)) - printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode); - return EPERM; - } - 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 %o]\tEPERM]\n", st->st_mode); - return EPERM; - } - if (st->st_nlink > 1) - { - if (tTd(44, 4)) - printf("\t[link count %d]\tEPERM\n", st->st_nlink); - return EPERM; - } - - if (uid == 0 && bitset(SFF_OPENASROOT, flags)) - ; - else if (uid == 0 && !bitset(SFF_ROOTOK, flags)) - mode >>= 6; - else if (st->st_uid != uid) - { - 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 || - !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; -} - /* -** 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. -*/ - -#ifndef O_ACCMODE -# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) -#endif - -FILE * -safefopen(fn, omode, cmode, sff) - char *fn; - int omode; - int cmode; - int sff; -{ - int rval; - FILE *fp; - int smode; - struct stat stb, sta; - - if (bitset(O_CREAT, omode)) - sff |= SFF_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, 0, 0, NULL, sff, smode, &stb); - else - rval = safefile(fn, RealUid, RealGid, RealUserName, - sff, smode, &stb); - if (rval != 0) - { - errno = rval; - return NULL; - } - if (stb.st_mode == ST_MODE_NOFILE) - omode |= O_EXCL; - - fp = dfopen(fn, omode, cmode); - if (fp == NULL) - return NULL; - if (bitset(O_EXCL, omode)) - return fp; - if (fstat(fileno(fp), &sta) < 0 || - sta.st_nlink != stb.st_nlink || - sta.st_dev != stb.st_dev || - sta.st_ino != stb.st_ino || - sta.st_uid != stb.st_uid || - sta.st_gid != stb.st_gid) - { - syserr("554 cannot open: file %s changed after open", fn); - fclose(fp); - errno = EPERM; - return NULL; - } - return fp; -} - /* ** 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'; } /* -** DFOPEN -- determined file open -** -** This routine has the semantics of fopen, 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. -*/ - -struct omodes -{ - int mask; - int mode; - char *farg; -} OpenModes[] = -{ - { O_ACCMODE, O_RDONLY, "r" }, - { O_ACCMODE|O_APPEND, O_WRONLY, "w" }, - { O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a" }, - { O_TRUNC, 0, "w+" }, - { O_APPEND, O_APPEND, "a+" }, - { 0, 0, "r+" }, -}; - -FILE * -dfopen(filename, omode, cmode) - char *filename; - int omode; - int cmode; -{ - register int tries; - int fd; - register struct omodes *om; - struct stat st; - - for (om = OpenModes; om->mask != 0; om++) - if ((omode & om->mask) == om->mode) - break; - - 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 (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; - } - if (fd < 0) - return NULL; - else - return fdopen(fd, om->farg); -} - /* ** 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, mci, PXLF_MAPFROM); + 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. ** ** Returns: ** none ** ** Side Effects: ** output of l to fp. */ void -putxline(l, mci, pxflags) +putxline(l, len, mci, pxflags) register char *l; + size_t len; register MCI *mci; int pxflags; { - register char *p; + register char *p, *end; register char svchar; int slop = 0; /* strip out 0200 bits -- these can look like TELNET protocol */ if (bitset(MCIF_7BIT, mci->mci_flags) || bitset(PXLF_STRIP8BIT, pxflags)) { 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 = strchr(l, '\n'); + p = memchr(l, '\n', end - l); if (p == NULL) - p = &l[strlen(l)]; + 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) { register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; svchar = *q; *q = '\0'; if (l[0] == '.' && slop == 0 && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { (void) putc('.', mci->mci_out); 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 (TrafficLogFile != NULL) (void) putc('>', TrafficLogFile); } fputs(l, mci->mci_out); (void) putc('!', mci->mci_out); fputs(mci->mci_mailer->m_eol, mci->mci_out); (void) putc(' ', mci->mci_out); if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%s!\n%05d >>> ", - l, (int) getpid()); + { + for ( ; l < q; ++l) + (void) putc(*l, TrafficLogFile); + fprintf(TrafficLogFile, "!\n%05d >>> ", + (int) getpid()); + } *q = svchar; l = q; slop = 1; } /* output last part */ if (l[0] == '.' && slop == 0 && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { (void) putc('.', mci->mci_out); 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 (TrafficLogFile != NULL) (void) putc('>', TrafficLogFile); } - if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%.*s\n", p - l, l); for ( ; l < p; ++l) + { + if (TrafficLogFile != NULL) + (void) putc(*l, TrafficLogFile); (void) putc(*l, mci->mci_out); + } + if (TrafficLogFile != NULL) + (void) putc('\n', TrafficLogFile); fputs(mci->mci_mailer->m_eol, mci->mci_out); if (*l == '\n') { if (*++l != ' ' && *l != '\t' && *l != '\0') { (void) putc(' ', mci->mci_out); if (TrafficLogFile != NULL) (void) putc(' ', TrafficLogFile); } } - } while (l[0] != '\0'); + } 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; -# ifdef LOG if (LogLevel > 98) - syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f); -# endif /* LOG */ + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "unlink %s", + f); i = unlink(f); -# ifdef LOG if (i < 0 && LogLevel > 97) - syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); -# endif /* LOG */ + 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(); 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) { -# ifdef LOG if (LogLevel > 1) - syslog(LOG_NOTICE, + sm_syslog(LOG_NOTICE, CurEnv->e_id, "timeout waiting for input from %.100s during %s", CurHostName ? CurHostName : "local", during); -# endif errno = 0; - usrerr("451 timeout waiting for input during %s", - during); 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); } 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); } /* -** 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; - - do - { - errno = 0; - i = wait(&st); - 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 -} - /* ** 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; struct stat stbuf; for (i = 0; i < 3; i++) { if (fstat(i, &stbuf) < 0 && errno == EBADF) { /* oops.... */ int fd; syserr("%s: fd %d not open", where, i); fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666); if (fd != i) { (void) dup2(fd, i); (void) close(fd); } } } #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) { - syslog(LOG_DEBUG, "%s: changed fds:", where); + 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; char *fmtstr; #ifdef S_IFSOCK SOCKADDR sa; #endif auto int slen; struct stat st; char buf[200]; extern char *hostnamebyanyaddr(); p = buf; snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); p += strlen(p); if (fstat(fd, &st) < 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; } slen = fcntl(fd, F_GETFL, NULL); if (slen != -1) { snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", slen); 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_size > sizeof (long)) fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd"; else fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld"; snprintf(p, SPACELEFT(buf, p), fmtstr, major(st.st_dev), minor(st.st_dev), st.st_ino, st.st_nlink, st.st_uid, st.st_gid, st.st_size); break; } printit: -#ifdef LOG if (logit) - syslog(LOG_DEBUG, "%.800s", buf); + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "%.800s", buf); else -#endif printf("%s\n", 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. */ #ifndef MAXSHORTSTR # define MAXSHORTSTR 203 #endif 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; } /* ** 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) syserr("prog_open: setgid(%ld) failed", (long) DefGid); if (setuid(DefUid) < 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; { -#ifdef LOG /* check for newlines and log if necessary */ (void) denlstring(f, TRUE, TRUE); -#endif 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++ = ' '; -#ifdef LOG if (logattacks) { - syslog(LOG_NOTICE, "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", + sm_syslog(LOG_NOTICE, CurEnv->e_id, + "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", RealHostName == NULL ? "[UNKNOWN]" : RealHostName, shortenstring(bp, 203)); } -#endif 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) { -#ifdef LOG if (LogLevel > 3) - syslog(LOG_DEBUG, "proc_list_probe: lost pid %d", + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "proc_list_probe: lost pid %d", ProcListVec[i]); -#endif 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); } Index: vendor/sendmail/dist-old/usr.sbin/sendmail/src/version.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/src/version.c (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/src/version.c (revision 26986) @@ -1,39 +1,39 @@ /* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988, 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. */ #ifndef lint -static char sccsid[] = "@(#)version.c 8.8.5.3 (Berkeley) 1/21/97"; +static char sccsid[] = "@(#)version.c 8.8.6.1 (Berkeley) 6/14/97"; #endif /* not lint */ -char Version[] = "8.8.5"; +char Version[] = "8.8.6"; Index: vendor/sendmail/dist-old/usr.sbin/sendmail/test/Results =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/test/Results (revision 26985) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/test/Results (revision 26986) @@ -1,97 +1,156 @@ The following are results of running t_setreuid on various architectures. OPSYS VERSION STATUS DATE TESTER/NOTES ===== ======= ====== ==== ============ SunOS 4.1 OK 93.07.19 eric SunOS 4.1.2 OK 93.07.19 eric SunOS 4.1.3 OK 93.09.25 Robert Elz BSD 4.4 OK 93.07.19 eric (wierd results, but functional) BSD 4.3Utah OK 93.07.19 eric FreeBSD 2.1-sta OK 96.04.14 Jaye Mathisen Ultrix 4.2A OK 93.07.19 eric Ultrix 4.3A OK 93.07.19 Allan Johannesen Ultrix 4.5 OK 96.09.18 Gregory Neil Shapiro HP-UX 8.07 OK 93.07.19 eric (on 7xx series) HP-UX 8.02 OK 93.07.19 Michael Corrigan (on 8xx series) HP-UX 8.00 OK 93.07.21 Michael Corrigan (on 3xx/4xx series) HP-UX 9.01 OK 93.11.19 Cassidy (on 7xx series) Solaris 2.1 Solaris 2.2 FAIL 93.07.19 Bill Wisner Solaris 2.3 FAIL 95.11.22 Scott J. Kramer Solaris 2.5 OK 96.02.29 Carson Gaspar Solaris 2.5.1 OK 96.11.29 Gregory Neil Shapiro OSF/1 T1.3-4 OK 93.07.19 eric (on DEC Alpha) OSF/1 1.3 OK 94.12.10 Jeff A. Earickson (on Intel Paragon) OSF/1 3.2D OK 96.09.18 Gregory Neil Shapiro OSF/1 4.0 OK 96.09.18 Gregory Neil Shapiro CxOS 11.5 OK 96.07.08 Eric Schnoebelen CxOS 11.0 OK 93.01.21 Eric Schnoebelen (CxOS 11.0 beta 1) CxOS 10.x OK 93.01.21 Eric Schnoebelen AIX 3.1.5 FAIL 93.08.07 David J. N. Begley AIX 3.2.3e FAIL 93.07.26 Steve Bauer AIX 3.2.4 FAIL 93.10.07 David J. N. Begley AIX 3.2.5 FAIL 94.05.17 Steve Bauer AIX 4.1 FAIL 96.10.21 Hakan Lindholm AIX 4.2 OK 96.10.16 Steve Bauer IRIX 4.0.4 OK 93.09.25 Robert Elz IRIX 5.2 OK 94.12.06 Mark Andrews IRIX 5.3 OK 94.12.06 Mark Andrews IRIX 6.2 OK 96.09.16 Kari E. Hurtta +IRIX 6.3 OK 97.02.10 Mark Andrews SCO 3.2v4.0 OK 93.10.02 Peter Wemm (with -lsocket from 3.2v4 devsys) NeXT 2.1 OK 93.07.28 eric NeXT 3.0 OK 34.05.05 Kevin John Wang Linux 0.99p10 OK 93.08.08 Karl London Linux 0.99p13 OK 93.09.27 Christian Kuhtz Linux 0.99p14 OK 93.11.30 Christian Kuhtz Linux 1.0 OK 94.03.19 Shayne Smith Linux 1.2.13 OK 95.11.02 Sven Neuhaus Linux 2.0.17 OK 96.09.03 Horst von Brand BSD/386 1.0 OK 93.11.13 Tony Sanders DELL 2.2 OK 93.11.15 Peter Wemm (using -DSETEUID) Pyramid 5.0d OK 95.01.14 David Miller The following are results of running t_seteuid on various architectures. OPSYS VERSION STATUS DATE TESTER/NOTES ===== ======= ====== ==== ============ Solaris 2.3 OK 95.11.22 Scott J. Kramer Solaris 2.4 OK 95.09.22 Thomas 'Mike' Michlmayr Solaris 2.5 OK 96.02.29 Carson Gaspar Solaris 2.5.1 OK 96.11.29 Gregory Neil Shapiro Linux 1.2.13 FAIL 95.11.02 Sven Neuhaus Linux 2.0.17 FAIL 96.09.03 Horst von Brand AIX 4.1 OK 96.10.21 Hakan Lindholm IRIX 5.2 OK 95.12.01 Mark Andrews IRIX 5.3 OK 95.12.01 Mark Andrews IRIX 6.2 OK 96.09.16 Kari E. Hurtta +IRIX 6.3 OK 97.02.10 Mark Andrews FreeBSD 2.1-sta OK 96.04.14 Jaye Mathisen Ultrix 4.5 FAIL 96.09.18 Gregory Neil Shapiro OSF/1 3.2D OK 96.09.18 Gregory Neil Shapiro OSF/1 4.0 OK 96.09.18 Gregory Neil Shapiro CxOS 11.5 FAIL 96.07.08 Eric Schnoebelen + + +The following are the results of running t_pathconf.c. Safe means that +the underlying filesystem (in NFS, the filesystem on the server) does not +permit regular (non-root) users to chown their files to another user. +Unsafe means that they can. Typically, BSD-based systems do not permit +giveaway and System V-based systems do. However, some systems (e.g., +Solaris) can set this on a per-system or per-filesystem basis. Entries +are the return value of pathconf, the errno value, and a * if chown +disagreed with the result of the pathconf call, and a ? if the test has +not been run. A mark of [R] means that the local filesystem has +chown set to be restricted, [U] means that it is set to be unrestricted. + + Safe Filesystem Unsafe Filesystem +SYSTEM LOCAL NFS-V2 NFS-V3 NFS-V2 NFS-V3 + +SunOS 4.1.3_U1 1/0 -1/EINVAL* n/a -1/EINVAL? n/a +SunOS 4.1.4 1/0 -1/EINVAL* n/a -1/EINVAL n/a + +AIX 3.2 0/0 0/0 + +Solaris 2.4 1/0 -1/EINVAL* +Solaris 2.5 1/0 -1/EINVAL* 1/0 0/0? +Solaris 2.5.1 1/0 -1/EINVAL* 0/0 + +DEC OSF1 3.0 0/0 0/0 +DEC OSF1 3.2D-2 0/0 0/0 0/0 +DEC OSF1 4.0A 0/0 0/0 0/0 +DEC OSF 4.0B 0/0 0/0 0/0 + +Ultrix 4.3 0/0 0/0 n/a n/a +Ultrix 4.5 1/0 1/0 + +HP-UX 9.05 -1/0 -1/EOPNOTSUPP* -1/EOPNOTSUPP +HP-UX 9.05[R] 1/0 -1/EOPNOTSUPP* -1/EOPNOTSUPP* +HP-UX 10.10 -1/0 -1/EOPNOTSUPP* -1/EOPNOTSUPP +HP-UX 10.20 -1/EOPNOTSUPP? -1/EOPNOTSUPP? +HP-UX 10.30 -1/0 -1/EOPNOTSUPP -1/EOPNOTSUPP + +BSD/OS 2.1 1/0 + +FreeBSD 2.1.7 1/0 -1/EINVAL* -1/EINVAL + +Irix 5.3 -1/0* -1/0 +Irix 6.2 1/0 -1/0 0/0* +Irix 6.2 -1/0 -1/0 +Irix 6.3 R10000 -1/0 -1/0 0/0* + +A/UX 3.1.1 1/0 + +DomainOS [R] -1/0* +DomainOS [U] -1/0 + +NCR MP-RAS 2 -1/0 +NCR MP-RAS 3 -1/0 + +Linux 2.0.27 1/0 1/0 Index: vendor/sendmail/dist-old/usr.sbin/sendmail/test/t_pathconf.c =================================================================== --- vendor/sendmail/dist-old/usr.sbin/sendmail/test/t_pathconf.c (nonexistent) +++ vendor/sendmail/dist-old/usr.sbin/sendmail/test/t_pathconf.c (revision 26986) @@ -0,0 +1,63 @@ +/* +** The following test program tries the pathconf(2) routine. It should +** be run in a non-NFS-mounted directory (e.g., /tmp) and on remote (NFS) +** mounted directories running both NFS-v2 and NFS-v3 from systems that +** both do and do not permit file giveaway. +*/ + +#include +#include +#include +#include +#include + +main() +{ + int fd; + int i; + char tbuf[100]; + extern int errno; + + if (geteuid() == 0) + { + printf("*** Run me as a non-root user! ***\n"); + exit(EX_USAGE); + } + + strcpy(tbuf, "TXXXXXX"); + fd = mkstemp(tbuf); + if (fd < 0) + { + printf("*** Could not create test file %s\n", tbuf); + exit(EX_CANTCREAT); + } + errno = 0; + i = pathconf(".", _PC_CHOWN_RESTRICTED); + printf("pathconf(.) returns %2d, errno = %d\n", i, errno); + errno = 0; + i = pathconf(tbuf, _PC_CHOWN_RESTRICTED); + printf("pathconf(%s) returns %2d, errno = %d\n", tbuf, i, errno); + errno = 0; + i = fpathconf(fd, _PC_CHOWN_RESTRICTED); + printf("fpathconf(%s) returns %2d, errno = %d\n", tbuf, i, errno); + if (errno == 0 && i >= 0) + { + /* so it claims that it doesn't work -- try anyhow */ + printf(" fpathconf claims that chown is safe "); + if (fchown(fd, 1, 1) >= 0) + printf("*** but fchown works anyhow! ***\n"); + else + printf("and fchown agrees\n"); + } + else + { + /* well, let's see what really happens */ + printf(" fpathconf claims that chown is not safe "); + if (fchown(fd, 1, 1) >= 0) + printf("as indeed it is not\n"); + else + printf("*** but in fact it is safe ***\n"); + } + unlink(tbuf); + exit(EX_OK); +} Property changes on: vendor/sendmail/dist-old/usr.sbin/sendmail/test/t_pathconf.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property