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