📄 deliver.c
字号:
/* run disconnected from terminal */
(void) setsid();
/* try to execute the mailer */
(void) execve(m->m_mailer, (ARGV_T) pv,
(ARGV_T) UserEnviron);
save_errno = errno;
syserr("Cannot exec %s", m->m_mailer);
if (bitnset(M_LOCALMAILER, m->m_flags) ||
transienterror(save_errno))
_exit(EX_OSERR);
_exit(EX_UNAVAILABLE);
}
/*
** Set up return value.
*/
if (mci == NULL)
{
mci = (MCI *) xalloc(sizeof *mci);
memset((char *) mci, '\0', sizeof *mci);
}
mci->mci_mailer = m;
if (clever)
{
mci->mci_state = MCIS_OPENING;
mci_cache(mci);
}
else
{
mci->mci_state = MCIS_OPEN;
}
mci->mci_pid = pid;
(void) close(mpvect[0]);
mci->mci_out = fdopen(mpvect[1], "w");
if (mci->mci_out == NULL)
{
syserr("deliver: cannot create mailer output channel, fd=%d",
mpvect[1]);
(void) close(mpvect[1]);
(void) close(rpvect[0]);
(void) close(rpvect[1]);
rcode = EX_OSERR;
goto give_up;
}
(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]);
(void) fclose(mci->mci_out);
mci->mci_out = NULL;
rcode = EX_OSERR;
goto give_up;
}
/* Don't cache non-clever connections */
if (!clever)
mci->mci_flags |= MCIF_TEMP;
}
/*
** 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)
{
static u_short again;
# if SASL && SFIO
# define DONE_TLS_B 0x01
# define DONE_TLS bitset(DONE_TLS_B, again)
# endif /* SASL && SFIO */
# if STARTTLS
# define DONE_STARTTLS_B 0x02
# define DONE_STARTTLS bitset(DONE_STARTTLS_B, again)
# endif /* STARTTLS */
# define ONLY_HELO_B 0x04
# define ONLY_HELO bitset(ONLY_HELO_B, again)
# define SET_HELO again |= ONLY_HELO_B
# define CLR_HELO again &= ~ONLY_HELO_B
again = 0;
# if STARTTLS || (SASL && SFIO)
reconnect: /* after switching to an authenticated connection */
# endif /* STARTTLS || (SASL && SFIO) */
# if SASL
mci->mci_saslcap = NULL;
# endif /* SASL */
smtpinit(m, mci, e, ONLY_HELO);
CLR_HELO;
# if STARTTLS
/* first TLS then AUTH to provide a security layer */
if (mci->mci_state != MCIS_CLOSED && !DONE_STARTTLS)
{
int olderrors;
bool hasdot;
bool usetls;
bool saveQuickAbort = QuickAbort;
bool saveSuprErrs = SuprErrs;
# if _FFR_TLS_CLT1
char *p;
# endif /* _FFR_TLS_CLT1 */
extern SOCKADDR CurHostAddr;
rcode = EX_OK;
usetls = bitset(MCIF_TLS, mci->mci_flags);
# if _FFR_TLS_CLT1
if (usetls &&
(p = macvalue(macid("{client_flags}", NULL), e))
!= NULL)
{
for (; *p != '\0'; p++)
{
/* look for just this one flag */
if (*p == D_CLTNOTLS)
{
usetls = FALSE;
break;
}
}
}
# endif /* _FFR_TLS_CLT1 */
hasdot = CurHostName[strlen(CurHostName) - 1] == '.';
if (hasdot)
CurHostName[strlen(CurHostName) - 1] = '\0';
define(macid("{server_name}", NULL),
newstr(CurHostName), e);
if (CurHostAddr.sa.sa_family != 0)
define(macid("{server_addr}", NULL),
newstr(anynet_ntoa(&CurHostAddr)), e);
else
define(macid("{server_addr}", NULL), NULL, e);
# if _FFR_TLS_O_T
if (usetls)
{
olderrors = Errors;
QuickAbort = FALSE;
SuprErrs = TRUE;
if (rscheck("try_tls", CurHostName, NULL,
e, TRUE, FALSE, 8) != EX_OK
|| Errors > olderrors)
usetls = FALSE;
SuprErrs = saveSuprErrs;
QuickAbort = saveQuickAbort;
}
# endif /* _FFR_TLS_O_T */
/* undo change of CurHostName */
if (hasdot)
CurHostName[strlen(CurHostName)] = '.';
if (usetls)
{
if ((rcode = starttls(m, mci, e)) == EX_OK)
{
/* start again without STARTTLS */
again |= DONE_STARTTLS_B;
mci->mci_flags |= MCIF_TLSACT;
}
else
{
char *s;
/*
** TLS negotation failed, what to do?
** fall back to unencrypted connection
** or abort? How to decide?
** set a macro and call a ruleset.
*/
mci->mci_flags &= ~MCIF_TLS;
switch (rcode)
{
case EX_TEMPFAIL:
s = "TEMP";
break;
case EX_USAGE:
s = "USAGE";
break;
case EX_PROTOCOL:
s = "PROTOCOL";
break;
case EX_SOFTWARE:
s = "SOFTWARE";
break;
/* everything else is a failure */
default:
s = "FAILURE";
rcode = EX_TEMPFAIL;
}
define(macid("{verify}", NULL),
newstr(s), e);
}
}
else
define(macid("{verify}", NULL), "NONE", e);
olderrors = Errors;
QuickAbort = FALSE;
SuprErrs = TRUE;
/*
** rcode == EX_SOFTWARE is special:
** the TLS negotation failed
** we have to drop the connection no matter what
** However, we call tls_server to give it the chance
** to log the problem and return an appropriate
** error code.
*/
if (rscheck("tls_server",
macvalue(macid("{verify}", NULL), e),
NULL, e, TRUE, TRUE, 6) != EX_OK ||
Errors > olderrors ||
rcode == EX_SOFTWARE)
{
char enhsc[ENHSCLEN];
extern char MsgBuf[];
if (ISSMTPCODE(MsgBuf) &&
extenhsc(MsgBuf + 4, ' ', enhsc) > 0)
{
p = newstr(MsgBuf);
}
else
{
p = "403 4.7.0 server not authenticated.";
(void) strlcpy(enhsc, "4.7.0",
sizeof enhsc);
}
SuprErrs = saveSuprErrs;
QuickAbort = saveQuickAbort;
if (rcode == EX_SOFTWARE)
{
/* drop the connection */
mci->mci_state = MCIS_QUITING;
if (mci->mci_in != NULL)
{
(void) fclose(mci->mci_in);
mci->mci_in = NULL;
}
mci->mci_flags &= ~MCIF_TLSACT;
(void) endmailer(mci, e, pv);
}
else
{
/* abort transfer */
smtpquit(m, mci, e);
}
/* temp or permanent failure? */
rcode = (*p == '4') ? EX_TEMPFAIL
: EX_UNAVAILABLE;
mci_setstat(mci, rcode, newstr(enhsc), p);
/*
** hack to get the error message into
** the envelope (done in giveresponse())
*/
(void) strlcpy(SmtpError, p, sizeof SmtpError);
}
QuickAbort = saveQuickAbort;
SuprErrs = saveSuprErrs;
if (DONE_STARTTLS && mci->mci_state != MCIS_CLOSED)
{
SET_HELO;
mci->mci_flags &= ~MCIF_EXTENS;
goto reconnect;
}
}
# endif /* STARTTLS */
# if SASL
/* if other server supports authentication let's authenticate */
if (mci->mci_state != MCIS_CLOSED &&
mci->mci_saslcap != NULL &&
# if SFIO
!DONE_TLS &&
# endif /* SFIO */
SASLInfo != NULL)
{
/*
** should we require some minimum authentication?
** XXX ignore result?
*/
if (smtpauth(m, mci, e) == EX_OK)
{
# if SFIO
int result;
sasl_ssf_t *ssf;
/* get security strength (features) */
result = sasl_getprop(mci->mci_conn, SASL_SSF,
(void **) &ssf);
if (LogLevel > 9)
sm_syslog(LOG_INFO, NOQID,
"SASL: outgoing connection to %.64s: mech=%.16s, bits=%d",
mci->mci_host,
macvalue(macid("{auth_type}",
NULL), e),
*ssf);
/*
** only switch to encrypted connection
** if a security layer has been negotiated
*/
if (result == SASL_OK && *ssf > 0)
{
/*
** convert sfio stuff to use SASL
** check return values
** if the call fails,
** fall back to unencrypted version
** unless some cf option requires
** encryption then the connection must
** be aborted
*/
if (sfdcsasl(mci->mci_in, mci->mci_out,
mci->mci_conn) == 0)
{
again |= DONE_TLS_B;
SET_HELO;
mci->mci_flags &= ~MCIF_EXTENS;
mci->mci_flags |= MCIF_AUTHACT;
goto reconnect;
}
syserr("SASL TLS switch failed in client");
}
/* else? XXX */
# endif /* SFIO */
mci->mci_flags |= MCIF_AUTHACT;
}
}
# endif /* SASL */
}
#endif /* SMTP */
do_transfer:
/* clear out per-message flags from connection structure */
mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
if (bitset(EF_HAS8BIT, e->e_flags) &&
!bitset(EF_DONT_MIME, e->e_flags) &&
bitnset(M_7BITS, m->m_flags))
mci->mci_flags |= MCIF_CVT8TO7;
#if MIME7TO8
if (bitnset(M_MAKE8BIT, m->m_flags) &&
!bitset(MCIF_7BIT, mci->mci_flags) &&
(p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
(strcasecmp(p, "quoted-printable") == 0 ||
strcasecmp(p, "base64") == 0) &&
(p = hvalue("Content-Type", e->e_header)) != NULL)
{
/* may want to convert 7 -> 8 */
/* XXX should really parse it here -- and use a class XXX */
if (strncasecmp(p, "text/plain", 10) == 0 &&
(p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
mci->mci_flags |= MCIF_CVT7TO8;
}
#endif /* MIME7TO8 */
if (tTd(11, 1))
{
dprintf("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 /* NAMED_BIND */
if (rcode == EX_OK)
{
/* shouldn't happen */
syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
(u_long) mci, rcode, errno, mci->mci_state,
firstsig);
mci_dump_all(TRUE);
rcode = EX_SOFTWARE;
}
#if DAEMON
else if (nummxhosts > hostnum)
{
/* try next MX site */
goto tryhost;
}
#endif /* DAEMON */
}
else if (!clever)
{
/*
** Format and send message.
*/
putfromline(mci, e);
(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
(*e->e_putbody)(mci, e, NULL);
/* get the exit status */
rcode = endmailer(mci, e, pv);
}
else
#if SMTP
{
/*
** 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 !_FFR_DYNAMIC_TOBUF
if (strlen(to->q_paddr) +
(t - tobuf) + 2 > sizeof tobuf)
{
/* not enough room */
continue;
}
#endif /* !_FFR_DYNAMIC_TOBUF */
# if STARTTLS
# if _FFR_TLS_RCPT
i = rscheck("tls_rcpt", to->q_user, NULL, e,
TRUE, TRUE, 4);
if (i != EX_OK)
{
markfailure(e, to, mci, i, FALSE);
giveresponse(i, to->q_status, m,
mci, ctladdr, xstart, e);
continue;
}
# endif /* _FFR_TLS_RCPT */
# endif /* STARTTLS */
if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
{
markfailure(e, to, mci, i, FALSE);
giveresponse(i, to->q_status, 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 && nummxhosts > hostnum)
{
/* try next MX site */
goto tryhost;
}
# endif /* DAEMON */
}
#else /* SMTP */
{
syserr("554 5.3.5 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 /* NAMED_BIND */
if (tTd(62, 1))
checkfds("after delivery");
/*
** Do final status disposal.
** We check for something in tobuf for the SMTP case.
** If we got a temporary failure, arrange to queue the
** addressees.
*/
give_up:
#if SMTP
if (bitnset(M_LMTP, m->m_flags))
{
lmtp_rcode = rcode;
tobuf[0] = '\0';
anyok = FALSE;
}
else
#endif /* SMTP */
anyok = rcode == EX_OK;
for (to = tochain; to != NULL; to = to->q_tchain)
{
/* see if address already marked */
if (!QS_IS_OK(to->q_state))
continue;
#if SMTP
/* if running LMTP, get the status fo
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -