summaryrefslogtreecommitdiff
path: root/contrib/sendmail/src/deliver.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/src/deliver.c')
-rw-r--r--contrib/sendmail/src/deliver.c299
1 files changed, 121 insertions, 178 deletions
diff --git a/contrib/sendmail/src/deliver.c b/contrib/sendmail/src/deliver.c
index a6b67c8acc18..045804779c0e 100644
--- a/contrib/sendmail/src/deliver.c
+++ b/contrib/sendmail/src/deliver.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2006 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -12,9 +12,9 @@
*/
#include <sendmail.h>
-#include <sys/time.h>
+#include <sm/time.h>
-SM_RCSID("@(#)$Id: deliver.c,v 8.986 2005/03/05 02:28:50 ca Exp $")
+SM_RCSID("@(#)$Id: deliver.c,v 8.1000 2006/03/02 01:37:39 ca Exp $")
#if HASSETUSERCONTEXT
# include <login_cap.h>
@@ -1201,13 +1201,13 @@ should_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status)
int status;
{
/*
- ** If the host was not found and a FallbackSmartHost is defined
- ** (and we have not yet tried it), then make one last try with
- ** it as the host.
+ ** If the host was not found or a temporary failure occurred
+ ** and a FallbackSmartHost is defined (and we have not yet
+ ** tried it), then make one last try with it as the host.
*/
- if (status == EX_NOHOST && FallbackSmartHost != NULL &&
- !*tried_fallbacksmarthost)
+ if ((status == EX_NOHOST || status == EX_TEMPFAIL) &&
+ FallbackSmartHost != NULL && !*tried_fallbacksmarthost)
{
*tried_fallbacksmarthost = true;
expand(FallbackSmartHost, hostbuf, hbsz, e);
@@ -2992,6 +2992,9 @@ reconnect: /* after switching to an encrypted connection */
case EX_SOFTWARE:
s = "SOFTWARE";
break;
+ case EX_UNAVAILABLE:
+ s = "NONE";
+ break;
/* everything else is a failure */
default:
@@ -3257,16 +3260,33 @@ do_transfer:
}
else if (!clever)
{
+ bool ok;
+
/*
** Format and send message.
*/
- putfromline(mci, e);
- (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
- (*e->e_putbody)(mci, e, NULL);
+ rcode = EX_OK;
+ errno = 0;
+ ok = putfromline(mci, e);
+ if (ok)
+ ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
+ if (ok)
+ ok = (*e->e_putbody)(mci, e, NULL);
+
+ /*
+ ** Ignore an I/O error that was caused by EPIPE.
+ ** Some broken mailers don't read the entire body
+ ** but just exit() thus causing an I/O error.
+ */
+
+ if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE))
+ ok = true;
- /* get the exit status */
+ /* (always) get the exit status */
rcode = endmailer(mci, e, pv);
+ if (!ok)
+ rcode = EX_TEMPFAIL;
if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0')
{
/*
@@ -4430,13 +4450,13 @@ logdelivery(m, mci, dsn, status, ctladdr, xstart, e)
** e -- the envelope.
**
** Returns:
-** none
+** true iff line was written successfully
**
** Side Effects:
** outputs some text to fp.
*/
-void
+bool
putfromline(mci, e)
register MCI *mci;
ENVELOPE *e;
@@ -4446,7 +4466,7 @@ putfromline(mci, e)
char xbuf[MAXLINE];
if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
- return;
+ return true;
mci->mci_flags |= MCIF_INHEADER;
@@ -4487,8 +4507,9 @@ putfromline(mci, e)
}
}
expand(template, buf, sizeof buf, e);
- putxline(buf, strlen(buf), mci, PXLF_HEADER);
+ return putxline(buf, strlen(buf), mci, PXLF_HEADER);
}
+
/*
** PUTBODY -- put the body of a message.
**
@@ -4499,24 +4520,26 @@ putfromline(mci, e)
** not be permitted in the resulting message.
**
** Returns:
-** none.
+** true iff message was written successfully
**
** 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 */
+#define OSTATE_HEAD 0 /* at beginning of line */
+#define OSTATE_CR 1 /* read a carriage return */
+#define OSTATE_INLINE 2 /* putting rest of line */
-void
+bool
putbody(mci, e, separator)
register MCI *mci;
register ENVELOPE *e;
char *separator;
{
bool dead = false;
+ bool ioerr = false;
+ int save_errno;
char buf[MAXLINE];
#if MIME8TO7
char *boundaries[MAXMIMENESTING + 1];
@@ -4546,10 +4569,12 @@ putbody(mci, e, separator)
{
if (bitset(MCIF_INHEADER, mci->mci_flags))
{
- putline("", mci);
+ if (!putline("", mci))
+ goto writeerr;
mci->mci_flags &= ~MCIF_INHEADER;
}
- putline("<<< No Message Collected >>>", mci);
+ if (!putline("<<< No Message Collected >>>", mci))
+ goto writeerr;
goto endofmessage;
}
@@ -4570,6 +4595,10 @@ putbody(mci, e, separator)
/* paranoia: the data file should always be in a rewound state */
(void) bfrewind(e->e_dfp);
+ /* simulate an I/O timeout when used as source */
+ if (tTd(84, 101))
+ sleep(319);
+
#if MIME8TO7
if (bitset(MCIF_CVT8TO7, mci->mci_flags))
{
@@ -4578,26 +4607,31 @@ putbody(mci, e, separator)
*/
/* make sure it looks like a MIME message */
- if (hvalue("MIME-Version", e->e_header) == NULL)
- putline("MIME-Version: 1.0", mci);
+ if (hvalue("MIME-Version", e->e_header) == NULL &&
+ !putline("MIME-Version: 1.0", mci))
+ goto writeerr;
if (hvalue("Content-Type", e->e_header) == NULL)
{
(void) sm_snprintf(buf, sizeof buf,
"Content-Type: text/plain; charset=%s",
defcharset(e));
- putline(buf, mci);
+ if (!putline(buf, mci))
+ goto writeerr;
}
/* now do the hard work */
boundaries[0] = NULL;
mci->mci_flags |= MCIF_INHEADER;
- (void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
+ if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER) ==
+ SM_IO_EOF)
+ goto writeerr;
}
# if MIME7TO8
else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
{
- (void) mime7to8(mci, e->e_header, e);
+ if (!mime7to8(mci, e->e_header, e))
+ goto writeerr;
}
# endif /* MIME7TO8 */
else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0)
@@ -4619,8 +4653,9 @@ putbody(mci, e, separator)
if (bitset(EF_DONT_MIME, e->e_flags))
SuprErrs = true;
- (void) mime8to7(mci, e->e_header, e, boundaries,
- M87F_OUTER|M87F_NO8TO7);
+ if (mime8to7(mci, e->e_header, e, boundaries,
+ M87F_OUTER|M87F_NO8TO7) == SM_IO_EOF)
+ goto writeerr;
/* restore SuprErrs */
SuprErrs = oldsuprerrs;
@@ -4640,7 +4675,8 @@ putbody(mci, e, separator)
if (bitset(MCIF_INHEADER, mci->mci_flags))
{
- putline("", mci);
+ if (!putline("", mci))
+ goto writeerr;
mci->mci_flags &= ~MCIF_INHEADER;
}
@@ -4651,7 +4687,7 @@ putbody(mci, e, separator)
buflim = &buf[mci->mci_mailer->m_linelimit - 1];
/* copy temp file to output with mapping */
- ostate = OS_HEAD;
+ ostate = OSTATE_HEAD;
bp = buf;
pbp = peekbuf;
while (!sm_io_error(mci->mci_out) && !dead)
@@ -4665,7 +4701,7 @@ putbody(mci, e, separator)
c &= 0x7f;
switch (ostate)
{
- case OS_HEAD:
+ case OSTATE_HEAD:
if (c == '\0' &&
bitnset(M_NONULLS,
mci->mci_mailer->m_flags))
@@ -4731,11 +4767,6 @@ putbody(mci, e, separator)
dead = true;
continue;
}
- else
- {
- /* record progress for DATA timeout */
- DataProgress = true;
- }
pos++;
}
for (xp = buf; xp < bp; xp++)
@@ -4748,11 +4779,6 @@ putbody(mci, e, separator)
dead = true;
break;
}
- else
- {
- /* record progress for DATA timeout */
- DataProgress = true;
- }
}
if (dead)
continue;
@@ -4763,11 +4789,6 @@ putbody(mci, e, separator)
mci->mci_mailer->m_eol)
== SM_IO_EOF)
break;
- else
- {
- /* record progress for DATA timeout */
- DataProgress = true;
- }
pos = 0;
}
else
@@ -4785,14 +4806,14 @@ putbody(mci, e, separator)
/* determine next state */
if (c == '\n')
- ostate = OS_HEAD;
+ ostate = OSTATE_HEAD;
else if (c == '\r')
- ostate = OS_CR;
+ ostate = OSTATE_CR;
else
- ostate = OS_INLINE;
+ ostate = OSTATE_INLINE;
continue;
- case OS_CR:
+ case OSTATE_CR:
if (c == '\n')
{
/* got CRLF */
@@ -4801,11 +4822,6 @@ putbody(mci, e, separator)
mci->mci_mailer->m_eol)
== SM_IO_EOF)
continue;
- else
- {
- /* record progress for DATA timeout */
- DataProgress = true;
- }
if (TrafficLogFile != NULL)
{
@@ -4813,7 +4829,7 @@ putbody(mci, e, separator)
SM_TIME_DEFAULT,
mci->mci_mailer->m_eol);
}
- ostate = OS_HEAD;
+ ostate = OSTATE_HEAD;
continue;
}
@@ -4821,13 +4837,13 @@ putbody(mci, e, separator)
SM_ASSERT(pbp < peekbuf + sizeof(peekbuf));
*pbp++ = c;
c = '\r';
- ostate = OS_INLINE;
+ ostate = OSTATE_INLINE;
goto putch;
- case OS_INLINE:
+ case OSTATE_INLINE:
if (c == '\r')
{
- ostate = OS_CR;
+ ostate = OSTATE_CR;
continue;
}
if (c == '\0' &&
@@ -4867,11 +4883,6 @@ putch:
dead = true;
continue;
}
- else
- {
- /* record progress for DATA timeout */
- DataProgress = true;
- }
pos++;
continue;
}
@@ -4887,11 +4898,6 @@ putch:
dead = true;
continue;
}
- else
- {
- /* record progress for DATA timeout */
- DataProgress = true;
- }
if (TrafficLogFile != NULL)
{
@@ -4900,7 +4906,7 @@ putch:
"!%s",
mci->mci_mailer->m_eol);
}
- ostate = OS_HEAD;
+ ostate = OSTATE_HEAD;
SM_ASSERT(pbp < peekbuf +
sizeof(peekbuf));
*pbp++ = c;
@@ -4917,13 +4923,8 @@ putch:
mci->mci_mailer->m_eol)
== SM_IO_EOF)
continue;
- else
- {
- /* record progress for DATA timeout */
- DataProgress = true;
- }
pos = 0;
- ostate = OS_HEAD;
+ ostate = OSTATE_HEAD;
}
else
{
@@ -4939,13 +4940,8 @@ putch:
dead = true;
continue;
}
- else
- {
- /* record progress for DATA timeout */
- DataProgress = true;
- }
pos++;
- ostate = OS_INLINE;
+ ostate = OSTATE_INLINE;
}
break;
}
@@ -4970,11 +4966,6 @@ putch:
dead = true;
break;
}
- else
- {
- /* record progress for DATA timeout */
- DataProgress = true;
- }
}
pos += bp - buf;
}
@@ -4984,11 +4975,9 @@ putch:
(void) sm_io_fputs(TrafficLogFile,
SM_TIME_DEFAULT,
mci->mci_mailer->m_eol);
- (void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
- mci->mci_mailer->m_eol);
-
- /* record progress for DATA timeout */
- DataProgress = true;
+ if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
+ mci->mci_mailer->m_eol) == SM_IO_EOF)
+ goto writeerr;
}
}
@@ -4998,6 +4987,7 @@ putch:
qid_printqueue(e->e_dfqgrp, e->e_dfqdir),
DATAFL_LETTER, e->e_id);
ExitStat = EX_IOERR;
+ ioerr = true;
}
endofmessage:
@@ -5012,23 +5002,35 @@ endofmessage:
** offset to match.
*/
+ save_errno = errno;
if (e->e_dfp != NULL)
(void) bfrewind(e->e_dfp);
/* some mailers want extra blank line at end of message */
if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
buf[0] != '\0' && buf[0] != '\n')
- putline("", mci);
+ {
+ if (!putline("", mci))
+ goto writeerr;
+ }
- (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
- if (sm_io_error(mci->mci_out) && errno != EPIPE)
+ if (!dead &&
+ (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF ||
+ (sm_io_error(mci->mci_out) && errno != EPIPE)))
{
+ save_errno = errno;
syserr("putbody: write error");
ExitStat = EX_IOERR;
+ ioerr = true;
}
- errno = 0;
+ errno = save_errno;
+ return !dead && !ioerr;
+
+ writeerr:
+ return false;
}
+
/*
** MAILFILE -- Send a message to a file.
**
@@ -5559,14 +5561,14 @@ mailfile(filename, mailer, ctladdr, sfflags, e)
}
#endif /* MIME7TO8 */
- putfromline(&mcibuf, e);
- (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
- (*e->e_putbody)(&mcibuf, e, NULL);
- putline("\n", &mcibuf);
- if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
+ if (!putfromline(&mcibuf, e) ||
+ !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) ||
+ !(*e->e_putbody)(&mcibuf, e, NULL) ||
+ !putline("\n", &mcibuf) ||
+ (sm_io_flush(f, SM_TIME_DEFAULT) != 0 ||
(SuperSafe != SAFE_NO &&
fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) ||
- sm_io_error(f))
+ sm_io_error(f)))
{
setstat(EX_IOERR);
#if !NOFTRUNCATE
@@ -6079,12 +6081,16 @@ starttls(m, mci, e)
XS_STARTTLS);
/* check return code from server */
- if (smtpresult == 454)
+ if (REPLYTYPE(smtpresult) == 4)
return EX_TEMPFAIL;
if (smtpresult == 501)
return EX_USAGE;
if (smtpresult == -1)
return smtpresult;
+
+ /* not an expected reply but we have to deal with it */
+ if (REPLYTYPE(smtpresult) == 5)
+ return EX_UNAVAILABLE;
if (smtpresult != 220)
return EX_PROTOCOL;
@@ -6128,86 +6134,23 @@ starttls(m, mci, e)
ssl_retry:
if ((result = SSL_connect(clt_ssl)) <= 0)
{
- int i;
- bool timedout;
- time_t left;
- time_t now = curtime();
- struct timeval tv;
+ int i, ssl_err;
- /* what to do in this case? */
- i = SSL_get_error(clt_ssl, result);
+ ssl_err = SSL_get_error(clt_ssl, result);
+ i = tls_retry(clt_ssl, rfd, wfd, tlsstart,
+ TimeOuts.to_starttls, ssl_err, "client");
+ if (i > 0)
+ goto ssl_retry;
- /*
- ** For SSL_ERROR_WANT_{READ,WRITE}:
- ** There is not a complete SSL record available yet
- ** or there is only a partial SSL record removed from
- ** the network (socket) buffer into the SSL buffer.
- ** The SSL_connect will only succeed when a full
- ** SSL record is available (assuming a "real" error
- ** doesn't happen). To handle when a "real" error
- ** does happen the select is set for exceptions too.
- ** The connection may be re-negotiated during this time
- ** so both read and write "want errors" need to be handled.
- ** A select() exception loops back so that a proper SSL
- ** error message can be gotten.
- */
-
- left = TimeOuts.to_starttls - (now - tlsstart);
- timedout = left <= 0;
- if (!timedout)
- {
- tv.tv_sec = left;
- tv.tv_usec = 0;
- }
-
- if (!timedout && FD_SETSIZE > 0 &&
- (rfd >= FD_SETSIZE ||
- (i == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE)))
- {
- if (LogLevel > 5)
- {
- sm_syslog(LOG_ERR, e->e_id,
- "STARTTLS=client, error: fd %d/%d too large",
- rfd, wfd);
- if (LogLevel > 8)
- tlslogerr("client");
- }
- errno = EINVAL;
- goto tlsfail;
- }
- if (!timedout && i == SSL_ERROR_WANT_READ)
- {
- fd_set ssl_maskr, ssl_maskx;
-
- FD_ZERO(&ssl_maskr);
- FD_SET(rfd, &ssl_maskr);
- FD_ZERO(&ssl_maskx);
- FD_SET(rfd, &ssl_maskx);
- if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv)
- > 0)
- goto ssl_retry;
- }
- if (!timedout && i == SSL_ERROR_WANT_WRITE)
- {
- fd_set ssl_maskw, ssl_maskx;
-
- FD_ZERO(&ssl_maskw);
- FD_SET(wfd, &ssl_maskw);
- FD_ZERO(&ssl_maskx);
- FD_SET(rfd, &ssl_maskx);
- if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv)
- > 0)
- goto ssl_retry;
- }
if (LogLevel > 5)
{
- sm_syslog(LOG_ERR, e->e_id,
- "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d, errno=%d",
- result, i, (int) timedout, errno);
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=client, error: connect failed=%d, SSL_error=%d, errno=%d, retry=%d",
+ result, ssl_err, errno, i);
if (LogLevel > 8)
tlslogerr("client");
}
-tlsfail:
+
SSL_free(clt_ssl);
clt_ssl = NULL;
return EX_SOFTWARE;