📄 usersmtp.c
字号:
** If putbody() couldn't finish due to a timeout,
** rewind it here in the timeout handler. See
** comments at the end of putbody() for reasoning.
*/
if (e->e_dfp != NULL)
(void) bfrewind(e->e_dfp);
errno = mci->mci_errno;
syserr("451 4.4.1 timeout writing message to %s", CurHostName);
smtpquit(m, mci, e);
return EX_TEMPFAIL;
}
if (tTd(18, 101))
{
/* simulate a DATA timeout */
timeout = 1;
}
else
timeout = DATA_PROGRESS_TIMEOUT;
ev = setevent(timeout, datatimeout, 0);
if (tTd(18, 101))
{
/* simulate a DATA timeout */
(void) sleep(1);
}
/*
** Output the actual message.
*/
(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
(*e->e_putbody)(mci, e, NULL);
/*
** Cleanup after sending message.
*/
clrevent(ev);
# if _FFR_CATCH_BROKEN_MTAS
{
fd_set readfds;
struct timeval timeout;
FD_ZERO(&readfds);
FD_SET(fileno(mci->mci_in), &readfds);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if (select(fileno(mci->mci_in) + 1, FDSET_CAST &readfds,
NULL, NULL, &timeout) > 0 &&
FD_ISSET(fileno(mci->mci_in), &readfds))
{
/* terminate the message */
fprintf(mci->mci_out, ".%s", m->m_eol);
if (TrafficLogFile != NULL)
fprintf(TrafficLogFile, "%05d >>> .\n",
(int) getpid());
if (Verbose)
nmessage(">>> .");
mci->mci_errno = EIO;
mci->mci_state = MCIS_ERROR;
mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
smtpquit(m, mci, e);
return EX_PROTOCOL;
}
}
# endif /* _FFR_CATCH_BROKEN_MTAS */
if (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";
sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
CurHostName, mci->mci_phase);
if (bitnset(M_LMTP, m->m_flags))
return EX_OK;
r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
if (r < 0)
{
smtpquit(m, mci, e);
return EX_TEMPFAIL;
}
mci->mci_state = MCIS_OPEN;
xstat = EX_NOTSTICKY;
if (r == 452)
rstat = EX_TEMPFAIL;
else if (REPLYTYPE(r) == 4)
rstat = xstat = EX_TEMPFAIL;
else if (REPLYCLASS(r) != 5)
rstat = xstat = EX_PROTOCOL;
else if (REPLYTYPE(r) == 2)
rstat = xstat = EX_OK;
else if (REPLYTYPE(r) == 5)
rstat = EX_UNAVAILABLE;
else
rstat = EX_PROTOCOL;
mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
SmtpReplyBuffer);
if (e->e_statmsg != NULL)
free(e->e_statmsg);
if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
(r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
r += 5;
else
r = 4;
e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
if (rstat != EX_PROTOCOL)
return rstat;
if (LogLevel > 1)
{
sm_syslog(LOG_CRIT, e->e_id,
"%.100s: SMTP DATA-2 protocol error: %s",
CurHostName,
shortenstring(SmtpReplyBuffer, 403));
}
return rstat;
}
static void
datatimeout()
{
if (DataProgress)
{
time_t timeout;
register EVENT *ev;
/* check back again later */
if (tTd(18, 101))
{
/* simulate a DATA timeout */
timeout = 1;
}
else
timeout = DATA_PROGRESS_TIMEOUT;
DataProgress = FALSE;
ev = setevent(timeout, datatimeout, 0);
}
else
{
/* no progress, give up */
longjmp(CtxDataTimeout, 1);
}
}
/*
** SMTPGETSTAT -- get status code from DATA in LMTP
**
** Parameters:
** m -- the mailer to which we are sending the message.
** mci -- the mailer connection structure.
** e -- the current envelope.
**
** Returns:
** The exit status corresponding to the reply code.
*/
int
smtpgetstat(m, mci, e)
MAILER *m;
MCI *mci;
ENVELOPE *e;
{
int r;
int status;
char *enhsc;
enhsc = NULL;
/* check for the results of the transaction */
r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
if (r < 0)
{
smtpquit(m, mci, e);
return EX_TEMPFAIL;
}
if (REPLYTYPE(r) == 4)
status = EX_TEMPFAIL;
else if (REPLYCLASS(r) != 5)
status = EX_PROTOCOL;
else if (REPLYTYPE(r) == 2)
status = EX_OK;
else if (REPLYTYPE(r) == 5)
status = EX_UNAVAILABLE;
else
status = EX_PROTOCOL;
if (e->e_statmsg != NULL)
free(e->e_statmsg);
if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
(r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
r += 5;
else
r = 4;
e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
mci_setstat(mci, status, ENHSCN(enhsc, smtptodsn(r)),
SmtpReplyBuffer);
if (LogLevel > 1 && status == EX_PROTOCOL)
{
sm_syslog(LOG_CRIT, e->e_id,
"%.100s: SMTP DATA-3 protocol error: %s",
CurHostName,
shortenstring(SmtpReplyBuffer, 403));
}
return status;
}
/*
** SMTPQUIT -- close the SMTP connection.
**
** Parameters:
** m -- a pointer to the mailer.
** mci -- the mailer connection information.
** e -- the current envelope.
**
** Returns:
** none.
**
** Side Effects:
** sends the final protocol and closes the connection.
*/
void
smtpquit(m, mci, e)
register MAILER *m;
register MCI *mci;
ENVELOPE *e;
{
bool oldSuprErrs = SuprErrs;
int rcode;
CurHostName = mci->mci_host; /* XXX UGLY XXX */
if (CurHostName == NULL)
CurHostName = MyHostName;
/*
** Suppress errors here -- we may be processing a different
** job when we do the quit connection, and we don't want the
** new job to be penalized for something that isn't it's
** problem.
*/
SuprErrs = TRUE;
/* send the quit message if we haven't gotten I/O error */
if (mci->mci_state != MCIS_ERROR &&
mci->mci_state != MCIS_QUITING)
{
int origstate = mci->mci_state;
SmtpPhase = "client QUIT";
mci->mci_state = MCIS_QUITING;
smtpmessage("QUIT", m, mci);
(void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL);
SuprErrs = oldSuprErrs;
if (mci->mci_state == MCIS_CLOSED ||
origstate == MCIS_CLOSED)
return;
}
/* now actually close the connection and pick up the zombie */
rcode = endmailer(mci, e, NULL);
if (rcode != EX_OK)
{
char *mailer = NULL;
if (mci->mci_mailer != NULL &&
mci->mci_mailer->m_name != NULL)
mailer = mci->mci_mailer->m_name;
/* look for naughty mailers */
sm_syslog(LOG_ERR, e->e_id,
"smtpquit: mailer%s%s exited with exit value %d\n",
mailer == NULL ? "" : " ",
mailer == NULL ? "" : mailer,
rcode);
}
SuprErrs = oldSuprErrs;
}
/*
** SMTPRSET -- send a RSET (reset) command
*/
void
smtprset(m, mci, e)
register MAILER *m;
register MCI *mci;
ENVELOPE *e;
{
int r;
CurHostName = mci->mci_host; /* XXX UGLY XXX */
if (CurHostName == NULL)
CurHostName = MyHostName;
SmtpPhase = "client RSET";
smtpmessage("RSET", m, mci);
r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL);
if (r < 0)
mci->mci_state = MCIS_ERROR;
else
{
/*
** Any response is deemed to be acceptable.
** The standard does not state the proper action
** to take when a value other than 250 is received.
*/
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;
ENVELOPE *e;
extern ENVELOPE BlankEnvelope;
CurHostName = mci->mci_host; /* XXX UGLY XXX */
if (CurHostName == NULL)
CurHostName = MyHostName;
e = &BlankEnvelope;
SmtpPhase = "client probe";
smtpmessage("RSET", m, mci);
r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL);
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, enhstat)
MAILER *m;
MCI *mci;
ENVELOPE *e;
time_t timeout;
void (*pfunc)();
char **enhstat;
{
register char *bufp;
register int r;
bool firstline = TRUE;
char junkbuf[MAXLINE];
static char enhstatcode[ENHSCLEN];
int save_errno;
if (mci->mci_out != NULL)
(void) fflush(mci->mci_out);
if (tTd(18, 1))
dprintf("reply\n");
/*
** Read the input line, being careful not to hang.
*/
bufp = SmtpReplyBuffer;
for (;;)
{
register char *p;
/* actually do the read */
if (e->e_xfp != NULL)
(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)
(void) 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 4.4.1 reply: read error from %s",
CurHostName == NULL ? "NO_HOST" : CurHostName);
/* errors on QUIT should not be persistent */
if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0)
mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
/* if debugging, pause so we can see state */
if (tTd(18, 100))
(void) pause();
mci->mci_state = MCIS_ERROR;
save_errno = errno;
smtpquit(m, mci, e);
# if XDEBUG
{
char wbuf[MAXLINE];
int wbufleft = sizeof wbuf;
p = wbuf;
if (e->e_to != NULL)
{
int plen;
snprintf(p, wbufleft, "%s... ",
shortenstring(e->e_to, MAXSHORTSTR));
plen = strlen(p);
p += plen;
wbufleft -= plen;
}
snprintf(p, wbufleft, "reply(%.100s) during %s",
CurHostName == NULL ? "NO_HOST" : CurHostName,
SmtpPhase);
checkfd012(wbuf);
}
# endif /* XDEBUG */
errno = save_errno;
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 == NULL ? "NO_HOST" : 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 formatted input */
if (!ISSMTPREPLY(bufp))
continue;
if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
enhstat != NULL &&
extenhsc(bufp + 4, ' ', enhstatcode) > 0)
*enhstat = enhstatcode;
/* process the line */
if (pfunc != NULL)
(*pfunc)(bufp, firstline, m, mci, e);
firstline = FALSE;
/* decode the reply code */
r = atoi(bufp);
/* extra semantics: 0xx codes are "informational" */
if (r < 100)
continue;
/* if no continuation lines, return this line */
if (bufp[3] != '-')
break;
/* first line of real reply -- ignore rest */
bufp = junkbuf;
}
/*
** Now look at SmtpReplyBuffer -- only care about the first
** line of the response from here on out.
*/
/* save temporary failure messages for posterity */
if (SmtpReplyBuffer[0] == '4' &&
(bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0'))
snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer);
/* reply code 421 is "Service Shutting Down" */
if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
{
/* send the quit protocol */
mci->mci_state = MCIS_SSD;
smtpquit(m, mci, e);
}
return r;
}
/*
** SMTPMESSAGE -- send message to server
**
** Parameters:
** f -- format
** m -- the mailer to control formatting.
** a, b, c -- parameters
**
** Returns:
** none.
**
** Side Effects:
** writes message to mci->mci_out.
*/
/*VARARGS1*/
void
# ifdef __STDC__
smtpmessage(char *f, MAILER *m, MCI *mci, ...)
# else /* __STDC__ */
smtpmessage(f, m, mci, va_alist)
char *f;
MAILER *m;
MCI *mci;
va_dcl
# endif /* __STDC__ */
{
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))
{
dprintf("smtpmessage: NULL mci_out\n");
}
}
#endif /* SMTP */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -