hubert@cac.washington.edu (Steve Hubert) (10/18/90)
DESCRIPTION:
Varargs handling is incorrect.
FIX:
Define FIXVARARGS to get the correct behavior out of the files listed
below.
=======================
conf.c
=======================
/*
* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted provided
* that: (1) source distributions retain this entire copyright notice and
* comment, and (2) distributions including binaries display the following
* acknowledgement: ``This product includes software developed by the
* University of California, Berkeley and its contributors'' in the
* documentation or other materials provided with the distribution and in
* all advertising materials mentioning features or use of this software.
* 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static char sccsid[] = "@(#)conf.c 5.26 (Berkeley) 6/1/90";
#endif /* not lint */
# include <sys/ioctl.h>
# include <sys/param.h>
# include <pwd.h>
# include "sendmail.h"
# include "pathnames.h"
#ifdef FIXVARARGS
#include <varargs.h>
#endif /* FIXVARARGS */
/*
** CONF.C -- Sendmail Configuration Tables.
**
** Defines the configuration of this installation.
**
** Compilation Flags:
** VMUNIX -- running on a Berkeley UNIX system.
**
** 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,
"full-name", H_ACHECK,
"return-receipt-to", H_FROM,
"errors-to", H_FROM,
/* destination fields */
"to", H_RCPT,
"resent-to", H_RCPT|H_RESENT,
"cc", H_RCPT,
"resent-cc", H_RCPT|H_RESENT,
"bcc", H_RCPT|H_ACHECK,
"resent-bcc", H_RCPT|H_ACHECK|H_RESENT,
/* 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,
"via", H_TRACE|H_FORCE,
"mail-from", H_TRACE|H_FORCE,
NULL, 0,
};
/*
** ARPANET error message numbers.
*/
char Arpa_Info[] = "050"; /* arbitrary info */
char Arpa_TSyserr[] = "451"; /* some (transient) system error */
char Arpa_PSyserr[] = "554"; /* some (permanent) system error */
char Arpa_Usrerr[] = "554"; /* some (fatal) user error */
/*
** Location of system files/databases/etc.
*/
char *ConfFile = _PATH_SENDMAILCF; /* runtime configuration */
char *FreezeFile = _PATH_SENDMAILFC; /* frozen version of above */
/*
** Miscellaneous stuff.
*/
int DtableSize = 50; /* max open files; reset in 4.2bsd */
extern int la; /* load average */
/*
** SETDEFAULTS -- set default values
**
** Because of the way freezing is done, these must be initialized
** using direct code.
**
** Parameters:
** none.
**
** Returns:
** none.
**
** Side Effects:
** Initializes a bunch of global variables to their
** default values.
*/
setdefaults()
{
QueueLA = 8;
QueueFactor = 10000;
RefuseLA = 12;
SpaceSub = ' ';
WkRecipFact = 1000;
WkClassFact = 1800;
WkTimeFact = 9000;
FileMode = 0644;
DefUid = 1;
DefGid = 1;
setdefuser();
}
/*
** SETDEFUSER -- set/reset DefUser using DefUid (for initgroups())
*/
setdefuser()
{
struct passwd *defpwent;
if (DefUser != NULL)
free(DefUser);
if ((defpwent = getpwuid(DefUid)) != NULL)
DefUser = newstr(defpwent->pw_name);
else
DefUser = newstr("nobody");
}
/*
** GETRUID -- get real user id (V7)
*/
getruid()
{
if (OpMode == MD_DAEMON)
return (RealUid);
else
return (getuid());
}
/*
** GETRGID -- get real group id (V7).
*/
getrgid()
{
if (OpMode == MD_DAEMON)
return (RealGid);
else
return (getgid());
}
/*
** 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;
extern struct passwd *getpwuid();
/* cache the result */
if (myname == NULL)
{
myname = getlogin();
if (myname == NULL || myname[0] == '\0')
{
pw = getpwuid(getruid());
if (pw != NULL)
myname = newstr(pw->pw_name);
}
else
{
myname = newstr(myname);
if ((pw = getpwnam(myname)) == NULL ||
getuid() != pw->pw_uid)
{
pw = getpwuid(getuid());
if (pw != NULL)
myname = newstr(pw->pw_name);
}
}
if (myname == NULL || myname[0] == '\0')
{
syserr("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
*/
# include <sys/stat.h>
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 FALSE should
** be returned.
**
** 'NoReturn' can be set to suppress the return-to-sender
** function; this should be done on huge messages.
**
** Parameters:
** to -- the person being sent to.
**
** Returns:
** TRUE -- ok to send.
** FALSE -- not ok.
**
** Side Effects:
** none (unless you include the usrerr stuff)
*/
bool
checkcompat(to)
register ADDRESS *to;
{
# ifdef lint
if (to == NULL)
to++;
# endif lint
# ifdef EXAMPLE_CODE
/* this code is intended as an example only */
register STAB *s;
s = stab("arpa", ST_MAILER, ST_FIND);
if (s != NULL && CurEnv->e_from.q_mailer != LocalMailer &&
to->q_mailer == s->s_mailer)
{
usrerr("No ARPA mail through this machine: see your system administration");
/* NoReturn = TRUE; to supress return copy */
return (FALSE);
}
# endif EXAMPLE_CODE
return (TRUE);
}
/*
** HOLDSIGS -- arrange to hold all signals
**
** Parameters:
** none.
**
** Returns:
** none.
**
** Side Effects:
** Arranges that signals are held.
*/
holdsigs()
{
}
/*
** RLSESIGS -- arrange to release all signals
**
** This undoes the effect of holdsigs.
**
** Parameters:
** none.
**
** Returns:
** none.
**
** Side Effects:
** Arranges that signals are released.
*/
rlsesigs()
{
}
/*
** 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.
*/
#ifndef sun
getla()
{
double avenrun[3];
if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0)
return (0);
return ((int) (avenrun[0] + 0.5));
}
#else /* sun */
#include <nlist.h>
struct nlist Nl[] =
{
{ "_avenrun" },
#define X_AVENRUN 0
{ 0 },
};
extern int la;
getla()
{
static int kmem = -1;
long avenrun[3];
extern off_t lseek();
if (kmem < 0)
{
kmem = open("/dev/kmem", 0, 0);
if (kmem < 0)
return (-1);
(void) ioctl(kmem, (int) FIOCLEX, (char *) 0);
nlist("/vmunix", Nl);
if (Nl[0].n_type == 0)
return (-1);
}
if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, 0) == -1 ||
read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
{
/* thank you Ian */
return (-1);
}
return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
}
#endif /* sun */
/*
** 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.
**
** 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.
*/
bool
shouldqueue(pri)
long pri;
{
if (la < QueueLA)
return (FALSE);
return (pri > (QueueFactor / (la - QueueLA + 1)));
}
/*
** 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.
*/
#ifdef FIXVARARGS
setproctitle(fmt, va_alist)
char *fmt;
va_dcl
#else /* FIXVARARGS */
/*VARARGS1*/
setproctitle(fmt, a, b, c)
char *fmt;
#endif /* FIXVARARGS */
{
# ifdef SETPROCTITLE
register char *p;
register int i;
extern char **Argv;
extern char *LastArgv;
char buf[MAXLINE];
#ifdef FIXVARARGS
va_list ap;
va_start(ap);
(void) vsprintf(buf, fmt, ap);
va_end(ap);
#else /* FIXVARARGS */
(void) sprintf(buf, fmt, a, b, c);
#endif /* FIXVARARGS */
/* make ps print "(sendmail)" */
p = Argv[0];
*p++ = '-';
i = strlen(buf);
if (i > LastArgv - p - 2)
{
i = LastArgv - p - 2;
buf[i] = '\0';
}
(void) strcpy(p, buf);
p += i;
while (p < LastArgv)
*p++ = ' ';
# endif SETPROCTITLE
}
/*
** REAPCHILD -- pick up the body of my child, lest it become a zombie
**
** Parameters:
** none.
**
** Returns:
** none.
**
** Side Effects:
** Picks up extant zombies.
*/
# ifdef VMUNIX
# include <sys/wait.h>
# endif VMUNIX
reapchild()
{
# ifdef WNOHANG
union wait status;
while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0)
continue;
# else WNOHANG
auto int status;
while (wait(&status) > 0)
continue;
# endif WNOHANG
}
=======================
err.c
=======================
/*
* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted provided
* that: (1) source distributions retain this entire copyright notice and
* comment, and (2) distributions including binaries display the following
* acknowledgement: ``This product includes software developed by the
* University of California, Berkeley and its contributors'' in the
* documentation or other materials provided with the distribution and in
* all advertising materials mentioning features or use of this software.
* 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static char sccsid[] = "@(#)err.c 5.10 (Berkeley) 6/1/90";
#endif /* not lint */
# include "sendmail.h"
# include <errno.h>
# include <netdb.h>
#ifdef FIXVARARGS
#include <varargs.h>
#endif /* FIXVARARGS */
/*
** SYSERR -- Print error message.
**
** Prints an error message via printf to the diagnostic
** output. If LOG is defined, it logs it also.
**
** Parameters:
** f -- the format string
** a, b, c, d, e -- parameters
**
** Returns:
** none
** Through TopFrame if QuickAbort is set.
**
** Side Effects:
** increments Errors.
** sets ExitStat.
*/
# ifdef lint
int sys_nerr;
char *sys_errlist[];
# endif lint
char MsgBuf[BUFSIZ*2]; /* text of most recent message */
#ifdef FIXVARARGS
syserr(fmt, va_alist)
char *fmt;
va_dcl
#else /* FIXVARARGS */
/*VARARGS1*/
syserr(fmt, a, b, c, d, e)
char *fmt;
#endif /* FIXVARARGS */
{
register char *p;
int olderrno = errno;
extern char Arpa_PSyserr[];
extern char Arpa_TSyserr[];
#ifdef FIXVARARGS
va_list ap;
va_start(ap);
#endif /* FIXVARARGS */
/* format and output the error message */
if (olderrno == 0)
p = Arpa_PSyserr;
else
p = Arpa_TSyserr;
#ifdef FIXVARARGS
fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
va_end(ap);
#else /* FIXVARARGS */
fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, a, b, c, d, e);
#endif /* FIXVARARGS */
puterrmsg(MsgBuf);
/* determine exit status if not already set */
if (ExitStat == EX_OK)
{
if (olderrno == 0)
ExitStat = EX_SOFTWARE;
else
ExitStat = EX_OSERR;
}
# ifdef LOG
if (LogLevel > 0)
syslog(LOG_CRIT, "%s: SYSERR: %s",
CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
&MsgBuf[4]);
# endif LOG
errno = 0;
if (QuickAbort)
longjmp(TopFrame, 2);
}
/*
** USRERR -- Signal user error.
**
** This is much like syserr except it is for user errors.
**
** Parameters:
** fmt, a, b, c, d -- printf strings
**
** Returns:
** none
** Through TopFrame if QuickAbort is set.
**
** Side Effects:
** increments Errors.
*/
#ifdef FIXVARARGS
usrerr(fmt, va_alist)
char *fmt;
va_dcl
#else /* FIXVARARGS */
/*VARARGS1*/
usrerr(fmt, a, b, c, d, e)
char *fmt;
#endif /* FIXVARARGS */
{
extern char SuprErrs;
extern char Arpa_Usrerr[];
extern int errno;
#ifdef FIXVARARGS
va_list ap;
va_start(ap);
#endif /* FIXVARARGS */
if (SuprErrs)
return;
#ifdef FIXVARARGS
fmtmsg(MsgBuf, CurEnv->e_to, Arpa_Usrerr, errno, fmt, ap);
va_end(ap);
#else /* FIXVARARGS */
fmtmsg(MsgBuf, CurEnv->e_to, Arpa_Usrerr, errno, fmt, a, b, c, d, e);
#endif /* FIXVARARGS */
puterrmsg(MsgBuf);
if (QuickAbort)
longjmp(TopFrame, 1);
}
/*
** MESSAGE -- print message (not necessarily an error)
**
** Parameters:
** num -- the default ARPANET error number (in ascii)
** msg -- the message (printf fmt) -- if it begins
** with a digit, this number overrides num.
** a, b, c, d, e -- printf arguments
**
** Returns:
** none
**
** Side Effects:
** none.
*/
#ifdef FIXVARARGS
message(num, msg, va_alist)
register char *num;
register char *msg;
va_dcl
#else /* FIXVARARGS */
/*VARARGS2*/
message(num, msg, a, b, c, d, e)
register char *num;
register char *msg;
#endif /* FIXVARARGS */
{
#ifdef FIXVARARGS
va_list ap;
va_start(ap);
#endif /* FIXVARARGS */
errno = 0;
#ifdef FIXVARARGS
fmtmsg(MsgBuf, CurEnv->e_to, num, 0, msg, ap);
va_end(ap);
#else /* FIXVARARGS */
fmtmsg(MsgBuf, CurEnv->e_to, num, 0, msg, a, b, c, d, e);
#endif /* FIXVARARGS */
putmsg(MsgBuf, FALSE);
}
/*
** NMESSAGE -- print message (not necessarily an error)
**
** Just like "message" except it never puts the to... tag on.
**
** Parameters:
** num -- the default ARPANET error number (in ascii)
** msg -- the message (printf fmt) -- if it begins
** with three digits, this number overrides num.
** a, b, c, d, e -- printf arguments
**
** Returns:
** none
**
** Side Effects:
** none.
*/
#ifdef FIXVARARGS
nmessage(num, msg, va_alist)
register char *num;
register char *msg;
va_dcl
#else /* FIXVARARGS */
/*VARARGS2*/
nmessage(num, msg, a, b, c, d, e)
register char *num;
register char *msg;
#endif /* FIXVARARGS */
{
#ifdef FIXVARARGS
va_list ap;
va_start(ap);
#endif /* FIXVARARGS */
errno = 0;
#ifdef FIXVARARGS
fmtmsg(MsgBuf, (char *) NULL, num, 0, msg, ap);
va_end(ap);
#else /* FIXVARARGS */
fmtmsg(MsgBuf, (char *) NULL, num, 0, msg, a, b, c, d, e);
#endif /* FIXVARARGS */
putmsg(MsgBuf, FALSE);
}
/*
** PUTMSG -- 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.
**
** Returns:
** none.
**
** Side Effects:
** Outputs msg to the transcript.
** If appropriate, outputs it to the channel.
** Deletes SMTP reply code number as appropriate.
*/
putmsg(msg, holdmsg)
char *msg;
bool holdmsg;
{
/* output to transcript if serious */
if (CurEnv->e_xfp != NULL && (msg[0] == '4' || msg[0] == '5'))
fprintf(CurEnv->e_xfp, "%s\n", msg);
/* output to channel if appropriate */
if (!holdmsg && (Verbose || msg[0] != '0'))
{
(void) fflush(stdout);
if (OpMode == MD_SMTP || OpMode == MD_ARPAFTP)
fprintf(OutChannel, "%s\r\n", msg);
else
fprintf(OutChannel, "%s\n", &msg[4]);
(void) fflush(OutChannel);
}
}
/*
** PUTERRMSG -- like putmsg, 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.
*/
puterrmsg(msg)
char *msg;
{
/* output the message as usual */
putmsg(msg, HoldErrs);
/* signal the error */
Errors++;
if (msg[0] == '5')
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.
*/
#ifdef FIXVARARGS
static
fmtmsg(eb, to, num, eno, fmt, ap)
register char *eb;
char *to;
char *num;
int eno;
char *fmt;
va_list ap;
#else /* FIXVARARGS */
/*VARARGS5*/
static
fmtmsg(eb, to, num, eno, fmt, a, b, c, d, e)
register char *eb;
char *to;
char *num;
int eno;
char *fmt;
#endif /* FIXVARARGS */
{
char del;
/* 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) sprintf(eb, "%3.3s%c", num, del);
eb += 4;
/* output the file name and line number */
if (FileName != NULL)
{
(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
eb += strlen(eb);
}
/* output the "to" person */
if (to != NULL && to[0] != '\0')
{
(void) sprintf(eb, "%s... ", to);
while (*eb != '\0')
*eb++ &= 0177;
}
/* output the message */
#ifdef FIXVARARGS
(void) vsprintf(eb, fmt, ap);
#else /* FIXVARARGS */
(void) sprintf(eb, fmt, a, b, c, d, e);
#endif /* FIXVARARGS */
while (*eb != '\0')
*eb++ &= 0177;
/* output the error code, if any */
if (eno != 0)
{
extern char *errstring();
(void) sprintf(eb, ": %s", errstring(eno));
eb += strlen(eb);
}
}
/*
** ERRSTRING -- return string description of error code
**
** Parameters:
** errno -- the error number to translate
**
** Returns:
** A string description of errno.
**
** Side Effects:
** none.
*/
char *
errstring(errno)
int errno;
{
extern char *sys_errlist[];
extern int sys_nerr;
static char buf[100];
# ifdef SMTP
extern char *SmtpPhase;
# endif SMTP
# ifdef DAEMON
# ifdef VMUNIX
/*
** Handle special network error codes.
**
** These are 4.2/4.3bsd specific; they should be in daemon.c.
*/
switch (errno)
{
case ETIMEDOUT:
case ECONNRESET:
(void) strcpy(buf, sys_errlist[errno]);
if (SmtpPhase != NULL)
{
(void) strcat(buf, " during ");
(void) strcat(buf, SmtpPhase);
}
if (CurHostName != NULL)
{
(void) strcat(buf, " with ");
(void) strcat(buf, CurHostName);
}
return (buf);
case EHOSTDOWN:
if (CurHostName == NULL)
break;
(void) sprintf(buf, "Host %s is down", CurHostName);
return (buf);
case ECONNREFUSED:
if (CurHostName == NULL)
break;
(void) sprintf(buf, "Connection refused by %s", CurHostName);
return (buf);
case (TRY_AGAIN+MAX_ERRNO):
(void) sprintf(buf, "Host Name Lookup Failure");
return (buf);
}
# endif VMUNIX
# endif DAEMON
if (errno > 0 && errno < sys_nerr)
return (sys_errlist[errno]);
(void) sprintf(buf, "Error %d", errno);
return (buf);
}
=======================
usersmtp.c
=======================
/*
* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted provided
* that: (1) source distributions retain this entire copyright notice and
* comment, and (2) distributions including binaries display the following
* acknowledgement: ``This product includes software developed by the
* University of California, Berkeley and its contributors'' in the
* documentation or other materials provided with the distribution and in
* all advertising materials mentioning features or use of this software.
* 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
# include "sendmail.h"
#ifndef lint
#ifdef SMTP
static char sccsid[] = "@(#)usersmtp.c 5.15 (Berkeley) 6/1/90 (with SMTP)";
#else
static char sccsid[] = "@(#)usersmtp.c 5.15 (Berkeley) 6/1/90 (without SMTP)";
#endif
#endif /* not lint */
# include <sysexits.h>
# include <errno.h>
#ifdef FIXVARARGS
#include <varargs.h>
#endif /* FIXVARARGS */
# ifdef 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 */
FILE *SmtpOut; /* output file */
FILE *SmtpIn; /* input file */
int SmtpPid; /* pid of mailer */
/* following represents the state of the SMTP connection */
int SmtpState; /* connection state, see below */
#define SMTP_CLOSED 0 /* connection is closed */
#define SMTP_OPEN 1 /* connection is open for business */
#define SMTP_SSD 2 /* service shutting down */
/*
** 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:
** appropriate exit status -- EX_OK on success.
** If not EX_OK, it should close the connection.
**
** Side Effects:
** creates connection and sends initial protocol.
*/
jmp_buf CtxGreeting;
smtpinit(m, pvp)
struct mailer *m;
char **pvp;
{
register int r;
EVENT *gte;
char buf[MAXNAME];
extern greettimeout();
/*
** Open the connection to the mailer.
*/
if (SmtpState == SMTP_OPEN)
syserr("smtpinit: already open");
SmtpIn = SmtpOut = NULL;
SmtpState = SMTP_CLOSED;
SmtpError[0] = '\0';
SmtpPhase = "user open";
setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
if (SmtpPid < 0)
{
if (tTd(18, 1))
printf("smtpinit: cannot open %s: stat %d errno %d\n",
pvp[0], ExitStat, errno);
if (CurEnv->e_xfp != NULL)
{
register char *p;
extern char *errstring();
extern char *statstring();
if (errno == 0)
{
p = statstring(ExitStat);
fprintf(CurEnv->e_xfp,
"%.3s %s.%s... %s\n",
p, pvp[1], m->m_name, p);
}
else
{
r = errno;
fprintf(CurEnv->e_xfp,
"421 %s.%s... Deferred: %s\n",
pvp[1], m->m_name, errstring(errno));
errno = r;
}
}
return (ExitStat);
}
SmtpState = SMTP_OPEN;
/*
** Get the greeting message.
** This should appear spontaneously. Give it five minutes to
** happen.
*/
if (setjmp(CtxGreeting) != 0)
goto tempfail;
gte = setevent((time_t) 300, greettimeout, 0);
SmtpPhase = "greeting wait";
setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
r = reply(m);
clrevent(gte);
if (r < 0 || REPLYTYPE(r) != 2)
goto tempfail;
/*
** Send the HELO command.
** My mother taught me to always introduce myself.
*/
smtpmessage("HELO %s", m, MyHostName);
SmtpPhase = "HELO wait";
setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
r = reply(m);
if (r < 0)
goto tempfail;
else if (REPLYTYPE(r) == 5)
goto unavailable;
else if (REPLYTYPE(r) != 2)
goto tempfail;
/*
** 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);
r = reply(m);
if (r < 0)
goto tempfail;
/* tell it we will be sending one transaction only */
smtpmessage("ONEX", m);
r = reply(m);
if (r < 0)
goto tempfail;
}
/*
** Send the MAIL command.
** Designates the sender.
*/
expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
if (CurEnv->e_from.q_mailer == LocalMailer ||
!bitnset(M_FROMPATH, m->m_flags))
{
smtpmessage("MAIL From:<%s>", m, buf);
}
else
{
smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
buf[0] == '@' ? ',' : ':', buf);
}
SmtpPhase = "MAIL wait";
setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
r = reply(m);
if (r < 0 || REPLYTYPE(r) == 4)
goto tempfail;
else if (r == 250)
return (EX_OK);
else if (r == 552)
goto unavailable;
/* protocol error -- close up */
smtpquit(m);
return (EX_PROTOCOL);
/* signal a temporary failure */
tempfail:
smtpquit(m);
return (EX_TEMPFAIL);
/* signal service unavailable */
unavailable:
smtpquit(m);
return (EX_UNAVAILABLE);
}
static
greettimeout()
{
/* timeout reading the greeting message */
longjmp(CtxGreeting, 1);
}
/*
** SMTPRCPT -- designate recipient.
**
** Parameters:
** to -- address of recipient.
** m -- the mailer we are sending to.
**
** Returns:
** exit status corresponding to recipient status.
**
** Side Effects:
** Sends the mail via SMTP.
*/
smtprcpt(to, m)
ADDRESS *to;
register MAILER *m;
{
register int r;
extern char *remotename();
smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
SmtpPhase = "RCPT wait";
setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
r = reply(m);
if (r < 0 || REPLYTYPE(r) == 4)
return (EX_TEMPFAIL);
else if (REPLYTYPE(r) == 2)
return (EX_OK);
else if (r == 550 || r == 551 || r == 553)
return (EX_NOUSER);
else if (r == 552 || r == 554)
return (EX_UNAVAILABLE);
return (EX_PROTOCOL);
}
/*
** SMTPDATA -- send the data and clean up the transaction.
**
** Parameters:
** m -- mailer being sent to.
** e -- the envelope for this message.
**
** Returns:
** exit status corresponding to DATA command.
**
** Side Effects:
** none.
*/
smtpdata(m, e)
struct mailer *m;
register ENVELOPE *e;
{
register int r;
/*
** 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);
SmtpPhase = "DATA wait";
setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
r = reply(m);
if (r < 0 || REPLYTYPE(r) == 4)
return (EX_TEMPFAIL);
else if (r == 554)
return (EX_UNAVAILABLE);
else if (r != 354)
return (EX_PROTOCOL);
/* now output the actual message */
(*e->e_puthdr)(SmtpOut, m, CurEnv);
putline("\n", SmtpOut, m);
(*e->e_putbody)(SmtpOut, m, CurEnv);
/* terminate the message */
fprintf(SmtpOut, ".%s", m->m_eol);
if (Verbose && !HoldErrs)
nmessage(Arpa_Info, ">>> .");
/* check for the results of the transaction */
SmtpPhase = "result wait";
setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
r = reply(m);
if (r < 0 || REPLYTYPE(r) == 4)
return (EX_TEMPFAIL);
else if (r == 250)
return (EX_OK);
else if (r == 552 || r == 554)
return (EX_UNAVAILABLE);
return (EX_PROTOCOL);
}
/*
** SMTPQUIT -- close the SMTP connection.
**
** Parameters:
** m -- a pointer to the mailer.
**
** Returns:
** none.
**
** Side Effects:
** sends the final protocol and closes the connection.
*/
smtpquit(m)
register MAILER *m;
{
int i;
/* if the connection is already closed, don't bother */
if (SmtpIn == NULL)
return;
/* send the quit message if not a forced quit */
if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
{
smtpmessage("QUIT", m);
(void) reply(m);
if (SmtpState == SMTP_CLOSED)
return;
}
/* now actually close the connection */
(void) fclose(SmtpIn);
(void) fclose(SmtpOut);
SmtpIn = SmtpOut = NULL;
SmtpState = SMTP_CLOSED;
/* and pick up the zombie */
i = endmailer(SmtpPid, m->m_argv[0]);
if (i != EX_OK)
syserr("smtpquit %s: stat %d", m->m_argv[0], i);
}
/*
** REPLY -- read arpanet reply
**
** Parameters:
** m -- the mailer we are reading the reply from.
**
** Returns:
** reply code it reads.
**
** Side Effects:
** flushes the mail file.
*/
reply(m)
MAILER *m;
{
(void) fflush(SmtpOut);
if (tTd(18, 1))
printf("reply\n");
/*
** Read the input line, being careful not to hang.
*/
for (;;)
{
register int r;
register char *p;
/* actually do the read */
if (CurEnv->e_xfp != NULL)
(void) fflush(CurEnv->e_xfp); /* for debugging */
/* if we are in the process of closing just give the code */
if (SmtpState == SMTP_CLOSED)
return (SMTPCLOSING);
/* get the line from the other side */
p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
if (p == NULL)
{
extern char MsgBuf[]; /* err.c */
extern char Arpa_TSyserr[]; /* conf.c */
/* if the remote end closed early, fake an error */
if (errno == 0)
# ifdef ECONNRESET
errno = ECONNRESET;
# else ECONNRESET
errno = EPIPE;
# endif ECONNRESET
message(Arpa_TSyserr, "reply: read error");
/* if debugging, pause so we can see state */
if (tTd(18, 100))
pause();
# ifdef LOG
syslog(LOG_INFO, "%s", &MsgBuf[4]);
# endif LOG
SmtpState = SMTP_CLOSED;
smtpquit(m);
return (-1);
}
fixcrlf(SmtpReplyBuffer, TRUE);
if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
{
/* serious error -- log the previous command */
if (SmtpMsgBuffer[0] != '\0')
fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
SmtpMsgBuffer[0] = '\0';
/* now log the message as from the other side */
fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
}
/* display the input for verbose mode */
if (Verbose && !HoldErrs)
nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
/* if continuation is required, we can go on */
if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
continue;
/* decode the reply code */
r = atoi(SmtpReplyBuffer);
/* extra semantics: 0xx codes are "informational" */
if (r < 100)
continue;
/* reply code 421 is "Service Shutting Down" */
if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
{
/* send the quit protocol */
SmtpState = SMTP_SSD;
smtpquit(m);
}
/* save temporary failure messages for posterity */
if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
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 SmtpOut.
*/
#ifdef FIXVARARGS
smtpmessage(f, m, va_alist)
char *f;
MAILER *m;
va_dcl
#else /* FIXVARARGS */
/*VARARGS1*/
smtpmessage(f, m, a, b, c)
char *f;
MAILER *m;
#endif /* FIXVARARGS */
{
#ifdef FIXVARARGS
va_list ap;
va_start(ap);
(void) vsprintf(SmtpMsgBuffer, f, ap);
va_end(ap);
#else /* FIXVARARGS */
(void) sprintf(SmtpMsgBuffer, f, a, b, c);
#endif /* FIXVARARGS */
if (tTd(18, 1) || (Verbose && !HoldErrs))
nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
if (SmtpOut != NULL)
fprintf(SmtpOut, "%s%s", SmtpMsgBuffer,
m == 0 ? "\r\n" : m->m_eol);
}
# endif SMTP