summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/src/usersmtp.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/src/usersmtp.c')
-rw-r--r--contrib/sendmail/src/usersmtp.c1721
1 files changed, 1158 insertions, 563 deletions
diff --git a/contrib/sendmail/src/usersmtp.c b/contrib/sendmail/src/usersmtp.c
index a9476fe..cc29982 100644
--- a/contrib/sendmail/src/usersmtp.c
+++ b/contrib/sendmail/src/usersmtp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -13,22 +13,21 @@
#include <sendmail.h>
-#ifndef lint
-# if SMTP
-static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.34 2001/06/26 21:55:23 gshapiro Exp $ (with SMTP)";
-# else /* SMTP */
-static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.34 2001/06/26 21:55:23 gshapiro Exp $ (without SMTP)";
-# endif /* SMTP */
-#endif /* ! lint */
+SM_RCSID("@(#)$Id: usersmtp.c,v 8.428 2002/01/08 00:56:23 ca Exp $")
#include <sysexits.h>
-#if SMTP
-
+extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool));
static void datatimeout __P((void));
static void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
+static int smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
+
+#if SASL
+extern void *sm_sasl_malloc __P((unsigned long));
+extern void sm_sasl_free __P((void *));
+#endif /* SASL */
/*
** USERSMTP -- run SMTP protocol from the user end.
@@ -36,16 +35,19 @@ static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
** 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" */
+#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" */
-#define ENHSCN(e, d) (e) == NULL ? (d) : newstr(e)
+#define ENHSCN(e, d) ((e) == NULL ? (d) : (e))
+
+#define ENHSCN_RPOOL(e, d, rpool) \
+ ((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
static char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
static bool SmtpNeedIntro; /* need "while talking" in transcript */
- /*
+/*
** SMTPINIT -- initialize SMTP.
**
** Opens the connection and sends the initial protocol.
@@ -78,8 +80,8 @@ smtpinit(m, mci, e, onlyhelo)
enhsc = NULL;
if (tTd(18, 1))
{
- dprintf("smtpinit ");
- mci_dump(mci, FALSE);
+ sm_dprintf("smtpinit ");
+ mci_dump(mci, false);
}
/*
@@ -90,10 +92,12 @@ smtpinit(m, mci, e, onlyhelo)
CurHostName = mci->mci_host; /* XXX UGLY XXX */
if (CurHostName == NULL)
CurHostName = MyHostName;
- SmtpNeedIntro = TRUE;
+ SmtpNeedIntro = true;
switch (mci->mci_state)
{
- case MCIS_ACTIVE:
+ case MCIS_MAIL:
+ case MCIS_RCPT:
+ case MCIS_DATA:
/* need to clear old information */
smtprset(m, mci, e);
/* FALLTHROUGH */
@@ -129,7 +133,7 @@ smtpinit(m, mci, e, onlyhelo)
*/
SmtpPhase = mci->mci_phase = "client greeting";
- sm_setproctitle(TRUE, e, "%s %s: %s",
+ sm_setproctitle(true, e, "%s %s: %s",
qid_printname(e), CurHostName, mci->mci_phase);
r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL);
if (r < 0)
@@ -150,12 +154,16 @@ helo:
hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
tryhelo:
+#if _FFR_IGNORE_EXT_ON_HELO
+ mci->mci_flags &= ~MCIF_HELO;
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
if (bitnset(M_LMTP, m->m_flags))
{
smtpmessage("LHLO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client LHLO";
}
- else if (bitset(MCIF_ESMTP, mci->mci_flags))
+ else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
+ !bitnset(M_FSMTP, m->m_flags))
{
smtpmessage("EHLO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client EHLO";
@@ -164,10 +172,16 @@ tryhelo:
{
smtpmessage("HELO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client HELO";
+#if _FFR_IGNORE_EXT_ON_HELO
+ mci->mci_flags |= MCIF_HELO;
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
}
- sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
+ sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
CurHostName, mci->mci_phase);
- r = reply(m, mci, e, TimeOuts.to_helo, helo_options, NULL);
+ r = reply(m, mci, e,
+ bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
+ : TimeOuts.to_helo,
+ helo_options, NULL);
if (r < 0)
goto tempfail1;
else if (REPLYTYPE(r) == 5)
@@ -195,7 +209,7 @@ tryhelo:
*p = '\0';
if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
!bitnset(M_LMTP, m->m_flags) &&
- strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
+ sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
{
syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
CurHostName);
@@ -206,6 +220,7 @@ tryhelo:
return;
}
+#if !_FFR_DEPRECATE_MAILER_FLAG_I
/*
** If this is expected to be another sendmail, send some internal
** commands.
@@ -219,6 +234,7 @@ tryhelo:
if (r < 0)
goto tempfail1;
}
+#endif /* !_FFR_DEPRECATE_MAILER_FLAG_I */
if (mci->mci_state != MCIS_CLOSED)
{
@@ -229,16 +245,12 @@ tryhelo:
/* got a 421 error code during startup */
tempfail1:
- if (mci->mci_errno == 0)
- mci->mci_errno = errno;
mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
if (mci->mci_state != MCIS_CLOSED)
smtpquit(m, mci, e);
return;
tempfail2:
- if (mci->mci_errno == 0)
- mci->mci_errno = errno;
/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
SmtpReplyBuffer);
@@ -247,12 +259,11 @@ tryhelo:
return;
unavailable:
- mci->mci_errno = errno;
mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
smtpquit(m, mci, e);
return;
}
- /*
+/*
** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
**
** Parameters:
@@ -276,23 +287,38 @@ esmtp_check(line, firstline, m, mci, e)
{
if (strstr(line, "ESMTP") != NULL)
mci->mci_flags |= MCIF_ESMTP;
+
+ /*
+ ** Dirty hack below. Quoting the author:
+ ** This was a response to people who wanted SMTP transmission to be
+ ** just-send-8 by default. Essentially, you could put this tag into
+ ** your greeting message to behave as though the F=8 flag was set on
+ ** the mailer.
+ */
+
if (strstr(line, "8BIT-OK") != NULL)
mci->mci_flags |= MCIF_8BITOK;
}
-# if SASL
- /*
+
+#if SASL
+/* specify prototype so compiler can check calls */
+static char *str_union __P((char *, char *, SM_RPOOL_T *));
+
+/*
** STR_UNION -- create the union of two lists
**
** Parameters:
** s1, s2 -- lists of items (separated by single blanks).
+** rpool -- resource pool from which result is allocated.
**
** Returns:
** the union of both lists.
*/
static char *
-str_union(s1, s2)
+str_union(s1, s2, rpool)
char *s1, *s2;
+ SM_RPOOL_T *rpool;
{
char *hr, *h1, *h, *res;
int l1, l2, rl;
@@ -304,8 +330,14 @@ str_union(s1, s2)
l1 = strlen(s1);
l2 = strlen(s2);
rl = l1 + l2;
- res = (char *)xalloc(rl + 2);
- (void) strlcpy(res, s1, rl);
+ res = (char *) sm_rpool_malloc(rpool, rl + 2);
+ if (res == NULL)
+ {
+ if (l1 > l2)
+ return s1;
+ return s2;
+ }
+ (void) sm_strlcpy(res, s1, rl);
hr = res + l1;
h1 = s2;
h = s2;
@@ -340,8 +372,9 @@ str_union(s1, s2)
}
return res;
}
-# endif /* SASL */
- /*
+#endif /* SASL */
+
+/*
** HELO_OPTIONS -- process the options on a HELO line.
**
** Parameters:
@@ -349,7 +382,7 @@ str_union(s1, s2)
** firstline -- set if this is the first line of the reply.
** m -- the mailer.
** mci -- the mailer connection info.
-** e -- the envelope.
+** e -- the envelope (unused).
**
** Returns:
** none.
@@ -364,62 +397,85 @@ helo_options(line, firstline, m, mci, e)
ENVELOPE *e;
{
register char *p;
+#if _FFR_IGNORE_EXT_ON_HELO
+ static bool logged = false;
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
if (firstline)
{
-# if SASL
- if (mci->mci_saslcap != NULL)
- sm_free(mci->mci_saslcap);
+#if SASL
mci->mci_saslcap = NULL;
-# endif /* SASL */
+#endif /* SASL */
+#if _FFR_IGNORE_EXT_ON_HELO
+ logged = false;
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
+ return;
+ }
+#if _FFR_IGNORE_EXT_ON_HELO
+ else if (bitset(MCIF_HELO, mci->mci_flags))
+ {
+ if (LogLevel > 8 && !logged)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "server=%s [%s] returned extensions despite HELO command",
+ macvalue(macid("{server_name}"), e),
+ macvalue(macid("{server_addr}"), e));
+ logged = true;
+ }
return;
}
+#endif /* _FFR_IGNORE_EXT_ON_HELO */
- if (strlen(line) < (SIZE_T) 5)
+ if (strlen(line) < 5)
return;
line += 4;
p = strpbrk(line, " =");
if (p != NULL)
*p++ = '\0';
- if (strcasecmp(line, "size") == 0)
+ if (sm_strcasecmp(line, "size") == 0)
{
mci->mci_flags |= MCIF_SIZE;
if (p != NULL)
mci->mci_maxsize = atol(p);
}
- else if (strcasecmp(line, "8bitmime") == 0)
+ else if (sm_strcasecmp(line, "8bitmime") == 0)
{
mci->mci_flags |= MCIF_8BITMIME;
mci->mci_flags &= ~MCIF_7BIT;
}
- else if (strcasecmp(line, "expn") == 0)
+ else if (sm_strcasecmp(line, "expn") == 0)
mci->mci_flags |= MCIF_EXPN;
- else if (strcasecmp(line, "dsn") == 0)
+ else if (sm_strcasecmp(line, "dsn") == 0)
mci->mci_flags |= MCIF_DSN;
- else if (strcasecmp(line, "enhancedstatuscodes") == 0)
+ else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0)
mci->mci_flags |= MCIF_ENHSTAT;
-# if STARTTLS
- else if (strcasecmp(line, "starttls") == 0)
+ else if (sm_strcasecmp(line, "pipelining") == 0)
+ mci->mci_flags |= MCIF_PIPELINED;
+#if STARTTLS
+ else if (sm_strcasecmp(line, "starttls") == 0)
mci->mci_flags |= MCIF_TLS;
-# endif /* STARTTLS */
-# if SASL
- else if (strcasecmp(line, "auth") == 0)
+#endif /* STARTTLS */
+ else if (sm_strcasecmp(line, "deliverby") == 0)
+ {
+ mci->mci_flags |= MCIF_DLVR_BY;
+ if (p != NULL)
+ mci->mci_min_by = atol(p);
+ }
+#if SASL
+ else if (sm_strcasecmp(line, "auth") == 0)
{
if (p != NULL && *p != '\0')
{
if (mci->mci_saslcap != NULL)
{
- char *h;
-
/*
- ** create the union with previous auth
+ ** Create the union with previous auth
** offerings because we recognize "auth "
** and "auth=" (old format).
*/
- h = mci->mci_saslcap;
- mci->mci_saslcap = str_union(h, p);
- if (h != mci->mci_saslcap)
- sm_free(h);
+
+ mci->mci_saslcap = str_union(mci->mci_saslcap,
+ p, mci->mci_rpool);
mci->mci_flags |= MCIF_AUTH;
}
else
@@ -427,34 +483,115 @@ helo_options(line, firstline, m, mci, e)
int l;
l = strlen(p) + 1;
- mci->mci_saslcap = (char *)xalloc(l);
- (void) strlcpy(mci->mci_saslcap, p, l);
- mci->mci_flags |= MCIF_AUTH;
+ mci->mci_saslcap = (char *)
+ sm_rpool_malloc(mci->mci_rpool, l);
+ if (mci->mci_saslcap != NULL)
+ {
+ (void) sm_strlcpy(mci->mci_saslcap, p,
+ l);
+ mci->mci_flags |= MCIF_AUTH;
+ }
}
}
}
-# endif /* SASL */
+#endif /* SASL */
+}
+#if SASL
+
+static int getsimple __P((void *, int, const char **, unsigned *));
+static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **));
+static int saslgetrealm __P((void *, int, const char **, const char **));
+static int readauth __P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
+static int getauth __P((MCI *, ENVELOPE *, SASL_AI_T *));
+static char *removemech __P((char *, char *, SM_RPOOL_T *));
+static int attemptauth __P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
+
+static sasl_callback_t callbacks[] =
+{
+ { SASL_CB_GETREALM, &saslgetrealm, NULL },
+#define CB_GETREALM_IDX 0
+ { SASL_CB_PASS, &getsecret, NULL },
+#define CB_PASS_IDX 1
+ { SASL_CB_USER, &getsimple, NULL },
+#define CB_USER_IDX 2
+ { SASL_CB_AUTHNAME, &getsimple, NULL },
+#define CB_AUTHNAME_IDX 3
+ { SASL_CB_VERIFYFILE, &safesaslfile, NULL },
+#define CB_SAFESASL_IDX 4
+ { SASL_CB_LIST_END, NULL, NULL }
+};
+
+/*
+** INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
+**
+** Parameters:
+** none.
+**
+** Returns:
+** SASL_OK -- if successful.
+** SASL error code -- otherwise.
+**
+** Side Effects:
+** checks/sets sasl_clt_init.
+*/
+
+static bool sasl_clt_init = false;
+
+static int
+init_sasl_client()
+{
+ int result;
+
+ if (sasl_clt_init)
+ return SASL_OK;
+ result = sasl_client_init(callbacks);
+
+ /* should we retry later again or just remember that it failed? */
+ if (result == SASL_OK)
+ sasl_clt_init = true;
+ return result;
}
-# if SASL
+/*
+** STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** checks/sets sasl_clt_init.
+*/
- /*
+void
+stop_sasl_client()
+{
+ if (!sasl_clt_init)
+ return;
+ sasl_clt_init = false;
+ sasl_done();
+}
+/*
** GETSASLDATA -- process the challenges from the SASL protocol
**
** This gets the relevant sasl response data out of the reply
-** from the server
+** from the server.
**
** Parameters:
** line -- the response line.
** firstline -- set if this is the first line of the reply.
** m -- the mailer.
** mci -- the mailer connection info.
-** e -- the envelope.
+** e -- the envelope (unused).
**
** Returns:
** none.
*/
-void
+static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
+
+static void
getsasldata(line, firstline, m, mci, e)
char *line;
bool firstline;
@@ -463,96 +600,94 @@ getsasldata(line, firstline, m, mci, e)
ENVELOPE *e;
{
int len;
- char *out;
int result;
+ char *out;
/* if not a continue we don't care about it */
- if ((strlen(line) <= 4) ||
+ len = strlen(line);
+ if ((len <= 4) ||
(line[0] != '3') ||
- (line[1] != '3') ||
- (line[2] != '4'))
+ !isascii(line[1]) || !isdigit(line[1]) ||
+ !isascii(line[2]) || !isdigit(line[2]))
{
- mci->mci_sasl_string = NULL;
+ SM_FREE_CLR(mci->mci_sasl_string);
return;
}
/* forget about "334 " */
line += 4;
- len = strlen(line);
+ len -= 4;
- out = xalloc(len + 1);
- result = sasl_decode64(line, len, out, (u_int *)&len);
+ out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
+ result = sasl_decode64(line, len, out, (unsigned int *)&len);
if (result != SASL_OK)
{
len = 0;
*out = '\0';
}
+
+ /*
+ ** mci_sasl_string is "shared" with Cyrus-SASL library; hence
+ ** it can't be in an rpool unless we use the same memory
+ ** management mechanism (with same rpool!) for Cyrus SASL.
+ */
+
if (mci->mci_sasl_string != NULL)
{
if (mci->mci_sasl_string_len <= len)
{
- sm_free(mci->mci_sasl_string);
+ sm_free(mci->mci_sasl_string); /* XXX */
mci->mci_sasl_string = xalloc(len + 1);
}
}
else
mci->mci_sasl_string = xalloc(len + 1);
- /* XXX this is probably leaked */
+
memcpy(mci->mci_sasl_string, out, len);
mci->mci_sasl_string[len] = '\0';
mci->mci_sasl_string_len = len;
- sm_free(out);
return;
}
-
- /*
-** READAUTH -- read auth value from a file
+/*
+** READAUTH -- read auth values from a file
**
** Parameters:
-** l -- line to define.
** filename -- name of file to read.
** safe -- if set, this is a safe read.
+** sai -- where to store auth_info.
+** rpool -- resource pool for sai.
**
** Returns:
-** line from file
-**
-** Side Effects:
-** overwrites local static buffer. The caller should copy
-** the result.
-**
+** EX_OK -- data succesfully read.
+** EX_UNAVAILABLE -- no valid filename.
+** EX_TEMPFAIL -- temporary failure.
*/
-/* lines in authinfo file */
-# define SASL_USER 1
-# define SASL_AUTHID 2
-# define SASL_PASSWORD 3
-# define SASL_DEFREALM 4
-# define SASL_MECH 5
-
static char *sasl_info_name[] =
{
- "",
"user id",
- "authorization id",
+ "authentication id",
"password",
"realm",
- "mechanism"
+ "mechlist"
};
-
-static char *
-readauth(l, filename, safe)
- int l;
+static int
+readauth(filename, safe, sai, rpool)
char *filename;
bool safe;
+ SASL_AI_T *sai;
+ SM_RPOOL_T *rpool;
{
- FILE *f;
+ SM_FILE_T *f;
long sff;
pid_t pid;
int lc;
- static char buf[MAXLINE];
+ char *s;
+ char buf[MAXLINE];
if (filename == NULL || filename[0] == '\0')
- return "";
+ return EX_UNAVAILABLE;
+
#if !_FFR_ALLOW_SASLINFO
/*
** make sure we don't use a program that is not
@@ -560,6 +695,7 @@ readauth(l, filename, safe)
** However, currently we don't pass this info (authinfo file
** specified by user) around, so we just turn off program access.
*/
+
if (filename[0] == '|')
{
auto int fd;
@@ -580,22 +716,29 @@ readauth(l, filename, safe)
if (pid < 0)
f = NULL;
else
- f = fdopen(fd, "r");
+ f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
+ (void *) &fd, SM_IO_RDONLY, NULL);
}
else
#endif /* !_FFR_ALLOW_SASLINFO */
{
pid = -1;
- sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
- | SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES;
+ sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
+ |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
+# if _FFR_GROUPREADABLEAUTHINFOFILE
+ if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
+# endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
+ sff |= SFF_NOGRFILES;
if (DontLockReadFiles)
sff |= SFF_NOLOCK;
+
#if _FFR_ALLOW_SASLINFO
/*
** XXX: make sure we don't read or open files that are not
** accesible to the user who specified a different authinfo
** file.
*/
+
sff |= SFF_MUSTOWN;
#else /* _FFR_ALLOW_SASLINFO */
if (safe)
@@ -606,62 +749,209 @@ readauth(l, filename, safe)
}
if (f == NULL)
{
- syserr("readauth: cannot open %s", filename);
- return "";
+ if (LogLevel > 5)
+ sm_syslog(LOG_ERR, NOQID,
+ "AUTH=client, error: can't open %s: %s",
+ filename, sm_errstring(errno));
+ return EX_TEMPFAIL;
}
lc = 0;
- while (lc < l && fgets(buf, sizeof buf, f) != NULL)
+ while (lc <= SASL_MECHLIST &&
+ sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
{
if (buf[0] != '#')
+ {
+ (*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
+ if ((s = strchr((*sai)[lc], '\n')) != NULL)
+ *s = '\0';
lc++;
+ }
}
- (void) fclose(f);
+ (void) sm_io_close(f, SM_TIME_DEFAULT);
if (pid > 0)
(void) waitfor(pid);
- if (lc < l)
- {
- if (LogLevel >= 9)
- sm_syslog(LOG_WARNING, NOQID, "SASL: error: can't read %s from %s",
- sasl_info_name[l], filename);
- return "";
- }
- lc = strlen(buf) - 1;
- if (lc >= 0)
- buf[lc] = '\0';
- if (tTd(95, 6))
- dprintf("readauth(%s, %d) = '%s'\n", filename, l, buf);
- return buf;
+ if (lc < SASL_PASSWORD)
+ {
+ if (LogLevel > 8)
+ sm_syslog(LOG_ERR, NOQID,
+ "AUTH=client, error: can't read %s from %s",
+ sasl_info_name[lc + 1], filename);
+ return EX_TEMPFAIL;
+ }
+ return EX_OK;
}
-# ifndef __attribute__
-# define __attribute__(x)
-# endif /* ! __attribute__ */
-
-static int getsimple __P((void *, int, const char **, unsigned *));
-static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **));
-static int saslgetrealm __P((void *, int, const char **, const char **));
+/*
+** GETAUTH -- get authinfo from ruleset call
+**
+** {server_name}, {server_addr} must be set
+**
+** Parameters:
+** mci -- the mailer connection structure.
+** e -- the envelope (including the sender to specify).
+** sai -- pointer to authinfo (result).
+**
+** Returns:
+** EX_OK -- ruleset was succesfully called, data may not
+** be available, sai must be checked.
+** EX_UNAVAILABLE -- ruleset unavailable (or failed).
+** EX_TEMPFAIL -- temporary failure (from ruleset).
+**
+** Side Effects:
+** Fills in sai if successful.
+*/
-static sasl_callback_t callbacks[] =
+static int
+getauth(mci, e, sai)
+ MCI *mci;
+ ENVELOPE *e;
+ SASL_AI_T *sai;
{
- { SASL_CB_GETREALM, &saslgetrealm, NULL },
-# define CB_GETREALM_IDX 0
- { SASL_CB_PASS, &getsecret, NULL },
-# define CB_PASS_IDX 1
- { SASL_CB_USER, &getsimple, NULL },
-# define CB_USER_IDX 2
- { SASL_CB_AUTHNAME, &getsimple, NULL },
-# define CB_AUTHNAME_IDX 3
- { SASL_CB_VERIFYFILE, &safesaslfile, NULL },
- { SASL_CB_LIST_END, NULL, NULL }
-};
+ int i, r, l, got, ret;
+ char **pvp;
+ char pvpbuf[PSBUFSIZE];
+
+ r = rscap("authinfo", macvalue(macid("{server_name}"), e),
+ macvalue(macid("{server_addr}"), e), e,
+ &pvp, pvpbuf, sizeof(pvpbuf));
+
+ if (r != EX_OK)
+ return EX_UNAVAILABLE;
+
+ /* other than expected return value: ok (i.e., no auth) */
+ if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
+ return EX_OK;
+ if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
+ return EX_TEMPFAIL;
- /*
+ /*
+ ** parse the data, put it into sai
+ ** format: "TDstring" (including the '"' !)
+ ** where T is a tag: 'U', ...
+ ** D is a delimiter: ':' or '='
+ */
+
+ ret = EX_OK; /* default return value */
+ i = 0;
+ got = 0;
+ while (i < SASL_ENTRIES)
+ {
+ if (pvp[i + 1] == NULL)
+ break;
+ if (pvp[i + 1][0] != '"')
+ break;
+ switch (pvp[i + 1][1])
+ {
+ case 'U':
+ case 'u':
+ r = SASL_USER;
+ break;
+ case 'I':
+ case 'i':
+ r = SASL_AUTHID;
+ break;
+ case 'P':
+ case 'p':
+ r = SASL_PASSWORD;
+ break;
+ case 'R':
+ case 'r':
+ r = SASL_DEFREALM;
+ break;
+ case 'M':
+ case 'm':
+ r = SASL_MECHLIST;
+ break;
+ default:
+ goto fail;
+ }
+ l = strlen(pvp[i + 1]);
+
+ /* check syntax */
+ if (l <= 3 || pvp[i + 1][l - 1] != '"')
+ goto fail;
+
+ /* remove closing quote */
+ pvp[i + 1][l - 1] = '\0';
+
+ /* remove "TD and " */
+ l -= 4;
+ (*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
+ if ((*sai)[r] == NULL)
+ goto tempfail;
+ if (pvp[i + 1][2] == ':')
+ {
+ /* ':text' (just copy) */
+ (void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
+ got |= 1 << r;
+ }
+ else if (pvp[i + 1][2] == '=')
+ {
+ unsigned int len;
+
+ /* '=base64' (decode) */
+ r = sasl_decode64(pvp[i + 1] + 3,
+ (unsigned int) l, (*sai)[r], &len);
+ if (r != SASL_OK)
+ goto fail;
+ got |= 1 << r;
+ }
+ else
+ goto fail;
+ if (tTd(95, 5))
+ sm_syslog(LOG_WARNING, NOQID, "getauth %s=%s",
+ sasl_info_name[r], (*sai)[r]);
+ ++i;
+ }
+
+ /* did we get the expected data? */
+ if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
+ bitset(SASL_PASSWORD_BIT, got)))
+ goto fail;
+
+ /* no authid? copy uid */
+ if (!bitset(SASL_AUTHID_BIT, got))
+ {
+ l = strlen((*sai)[SASL_USER]) + 1;
+ (*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
+ l + 1);
+ if ((*sai)[SASL_AUTHID] == NULL)
+ goto tempfail;
+ (void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
+ }
+
+ /* no uid? copy authid */
+ if (!bitset(SASL_USER_BIT, got))
+ {
+ l = strlen((*sai)[SASL_AUTHID]) + 1;
+ (*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
+ l + 1);
+ if ((*sai)[SASL_USER] == NULL)
+ goto tempfail;
+ (void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
+ }
+ return EX_OK;
+
+ tempfail:
+ ret = EX_TEMPFAIL;
+ fail:
+ if (LogLevel > 8)
+ sm_syslog(LOG_WARNING, NOQID,
+ "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
+ macvalue(macid("{server_name}"), e),
+ macvalue(macid("{server_addr}"), e),
+ ret == EX_TEMPFAIL ? "temp" : "");
+ for (i = 0; i <= SASL_MECHLIST; i++)
+ (*sai)[i] = NULL; /* just clear; rpool */
+ return ret;
+}
+/*
** GETSIMPLE -- callback to get userid or authid
**
** Parameters:
-** context -- unused
+** context -- sai
** id -- what to do
** result -- (pointer to) result
** len -- (pointer to) length of result
@@ -672,80 +962,128 @@ static sasl_callback_t callbacks[] =
static int
getsimple(context, id, result, len)
- void *context __attribute__((unused));
+ void *context;
int id;
const char **result;
unsigned *len;
{
- char *h;
-# if SASL > 10509
- int addrealm;
- static int addedrealm = FALSE;
-# endif /* SASL > 10509 */
- static char *user = NULL;
- static char *authid = NULL;
-
- if (result == NULL)
+ char *h, *s;
+# if SASL > 10509
+ bool addrealm;
+# endif /* SASL > 10509 */
+ size_t l;
+ SASL_AI_T *sai;
+ char *authid = NULL;
+
+ if (result == NULL || context == NULL)
return SASL_BADPARAM;
+ sai = (SASL_AI_T *) context;
+
+ /*
+ ** Unfortunately it is not clear whether this routine should
+ ** return a copy of a string or just a pointer to a string.
+ ** The Cyrus-SASL plugins treat these return values differently, e.g.,
+ ** plugins/cram.c free()s authid, plugings/digestmd5.c does not.
+ ** The best solution to this problem is to fix Cyrus-SASL, but it
+ ** seems there is nobody who creates patches... Hello CMU!?
+ ** The second best solution is to have flags that tell this routine
+ ** whether to return an malloc()ed copy.
+ ** The next best solution is to always return an malloc()ed copy,
+ ** and suffer from some memory leak, which is ugly for persistent
+ ** queue runners.
+ ** For now we go with the last solution...
+ ** We can't use rpools (which would avoid this particular problem)
+ ** as explained in sasl.c.
+ */
switch (id)
{
case SASL_CB_USER:
- if (user == NULL)
+ l = strlen((*sai)[SASL_USER]) + 1;
+ s = sm_sasl_malloc(l);
+ if (s == NULL)
{
- h = readauth(SASL_USER, SASLInfo, TRUE);
- user = newstr(h);
+ if (len != NULL)
+ *len = 0;
+ *result = NULL;
+ return SASL_NOMEM;
}
- *result = user;
+ (void) sm_strlcpy(s, (*sai)[SASL_USER], l);
+ *result = s;
if (tTd(95, 5))
- dprintf("AUTH username '%s'\n", *result);
+ sm_syslog(LOG_WARNING, NOQID, "AUTH username '%s'",
+ *result);
if (len != NULL)
- *len = user ? strlen(user) : 0;
+ *len = *result != NULL ? strlen(*result) : 0;
break;
case SASL_CB_AUTHNAME:
-# if SASL > 10509
+ h = (*sai)[SASL_AUTHID];
+# if SASL > 10509
/* XXX maybe other mechanisms too?! */
- addrealm = context != NULL &&
- strcasecmp(context, "CRAM-MD5") == 0;
- if (addedrealm != addrealm && authid != NULL)
- {
-# if SASL > 10522
- /*
- ** digest-md5 prior to 1.5.23 doesn't copy the
- ** value it gets from the callback, but free()s
- ** it later on
- ** workaround: don't free() it here
- ** this can cause a memory leak!
- */
+ addrealm = (*sai)[SASL_MECH] != NULL &&
+ sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0;
- sm_free(authid);
-# endif /* SASL > 10522 */
- authid = NULL;
- addedrealm = addrealm;
- }
-# endif /* SASL > 10509 */
- if (authid == NULL)
+ /*
+ ** Add realm to authentication id unless authid contains
+ ** '@' (i.e., a realm) or the default realm is empty.
+ */
+
+ if (addrealm && h != NULL && strchr(h, '@') == NULL)
{
- h = readauth(SASL_AUTHID, SASLInfo, TRUE);
-# if SASL > 10509
- if (addrealm && strchr(h, '@') == NULL)
+ /* has this been done before? */
+ if ((*sai)[SASL_ID_REALM] == NULL)
{
- size_t l;
char *realm;
- realm = callbacks[CB_GETREALM_IDX].context;
- l = strlen(h) + strlen(realm) + 2;
- authid = xalloc(l);
- snprintf(authid, l, "%s@%s", h, realm);
+ realm = (*sai)[SASL_DEFREALM];
+
+ /* do not add an empty realm */
+ if (*realm == '\0')
+ {
+ authid = h;
+ (*sai)[SASL_ID_REALM] = NULL;
+ }
+ else
+ {
+ l = strlen(h) + strlen(realm) + 2;
+
+ /* should use rpool, but from where? */
+ authid = sm_sasl_malloc(l);
+ if (authid != NULL)
+ {
+ (void) sm_snprintf(authid, l,
+ "%s@%s",
+ h, realm);
+ (*sai)[SASL_ID_REALM] = authid;
+ }
+ else
+ {
+ authid = h;
+ (*sai)[SASL_ID_REALM] = NULL;
+ }
+ }
}
else
-# endif /* SASL > 10509 */
- authid = newstr(h);
+ authid = (*sai)[SASL_ID_REALM];
+ }
+ else
+# endif /* SASL > 10509 */
+ authid = h;
+ l = strlen(authid) + 1;
+ s = sm_sasl_malloc(l);
+ if (s == NULL)
+ {
+ if (len != NULL)
+ *len = 0;
+ *result = NULL;
+ return SASL_NOMEM;
}
- *result = authid;
+ (void) sm_strlcpy(s, authid, l);
+ *result = s;
if (tTd(95, 5))
- dprintf("AUTH authid '%s'\n", *result);
+ sm_syslog(LOG_WARNING, NOQID, "AUTH authid '%s'",
+ *result);
if (len != NULL)
*len = authid ? strlen(authid) : 0;
break;
@@ -761,13 +1099,12 @@ getsimple(context, id, result, len)
}
return SASL_OK;
}
-
- /*
+/*
** GETSECRET -- callback to get password
**
** Parameters:
** conn -- connection information
-** context -- unused
+** context -- sai
** id -- what to do
** psecret -- (pointer to) result
**
@@ -778,30 +1115,29 @@ getsimple(context, id, result, len)
static int
getsecret(conn, context, id, psecret)
sasl_conn_t *conn;
- void *context __attribute__((unused));
+ SM_UNUSED(void *context);
int id;
sasl_secret_t **psecret;
{
- char *h;
int len;
- static char *authpass = NULL;
+ char *authpass;
+ SASL_AI_T *sai;
if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
return SASL_BADPARAM;
- if (authpass == NULL)
- {
- h = readauth(SASL_PASSWORD, SASLInfo, TRUE);
- authpass = newstr(h);
- }
+ sai = (SASL_AI_T *) context;
+ authpass = (*sai)[SASL_PASSWORD];
len = strlen(authpass);
- *psecret = (sasl_secret_t *) xalloc(sizeof(sasl_secret_t) + len + 1);
- (void) strlcpy((*psecret)->data, authpass, len + 1);
- (*psecret)->len = len;
+ *psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
+ len + 1);
+ if (*psecret == NULL)
+ return SASL_FAIL;
+ (void) sm_strlcpy((*psecret)->data, authpass, len + 1);
+ (*psecret)->len = (unsigned long) len;
return SASL_OK;
}
-
- /*
+/*
** SAFESASLFILE -- callback for sasl: is file safe?
**
** Parameters:
@@ -810,66 +1146,70 @@ getsecret(conn, context, id, psecret)
** type -- type of file to check
**
** Returns:
-** SASL_OK: file can be used
-** SASL_CONTINUE: don't use file
-** SASL_FAIL: failure (not used here)
+** SASL_OK -- file can be used
+** SASL_CONTINUE -- don't use file
+** SASL_FAIL -- failure (not used here)
**
*/
+
int
-# if SASL > 10515
+#if SASL > 10515
safesaslfile(context, file, type)
-# else /* SASL > 10515 */
+#else /* SASL > 10515 */
safesaslfile(context, file)
-# endif /* SASL > 10515 */
+#endif /* SASL > 10515 */
void *context;
char *file;
-# if SASL > 10515
+#if SASL > 10515
int type;
-# endif /* SASL > 10515 */
+#endif /* SASL > 10515 */
{
long sff;
int r;
+#if SASL <= 10515
+ size_t len;
+#endif /* SASL <= 10515 */
char *p;
if (file == NULL || *file == '\0')
return SASL_OK;
-
- sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK;
+ sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
+#if SASL <= 10515
if ((p = strrchr(file, '/')) == NULL)
p = file;
else
++p;
-# if SASL <= 10515
/* everything beside libs and .conf files must not be readable */
- r = strlen(p);
- if ((r <= 3 || strncmp(p, "lib", 3) != 0) &&
- (r <= 5 || strncmp(p + r - 5, ".conf", 5) != 0)
-# if _FFR_UNSAFE_SASL
- && !bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail)
-# endif /* _FFR_UNSAFE_SASL */
- )
- sff |= SFF_NORFILES;
-# else /* SASL > 10515 */
+ len = strlen(p);
+ if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
+ (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
+ {
+ if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
+ sff |= SFF_NORFILES;
+ if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
+ sff |= SFF_NOGWFILES;
+ }
+#else /* SASL <= 10515 */
/* files containing passwords should be not readable */
if (type == SASL_VRFY_PASSWD)
{
-# if _FFR_UNSAFE_SASL
- if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail))
+ if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
sff |= SFF_NOWRFILES;
else
-# endif /* _FFR_UNSAFE_SASL */
sff |= SFF_NORFILES;
+ if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
+ sff |= SFF_NOGWFILES;
}
-# endif /* SASL <= 10515 */
+#endif /* SASL <= 10515 */
p = file;
if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
S_IRUSR, NULL)) == 0)
return SASL_OK;
- if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9))
+ if (LogLevel > (r != ENOENT ? 8 : 10))
sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
- p, errstring(r));
+ p, sm_errstring(r));
return SASL_CONTINUE;
}
@@ -880,7 +1220,6 @@ safesaslfile(context, file)
**
** Parameters:
** context -- context shared between invocations
-** here: realm to return
** availrealms -- list of available realms
** {realm, realm, ...}
** result -- pointer to result
@@ -888,6 +1227,7 @@ safesaslfile(context, file)
** Returns:
** failure/success
*/
+
static int
saslgetrealm(context, id, availrealms, result)
void *context;
@@ -895,14 +1235,22 @@ saslgetrealm(context, id, availrealms, result)
const char **availrealms;
const char **result;
{
- if (LogLevel > 12)
- sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s",
- context == NULL ? "<No Context>" : (char *) context,
- (availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms);
- if (context == NULL)
+ char *r;
+ SASL_AI_T *sai;
+
+ sai = (SASL_AI_T *) context;
+ if (sai == NULL)
return SASL_FAIL;
+ r = (*sai)[SASL_DEFREALM];
+
+ if (LogLevel > 12)
+ sm_syslog(LOG_INFO, NOQID,
+ "AUTH=client, realm=%s, available realms=%s",
+ r == NULL ? "<No Realm>" : r,
+ (availrealms == NULL || *availrealms == NULL)
+ ? "<No Realms>" : *availrealms);
- /* check whether context is in list? */
+ /* check whether context is in list */
if (availrealms != NULL && *availrealms != NULL)
{
if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
@@ -910,15 +1258,15 @@ saslgetrealm(context, id, availrealms, result)
{
if (LogLevel > 8)
sm_syslog(LOG_ERR, NOQID,
- "saslgetrealm: realm %s not in list %s",
- context, *availrealms);
+ "AUTH=client, realm=%s not in list=%s",
+ r, *availrealms);
return SASL_FAIL;
}
}
- *result = (char *)context;
+ *result = r;
return SASL_OK;
}
- /*
+/*
** ITEMINLIST -- does item appear in list?
**
** Check whether item appears in list (which must be separated by a
@@ -952,7 +1300,7 @@ iteminlist(item, list, delim)
len = strlen(item);
while (s != NULL && *s != '\0')
{
- if (strncasecmp(s, item, len) == 0 &&
+ if (sm_strncasecmp(s, item, len) == 0 &&
(s[len] == '\0' || strchr(delim, s[len]) != NULL))
return s;
s = strpbrk(s, delim);
@@ -962,21 +1310,23 @@ iteminlist(item, list, delim)
}
return NULL;
}
- /*
+/*
** REMOVEMECH -- remove item [rem] from list [list]
**
** Parameters:
** rem -- item to remove
** list -- list of items
+** rpool -- resource pool from which result is allocated.
**
** Returns:
** pointer to new list (NULL in case of error).
*/
-char *
-removemech(rem, list)
+static char *
+removemech(rem, list, rpool)
char *rem;
char *list;
+ SM_RPOOL_T *rpool;
{
char *ret;
char *needle;
@@ -999,13 +1349,13 @@ removemech(rem, list)
/* length of string without rem */
len = strlen(list) - strlen(rem);
- if (len == 0)
+ if (len <= 0)
{
- ret = xalloc(1); /* XXX leaked */
+ ret = (char *) sm_rpool_malloc_x(rpool, 1);
*ret = '\0';
return ret;
}
- ret = xalloc(len); /* XXX leaked */
+ ret = (char *) sm_rpool_malloc_x(rpool, len);
memset(ret, '\0', len);
/* copy from start to removed item */
@@ -1024,126 +1374,69 @@ removemech(rem, list)
ret[(needle - list) - 1] = '\0';
return ret;
}
- /*
-** INTERSECT -- create the intersection between two lists
-**
-** Parameters:
-** s1, s2 -- lists of items (separated by single blanks).
-**
-** Returns:
-** the intersection of both lists.
-*/
-
-char *
-intersect(s1, s2)
- char *s1, *s2;
-{
- char *hr, *h1, *h, *res;
- int l1, l2, rl;
-
- if (s1 == NULL || s2 == NULL) /* NULL string(s) -> NULL result */
- return NULL;
- l1 = strlen(s1);
- l2 = strlen(s2);
- rl = min(l1, l2);
- res = (char *)xalloc(rl + 1);
- *res = '\0';
- if (rl == 0) /* at least one string empty? */
- return res;
- hr = res;
- h1 = s1;
- h = s1;
-
- /* walk through s1 */
- while (h != NULL && *h1 != '\0')
- {
- /* is there something after the current word? */
- if ((h = strchr(h1, ' ')) != NULL)
- *h = '\0';
- l1 = strlen(h1);
-
- /* does the current word appear in s2 ? */
- if (iteminlist(h1, s2, " ") != NULL)
- {
- /* add a blank if not first item */
- if (hr != res)
- *hr++ = ' ';
-
- /* copy the item */
- memcpy(hr, h1, l1);
-
- /* advance pointer in result list */
- hr += l1;
- *hr = '\0';
- }
- if (h != NULL)
- {
- /* there are more items */
- *h = ' ';
- h1 = h + 1;
- }
- }
- return res;
-}
- /*
+/*
** ATTEMPTAUTH -- try to AUTHenticate using one mechanism
**
** Parameters:
** m -- the mailer.
** mci -- the mailer connection structure.
** e -- the envelope (including the sender to specify).
-** mechused - filled in with mechanism used
+** sai - sasl authinfo
**
** Returns:
-** EX_OK/EX_TEMPFAIL
+** EX_OK -- authentication was successful.
+** EX_NOPERM -- authentication failed.
+** EX_IOERR -- authentication dialogue failed (I/O problem?).
+** EX_TEMPFAIL -- temporary failure.
+**
*/
-int
-attemptauth(m, mci, e, mechused)
+static int
+attemptauth(m, mci, e, sai)
MAILER *m;
MCI *mci;
ENVELOPE *e;
- char **mechused;
+ SASL_AI_T *sai;
{
int saslresult, smtpresult;
sasl_external_properties_t ssf;
sasl_interact_t *client_interact = NULL;
char *out;
unsigned int outlen;
- static char *mechusing;
+ char *mechusing;
sasl_security_properties_t ssp;
char in64[MAXOUTLEN];
-# if NETINET
+#if NETINET
extern SOCKADDR CurHostAddr;
-# endif /* NETINET */
+#endif /* NETINET */
+
+ /* no mechanism selected (yet) */
+ (*sai)[SASL_MECH] = NULL;
- *mechused = NULL;
+ /* dispose old connection */
if (mci->mci_conn != NULL)
- {
sasl_dispose(&(mci->mci_conn));
- /* just in case, sasl_dispose() should take care of it */
- mci->mci_conn = NULL;
- }
-
/* make a new client sasl connection */
saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
: "smtp",
CurHostName, NULL, 0, &mci->mci_conn);
+ if (saslresult != SASL_OK)
+ return EX_TEMPFAIL;
/* set properties */
(void) memset(&ssp, '\0', sizeof ssp);
-# if SFIO
+
/* XXX should these be options settable via .cf ? */
- /* ssp.min_ssf = 0; is default due to memset() */
+# if STARTTLS
+#endif /* STARTTLS */
{
- ssp.max_ssf = INT_MAX;
+ ssp.max_ssf = MaxSLBits;
ssp.maxbufsize = MAXOUTLEN;
-# if 0
+# if 0
ssp.security_flags = SASL_SEC_NOPLAINTEXT;
-# endif /* 0 */
+# endif /* 0 */
}
-# endif /* SFIO */
saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
if (saslresult != SASL_OK)
return EX_TEMPFAIL;
@@ -1151,19 +1444,19 @@ attemptauth(m, mci, e, mechused)
/* external security strength factor, authentication id */
ssf.ssf = 0;
ssf.auth_id = NULL;
-# if _FFR_EXT_MECH
- out = macvalue(macid("{cert_subject}", NULL), e);
+#if STARTTLS
+ out = macvalue(macid("{cert_subject}"), e);
if (out != NULL && *out != '\0')
ssf.auth_id = out;
- out = macvalue(macid("{cipher_bits}", NULL), e);
+ out = macvalue(macid("{cipher_bits}"), e);
if (out != NULL && *out != '\0')
ssf.ssf = atoi(out);
-# endif /* _FFR_EXT_MECH */
+#endif /* STARTTLS */
saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
if (saslresult != SASL_OK)
return EX_TEMPFAIL;
-# if NETINET
+#if NETINET
/* set local/remote ipv4 addresses */
if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
{
@@ -1175,7 +1468,8 @@ attemptauth(m, mci, e, mechused)
!= SASL_OK)
return EX_TEMPFAIL;
addrsize = sizeof(struct sockaddr_in);
- if (getsockname(fileno(mci->mci_out),
+ if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
+ NULL),
(struct sockaddr *) &saddr_l, &addrsize) == 0)
{
if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
@@ -1183,28 +1477,26 @@ attemptauth(m, mci, e, mechused)
return EX_TEMPFAIL;
}
}
-# endif /* NETINET */
+#endif /* NETINET */
/* start client side of sasl */
saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
NULL, &client_interact,
&out, &outlen,
(const char **)&mechusing);
- callbacks[CB_AUTHNAME_IDX].context = mechusing;
if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
{
-# if SFIO
if (saslresult == SASL_NOMECH && LogLevel > 8)
{
sm_syslog(LOG_NOTICE, e->e_id,
- "available AUTH mechanisms do not fulfill requirements");
+ "AUTH=client, available mechanisms do not fulfill requirements");
}
-# endif /* SFIO */
return EX_TEMPFAIL;
}
- *mechused = mechusing;
+ /* just point current mechanism to the data in the sasl library */
+ (*sai)[SASL_MECH] = mechusing;
/* send the info across the wire */
if (outlen > 0)
@@ -1223,30 +1515,30 @@ attemptauth(m, mci, e, mechused)
{
smtpmessage("AUTH %s", m, mci, mechusing);
}
+ sm_sasl_free(out); /* XXX only if no rpool is used */
/* get the reply */
- smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, getsasldata, NULL);
- /* which timeout? XXX */
+ smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL);
for (;;)
{
/* check return code from server */
if (smtpresult == 235)
{
- define(macid("{auth_type}", NULL),
- newstr(mechusing), e);
-# if !SFIO
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, NOQID,
- "SASL: outgoing connection to %.64s: mech=%.16s",
- mci->mci_host, mechusing);
-# endif /* !SFIO */
+ macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
+ mechusing);
return EX_OK;
}
if (smtpresult == -1)
return EX_IOERR;
- if (smtpresult != 334)
+ if (REPLYTYPE(smtpresult) == 5)
+ return EX_NOPERM; /* ugly, but ... */
+ if (REPLYTYPE(smtpresult) != 3)
+ {
+ /* should we fail deliberately, see RFC 2554 4. ? */
+ /* smtpmessage("*", m, mci); */
return EX_TEMPFAIL;
+ }
saslresult = sasl_client_step(mci->mci_conn,
mci->mci_sasl_string,
@@ -1257,11 +1549,11 @@ attemptauth(m, mci, e, mechused)
if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
{
if (tTd(95, 5))
- dprintf("AUTH FAIL: %s (%d)\n",
+ sm_dprintf("AUTH FAIL=%s (%d)\n",
sasl_errstring(saslresult, NULL, NULL),
saslresult);
- /* fail deliberately, see RFC 2254 4. */
+ /* fail deliberately, see RFC 2554 4. */
smtpmessage("*", m, mci);
/*
@@ -1269,9 +1561,9 @@ attemptauth(m, mci, e, mechused)
** mechanism; how to do that?
*/
- smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
+ smtpresult = reply(m, mci, e, TimeOuts.to_auth,
getsasldata, NULL);
- return EX_TEMPFAIL;
+ return EX_NOPERM;
}
if (outlen > 0)
@@ -1287,15 +1579,14 @@ attemptauth(m, mci, e, mechused)
}
else
in64[0] = '\0';
+ sm_sasl_free(out); /* XXX only if no rpool is used */
smtpmessage("%s", m, mci, in64);
- smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
+ smtpresult = reply(m, mci, e, TimeOuts.to_auth,
getsasldata, NULL);
- /* which timeout? XXX */
}
/* NOTREACHED */
}
-
- /*
+/*
** SMTPAUTH -- try to AUTHenticate
**
** This will try mechanisms in the order the sasl library decided until:
@@ -1309,7 +1600,16 @@ attemptauth(m, mci, e, mechused)
** e -- the envelope.
**
** Returns:
-** EX_OK/EX_TEMPFAIL
+** EX_OK -- authentication was successful
+** EX_UNAVAILABLE -- authentication not possible, e.g.,
+** no data available.
+** EX_NOPERM -- authentication failed.
+** EX_TEMPFAIL -- temporary failure.
+**
+** Notice: AuthInfo is used for all connections, hence we must
+** return EX_TEMPFAIL only if we really want to retry, i.e.,
+** iff getauth() tempfailed or getauth() was used and
+** authentication tempfailed.
*/
int
@@ -1319,59 +1619,94 @@ smtpauth(m, mci, e)
ENVELOPE *e;
{
int result;
- char *mechused;
- char *h;
- static char *defrealm = NULL;
- static char *mechs = NULL;
+ int i;
+ bool usedgetauth;
- mci->mci_sasl_auth = FALSE;
- if (defrealm == NULL)
- {
- h = readauth(SASL_DEFREALM, SASLInfo, TRUE);
- if (h != NULL && *h != '\0')
- defrealm = newstr(h);
- }
- if (defrealm == NULL || *defrealm == '\0')
- defrealm = newstr(macvalue('j', CurEnv));
- callbacks[CB_GETREALM_IDX].context = defrealm;
+ mci->mci_sasl_auth = false;
+ for (i = 0; i < SASL_MECH ; i++)
+ mci->mci_sai[i] = NULL;
-# if _FFR_DEFAUTHINFO_MECHS
- if (mechs == NULL)
+ result = getauth(mci, e, &(mci->mci_sai));
+ if (result == EX_TEMPFAIL)
+ return result;
+ usedgetauth = true;
+
+ /* no data available: don't try to authenticate */
+ if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
+ return result;
+ if (result != EX_OK)
{
- h = readauth(SASL_MECH, SASLInfo, TRUE);
- if (h != NULL && *h != '\0')
- mechs = newstr(h);
+ if (SASLInfo == NULL)
+ return EX_UNAVAILABLE;
+
+ /* read authinfo from file */
+ result = readauth(SASLInfo, true, &(mci->mci_sai),
+ mci->mci_rpool);
+ if (result != EX_OK)
+ return result;
+ usedgetauth = false;
}
-# endif /* _FFR_DEFAUTHINFO_MECHS */
- if (mechs == NULL || *mechs == '\0')
- mechs = AuthMechanisms;
- mci->mci_saslcap = intersect(mechs, mci->mci_saslcap);
+
+ /* check whether sufficient data is available */
+ if (mci->mci_sai[SASL_PASSWORD] == NULL ||
+ *(mci->mci_sai)[SASL_PASSWORD] == '\0')
+ return EX_UNAVAILABLE;
+ if ((mci->mci_sai[SASL_AUTHID] == NULL ||
+ *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
+ (mci->mci_sai[SASL_USER] == NULL ||
+ *(mci->mci_sai)[SASL_USER] == '\0'))
+ return EX_UNAVAILABLE;
+
+ /* set the context for the callback function to sai */
+ callbacks[CB_PASS_IDX].context = (void *)&mci->mci_sai;
+ callbacks[CB_USER_IDX].context = (void *)&mci->mci_sai;
+ callbacks[CB_AUTHNAME_IDX].context = (void *)&mci->mci_sai;
+ callbacks[CB_GETREALM_IDX].context = (void *)&mci->mci_sai;
+#if 0
+ callbacks[CB_SAFESASL_IDX].context = (void *)&mci->mci_sai;
+#endif /* 0 */
+
+ /* set default value for realm */
+ if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
+ (mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
+ macvalue('j', CurEnv));
+
+ /* set default value for list of mechanism to use */
+ if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
+ *(mci->mci_sai)[SASL_MECHLIST] == '\0')
+ (mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
+
+ /* create list of mechanisms to try */
+ mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
+ mci->mci_saslcap, mci->mci_rpool);
/* initialize sasl client library */
- result = sasl_client_init(callbacks);
+ result = init_sasl_client();
if (result != SASL_OK)
- return EX_TEMPFAIL;
+ return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
do
{
- result = attemptauth(m, mci, e, &mechused);
+ result = attemptauth(m, mci, e, &(mci->mci_sai));
if (result == EX_OK)
- mci->mci_sasl_auth = TRUE;
- else if (result == EX_TEMPFAIL)
+ mci->mci_sasl_auth = true;
+ else if (result == EX_TEMPFAIL || result == EX_NOPERM)
{
- mci->mci_saslcap = removemech(mechused,
- mci->mci_saslcap);
+ mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
+ mci->mci_saslcap,
+ mci->mci_rpool);
if (mci->mci_saslcap == NULL ||
*(mci->mci_saslcap) == '\0')
- return EX_TEMPFAIL;
+ return usedgetauth ? result
+ : EX_UNAVAILABLE;
}
- else /* all others for now */
- return EX_TEMPFAIL;
+ else
+ return result;
} while (result != EX_OK);
return result;
}
-# endif /* SASL */
+#endif /* SASL */
- /*
+/*
** SMTPMAILFROM -- send MAIL command
**
** Parameters:
@@ -1389,18 +1724,31 @@ smtpmailfrom(m, mci, e)
int r;
char *bufp;
char *bodytype;
+ char *enhsc;
char buf[MAXNAME + 1];
char optbuf[MAXLINE];
- char *enhsc;
if (tTd(18, 2))
- dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
+ sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
enhsc = NULL;
+ /*
+ ** Check if connection is gone, if so
+ ** it's a tempfail and we use mci_errno
+ ** for the reason.
+ */
+
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ errno = mci->mci_errno;
+ return EX_TEMPFAIL;
+ }
+
/* set up appropriate options to include */
if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
{
- snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize);
+ (void) sm_snprintf(optbuf, sizeof optbuf, " SIZE=%ld",
+ e->e_msgsize);
bufp = &optbuf[strlen(optbuf)];
}
else
@@ -1421,7 +1769,7 @@ smtpmailfrom(m, mci, e)
if (bodytype != NULL &&
SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
{
- snprintf(bufp, SPACELEFT(optbuf, bufp),
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
" BODY=%s", bodytype);
bufp += strlen(bufp);
}
@@ -1433,7 +1781,7 @@ smtpmailfrom(m, mci, e)
/* EMPTY */
/* just pass it through */
}
-# if MIME8TO7
+#if MIME8TO7
else if (bitset(MM_CVTMIME, MimeMode) &&
!bitset(EF_DONT_MIME, e->e_flags) &&
(!bitset(MM_PASS8BIT, MimeMode) ||
@@ -1442,7 +1790,7 @@ smtpmailfrom(m, mci, e)
/* must convert from 8bit MIME format to 7bit encoded */
mci->mci_flags |= MCIF_CVT8TO7;
}
-# endif /* MIME8TO7 */
+#endif /* MIME8TO7 */
else if (!bitset(MM_PASS8BIT, MimeMode))
{
/* cannot just send a 8-bit version */
@@ -1458,7 +1806,7 @@ smtpmailfrom(m, mci, e)
if (e->e_envid != NULL &&
SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
{
- snprintf(bufp, SPACELEFT(optbuf, bufp),
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
" ENVID=%s", e->e_envid);
bufp += strlen(bufp);
}
@@ -1467,7 +1815,7 @@ smtpmailfrom(m, mci, e)
if (bitset(EF_RET_PARAM, e->e_flags) &&
SPACELEFT(optbuf, bufp) > 9)
{
- snprintf(bufp, SPACELEFT(optbuf, bufp),
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
" RET=%s",
bitset(EF_NO_BODY_RETN, e->e_flags) ?
"HDRS" : "FULL");
@@ -1477,22 +1825,50 @@ smtpmailfrom(m, mci, e)
if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
-# if SASL
+#if SASL
&& (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
-# endif /* SASL */
+#endif /* SASL */
)
{
- snprintf(bufp, SPACELEFT(optbuf, bufp),
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
" AUTH=%s", e->e_auth_param);
bufp += strlen(bufp);
}
/*
+ ** 17 is the max length required, we could use log() to compute
+ ** the exact length (and check IS_DLVR_TRACE())
+ */
+
+ if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
+ IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
+ {
+ long dby;
+
+ /*
+ ** Avoid problems with delays (for R) since the check
+ ** in deliver() whether min-deliver-time is sufficient.
+ ** Alternatively we could pass the computed time to this
+ ** function.
+ */
+
+ dby = e->e_deliver_by - (curtime() - e->e_ctime);
+ if (dby <= 0 && IS_DLVR_RETURN(e))
+ dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
+ " BY=%ld;%c%s",
+ dby,
+ IS_DLVR_RETURN(e) ? 'R' : 'N',
+ IS_DLVR_TRACE(e) ? "T" : "");
+ bufp += strlen(bufp);
+ }
+
+ /*
** Send the MAIL command.
** Designates the sender.
*/
- mci->mci_state = MCIS_ACTIVE;
+ mci->mci_state = MCIS_MAIL;
if (bitset(EF_RESPONSE, e->e_flags) &&
!bitnset(M_NO_NULL_FROM, m->m_flags))
@@ -1520,13 +1896,12 @@ smtpmailfrom(m, mci, e)
*bufp == '@' ? ',' : ':', bufp, optbuf);
}
SmtpPhase = mci->mci_phase = "client MAIL";
- sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
+ sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
CurHostName, mci->mci_phase);
r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc);
if (r < 0)
{
/* communications failure */
- mci->mci_errno = errno;
mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
smtpquit(m, mci, e);
return EX_TEMPFAIL;
@@ -1594,7 +1969,7 @@ smtpmailfrom(m, mci, e)
smtpquit(m, mci, e);
return EX_PROTOCOL;
}
- /*
+/*
** SMTPRCPT -- designate recipient.
**
** Parameters:
@@ -1611,58 +1986,104 @@ smtpmailfrom(m, mci, e)
*/
int
-smtprcpt(to, m, mci, e)
+smtprcpt(to, m, mci, e, ctladdr, xstart)
ADDRESS *to;
register MAILER *m;
MCI *mci;
ENVELOPE *e;
+ ADDRESS *ctladdr;
+ time_t xstart;
{
- register int r;
char *bufp;
char optbuf[MAXLINE];
- char *enhsc;
- enhsc = NULL;
+#if PIPELINING
+ /*
+ ** If there is status waiting from the other end, read it.
+ ** This should normally happen because of SMTP pipelining.
+ */
+
+ while (mci->mci_nextaddr != NULL &&
+ sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL))
+ {
+ int r;
+
+ r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
+ if (r != EX_OK)
+ {
+ markfailure(e, mci->mci_nextaddr, mci, r, false);
+ giveresponse(r, mci->mci_nextaddr->q_status, m, mci,
+ ctladdr, xstart, e, to);
+ }
+ mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
+ }
+#endif /* PIPELINING */
+
+ /*
+ ** Check if connection is gone, if so
+ ** it's a tempfail and we use mci_errno
+ ** for the reason.
+ */
+
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ errno = mci->mci_errno;
+ return EX_TEMPFAIL;
+ }
+
optbuf[0] = '\0';
bufp = optbuf;
/*
- ** warning: in the following it is assumed that the free space
+ ** Warning: in the following it is assumed that the free space
** in bufp is sizeof optbuf
*/
+
if (bitset(MCIF_DSN, mci->mci_flags))
{
+ if (IS_DLVR_NOTIFY(e) &&
+ !bitset(MCIF_DLVR_BY, mci->mci_flags))
+ {
+ /* RFC 2852: 4.1.4.2 */
+ if (!bitset(QHASNOTIFY, to->q_flags))
+ to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
+ else if (bitset(QPINGONSUCCESS, to->q_flags) ||
+ bitset(QPINGONFAILURE, to->q_flags) ||
+ bitset(QPINGONDELAY, to->q_flags))
+ to->q_flags |= QPINGONDELAY;
+ }
+
/* NOTIFY= parameter */
if (bitset(QHASNOTIFY, to->q_flags) &&
bitset(QPRIMARY, to->q_flags) &&
!bitnset(M_LOCALMAILER, m->m_flags))
{
- bool firstone = TRUE;
+ bool firstone = true;
- (void) strlcat(bufp, " NOTIFY=", sizeof optbuf);
+ (void) sm_strlcat(bufp, " NOTIFY=", sizeof optbuf);
if (bitset(QPINGONSUCCESS, to->q_flags))
{
- (void) strlcat(bufp, "SUCCESS", sizeof optbuf);
- firstone = FALSE;
+ (void) sm_strlcat(bufp, "SUCCESS", sizeof optbuf);
+ firstone = false;
}
if (bitset(QPINGONFAILURE, to->q_flags))
{
if (!firstone)
- (void) strlcat(bufp, ",",
+ (void) sm_strlcat(bufp, ",",
sizeof optbuf);
- (void) strlcat(bufp, "FAILURE", sizeof optbuf);
- firstone = FALSE;
+ (void) sm_strlcat(bufp, "FAILURE", sizeof optbuf);
+ firstone = false;
}
if (bitset(QPINGONDELAY, to->q_flags))
{
if (!firstone)
- (void) strlcat(bufp, ",",
+ (void) sm_strlcat(bufp, ",",
sizeof optbuf);
- (void) strlcat(bufp, "DELAY", sizeof optbuf);
- firstone = FALSE;
+ (void) sm_strlcat(bufp, "DELAY", sizeof optbuf);
+ firstone = false;
}
if (firstone)
- (void) strlcat(bufp, "NEVER", sizeof optbuf);
+ (void) sm_strlcat(bufp, "NEVER", sizeof optbuf);
bufp += strlen(bufp);
}
@@ -1670,39 +2091,113 @@ smtprcpt(to, m, mci, e)
if (to->q_orcpt != NULL &&
SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
{
- snprintf(bufp, SPACELEFT(optbuf, bufp),
+ (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
" ORCPT=%s", to->q_orcpt);
bufp += strlen(bufp);
}
}
smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
+ mci->mci_state = MCIS_RCPT;
SmtpPhase = mci->mci_phase = "client RCPT";
- sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
+ sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
CurHostName, mci->mci_phase);
+
+#if PIPELINING
+ /*
+ ** If running SMTP pipelining, we will pick up status later
+ */
+
+ if (bitset(MCIF_PIPELINED, mci->mci_flags))
+ return EX_OK;
+#endif /* PIPELINING */
+
+ return smtprcptstat(to, m, mci, e);
+}
+/*
+** SMTPRCPTSTAT -- get recipient status
+**
+** This is only called during SMTP pipelining
+**
+** Parameters:
+** to -- address of recipient.
+** m -- mailer being sent to.
+** mci -- the mailer connection information.
+** e -- the envelope for this message.
+**
+** Returns:
+** EX_* -- protocol status
+*/
+
+static int
+smtprcptstat(to, m, mci, e)
+ ADDRESS *to;
+ MAILER *m;
+ register MCI *mci;
+ register ENVELOPE *e;
+{
+ int r;
+ int save_errno;
+ char *enhsc;
+
+ /*
+ ** Check if connection is gone, if so
+ ** it's a tempfail and we use mci_errno
+ ** for the reason.
+ */
+
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ errno = mci->mci_errno;
+ return EX_TEMPFAIL;
+ }
+
+ enhsc = NULL;
r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc);
- to->q_rstatus = newstr(SmtpReplyBuffer);
- to->q_status = ENHSCN(enhsc, smtptodsn(r));
+ save_errno = errno;
+ to->q_rstatus = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
+ to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
if (!bitnset(M_LMTP, m->m_flags))
to->q_statmta = mci->mci_host;
if (r < 0 || REPLYTYPE(r) == 4)
+ {
+ mci->mci_retryrcpt = true;
+ errno = save_errno;
return EX_TEMPFAIL;
+ }
else if (REPLYTYPE(r) == 2)
+ {
+ char *t;
+
+ if ((t = mci->mci_tolist) != NULL)
+ {
+ char *p;
+
+ *t++ = ',';
+ for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
+ continue;
+ *t = '\0';
+ mci->mci_tolist = t;
+ }
+#if PIPELINING
+ mci->mci_okrcpts++;
+#endif /* PIPELINING */
return EX_OK;
+ }
else if (r == 550)
{
- to->q_status = ENHSCN(enhsc, "5.1.1");
+ to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
return EX_NOUSER;
}
else if (r == 551)
{
- to->q_status = ENHSCN(enhsc, "5.1.6");
+ to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
return EX_NOUSER;
}
else if (r == 553)
{
- to->q_status = ENHSCN(enhsc, "5.1.3");
+ to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
return EX_NOUSER;
}
else if (REPLYTYPE(r) == 5)
@@ -1722,7 +2217,7 @@ smtprcpt(to, m, mci, e)
SmtpReplyBuffer);
return EX_PROTOCOL;
}
- /*
+/*
** SMTPDATA -- send the data and clean up the transaction.
**
** Parameters:
@@ -1732,19 +2227,18 @@ smtprcpt(to, m, mci, e)
**
** Returns:
** exit status corresponding to DATA command.
-**
-** Side Effects:
-** none.
*/
static jmp_buf CtxDataTimeout;
-static EVENT *volatile DataTimeout = NULL;
+static SM_EVENT *volatile DataTimeout = NULL;
int
-smtpdata(m, mci, e)
+smtpdata(m, mci, e, ctladdr, xstart)
MAILER *m;
register MCI *mci;
register ENVELOPE *e;
+ ADDRESS *ctladdr;
+ time_t xstart;
{
register int r;
int rstat;
@@ -1752,30 +2246,79 @@ smtpdata(m, mci, e)
time_t timeout;
char *enhsc;
+ /*
+ ** Check if connection is gone, if so
+ ** it's a tempfail and we use mci_errno
+ ** for the reason.
+ */
+
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ errno = mci->mci_errno;
+ return EX_TEMPFAIL;
+ }
+
enhsc = NULL;
/*
** Send the data.
** First send the command and check that it is ok.
- ** Then send the data.
+ ** Then send the data (if there are valid recipients).
** 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, mci);
+
+#if PIPELINING
+ if (mci->mci_nextaddr != NULL)
+ {
+ char *oldto = e->e_to;
+
+ /* pick up any pending RCPT responses for SMTP pipelining */
+ while (mci->mci_nextaddr != NULL)
+ {
+ int r;
+
+ e->e_to = mci->mci_nextaddr->q_paddr;
+ r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
+ if (r != EX_OK)
+ {
+ markfailure(e, mci->mci_nextaddr, mci, r,
+ false);
+ giveresponse(r, mci->mci_nextaddr->q_status, m,
+ mci, ctladdr, xstart, e,
+ mci->mci_nextaddr);
+ if (r == EX_TEMPFAIL)
+ mci->mci_nextaddr->q_state = QS_RETRY;
+ }
+ mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
+ }
+ e->e_to = oldto;
+ }
+#endif /* PIPELINING */
+
+ /* now proceed with DATA phase */
SmtpPhase = mci->mci_phase = "client DATA 354";
- sm_setproctitle(TRUE, e, "%s %s: %s",
+ mci->mci_state = MCIS_DATA;
+ sm_setproctitle(true, e, "%s %s: %s",
qid_printname(e), CurHostName, mci->mci_phase);
r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc);
if (r < 0 || REPLYTYPE(r) == 4)
{
smtpquit(m, mci, e);
+ errno = mci->mci_errno;
return EX_TEMPFAIL;
}
else if (REPLYTYPE(r) == 5)
{
smtprset(m, mci, e);
+#if PIPELINING
+ if (mci->mci_okrcpts <= 0)
+ return mci->mci_retryrcpt ? EX_TEMPFAIL
+ : EX_UNAVAILABLE;
+#endif /* PIPELINING */
return EX_UNAVAILABLE;
}
else if (REPLYTYPE(r) != 3)
@@ -1790,9 +2333,19 @@ smtpdata(m, mci, e)
smtprset(m, mci, e);
mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
SmtpReplyBuffer);
+#if PIPELINING
+ if (mci->mci_okrcpts <= 0)
+ return mci->mci_retryrcpt ? EX_TEMPFAIL
+ : EX_PROTOCOL;
+#endif /* PIPELINING */
return EX_PROTOCOL;
}
+#if PIPELINING
+ if (mci->mci_okrcpts > 0)
+ {
+#endif /* PIPELINING */
+
/*
** Set timeout around data writes. Make it at least large
** enough for DNS timeouts on all recipients plus some fudge
@@ -1828,7 +2381,7 @@ smtpdata(m, mci, e)
else
timeout = DATA_PROGRESS_TIMEOUT;
- DataTimeout = setevent(timeout, datatimeout, 0);
+ DataTimeout = sm_setevent(timeout, datatimeout, 0);
/*
@@ -1850,42 +2403,36 @@ smtpdata(m, mci, e)
*/
if (DataTimeout != NULL)
- clrevent(DataTimeout);
+ sm_clrevent(DataTimeout);
-# if _FFR_CATCH_BROKEN_MTAS
- {
- fd_set readfds;
- struct timeval timeout;
+#if PIPELINING
+ }
+#endif /* PIPELINING */
- 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(">>> .");
+#if _FFR_CATCH_BROKEN_MTAS
+ if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL))
+ {
+ /* terminate the message */
+ (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
+ m->m_eol);
+ if (TrafficLogFile != NULL)
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d >>> .\n", (int) CurrentPid);
+ if (Verbose)
+ nmessage(">>> .");
- sm_syslog(LOG_CRIT, e->e_id,
- "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
- CurHostName);
- 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;
- }
+ sm_syslog(LOG_CRIT, e->e_id,
+ "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
+ CurHostName);
+ 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 */
+#endif /* _FFR_CATCH_BROKEN_MTAS */
- if (ferror(mci->mci_out))
+ if (sm_io_error(mci->mci_out))
{
/* error during processing -- don't send the dot */
mci->mci_errno = EIO;
@@ -1896,15 +2443,16 @@ smtpdata(m, mci, e)
}
/* terminate the message */
- fprintf(mci->mci_out, ".%s", m->m_eol);
+ (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s", m->m_eol);
if (TrafficLogFile != NULL)
- fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid());
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d >>> .\n", (int) CurrentPid);
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),
+ sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
CurHostName, mci->mci_phase);
if (bitnset(M_LMTP, m->m_flags))
return EX_OK;
@@ -1920,26 +2468,24 @@ smtpdata(m, mci, e)
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 (REPLYCLASS(r) != 5)
+ rstat = xstat = EX_PROTOCOL;
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)
- sm_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]);
+ e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
SmtpPhase = mci->mci_phase = "idle";
- sm_setproctitle(TRUE, e, "%s: %s", CurHostName, mci->mci_phase);
+ sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
if (rstat != EX_PROTOCOL)
return rstat;
if (LogLevel > 1)
@@ -1952,7 +2498,6 @@ smtpdata(m, mci, e)
return rstat;
}
-
static void
datatimeout()
{
@@ -1978,8 +2523,8 @@ datatimeout()
timeout = DATA_PROGRESS_TIMEOUT;
/* reset the timeout */
- DataTimeout = sigsafe_setevent(timeout, datatimeout, 0);
- DataProgress = FALSE;
+ DataTimeout = sm_sigsafe_setevent(timeout, datatimeout, 0);
+ DataProgress = false;
}
else
{
@@ -1993,10 +2538,9 @@ datatimeout()
errno = ETIMEDOUT;
longjmp(CtxDataTimeout, 1);
}
-
errno = save_errno;
}
- /*
+/*
** SMTPGETSTAT -- get status code from DATA in LMTP
**
** Parameters:
@@ -2015,10 +2559,11 @@ smtpgetstat(m, mci, e)
ENVELOPE *e;
{
int r;
- int status;
+ int status, xstat;
char *enhsc;
enhsc = NULL;
+
/* check for the results of the transaction */
r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
if (r < 0)
@@ -2026,25 +2571,24 @@ smtpgetstat(m, mci, e)
smtpquit(m, mci, e);
return EX_TEMPFAIL;
}
+ xstat = EX_NOTSTICKY;
if (REPLYTYPE(r) == 4)
status = EX_TEMPFAIL;
- else if (REPLYCLASS(r) != 5)
- status = EX_PROTOCOL;
else if (REPLYTYPE(r) == 2)
- status = EX_OK;
+ status = xstat = EX_OK;
+ else if (REPLYCLASS(r) != 5)
+ status = xstat = EX_PROTOCOL;
else if (REPLYTYPE(r) == 5)
status = EX_UNAVAILABLE;
else
status = EX_PROTOCOL;
- if (e->e_statmsg != NULL)
- sm_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)),
+ e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
+ mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
SmtpReplyBuffer);
if (LogLevel > 1 && status == EX_PROTOCOL)
{
@@ -2055,7 +2599,7 @@ smtpgetstat(m, mci, e)
}
return status;
}
- /*
+/*
** SMTPQUIT -- close the SMTP connection.
**
** Parameters:
@@ -2078,11 +2622,17 @@ smtpquit(m, mci, e)
{
bool oldSuprErrs = SuprErrs;
int rcode;
+ char *oldcurhost;
+ oldcurhost = CurHostName;
CurHostName = mci->mci_host; /* XXX UGLY XXX */
if (CurHostName == NULL)
CurHostName = MyHostName;
+#if PIPELINING
+ mci->mci_okrcpts = 0;
+#endif /* PIPELINING */
+
/*
** Suppress errors here -- we may be processing a different
** job when we do the quit connection, and we don't want the
@@ -2090,7 +2640,7 @@ smtpquit(m, mci, e)
** problem.
*/
- SuprErrs = TRUE;
+ SuprErrs = true;
/* send the quit message if we haven't gotten I/O error */
if (mci->mci_state != MCIS_ERROR &&
@@ -2105,7 +2655,7 @@ smtpquit(m, mci, e)
SuprErrs = oldSuprErrs;
if (mci->mci_state == MCIS_CLOSED ||
origstate == MCIS_CLOSED)
- return;
+ goto end;
}
/* now actually close the connection and pick up the zombie */
@@ -2127,8 +2677,12 @@ smtpquit(m, mci, e)
}
SuprErrs = oldSuprErrs;
+
+ end:
+ CurHostName = oldcurhost;
+ return;
}
- /*
+/*
** SMTPRSET -- send a RSET (reset) command
**
** Parameters:
@@ -2155,6 +2709,22 @@ smtprset(m, mci, e)
if (CurHostName == NULL)
CurHostName = MyHostName;
+#if PIPELINING
+ mci->mci_okrcpts = 0;
+#endif /* PIPELINING */
+
+ /*
+ ** Check if connection is gone, if so
+ ** it's a tempfail and we use mci_errno
+ ** for the reason.
+ */
+
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ errno = mci->mci_errno;
+ return;
+ }
+
SmtpPhase = "client RSET";
smtpmessage("RSET", m, mci);
r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL);
@@ -2177,7 +2747,7 @@ smtprset(m, mci, e)
}
smtpquit(m, mci, e);
}
- /*
+/*
** SMTPPROBE -- check the connection state
**
** Parameters:
@@ -2211,7 +2781,7 @@ smtpprobe(mci)
smtpquit(m, mci, e);
return r;
}
- /*
+/*
** REPLY -- read arpanet reply
**
** Parameters:
@@ -2221,6 +2791,7 @@ smtpprobe(mci)
** timeout -- the timeout for reads.
** pfunc -- processing function called on each line of response.
** If null, no special processing is done.
+** enhstat -- optional, returns enhanced error code string (if set)
**
** Returns:
** reply code it reads.
@@ -2240,16 +2811,25 @@ reply(m, mci, e, timeout, pfunc, enhstat)
{
register char *bufp;
register int r;
- bool firstline = TRUE;
+ bool firstline = true;
char junkbuf[MAXLINE];
static char enhstatcode[ENHSCLEN];
int save_errno;
+ /*
+ ** Flush the output before reading response.
+ **
+ ** For SMTP pipelining, it would be better if we didn't do
+ ** this if there was already data waiting to be read. But
+ ** to do it properly means pushing it to the I/O library,
+ ** since it really needs to be done below the buffer layer.
+ */
+
if (mci->mci_out != NULL)
- (void) fflush(mci->mci_out);
+ (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
if (tTd(18, 1))
- dprintf("reply\n");
+ sm_dprintf("reply\n");
/*
** Read the input line, being careful not to hang.
@@ -2261,18 +2841,28 @@ reply(m, mci, e, timeout, pfunc, enhstat)
register char *p;
/* actually do the read */
- if (e->e_xfp != NULL)
- (void) fflush(e->e_xfp); /* for debugging */
+ if (e->e_xfp != NULL) /* for debugging */
+ (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
/* if we are in the process of closing just give the code */
if (mci->mci_state == MCIS_CLOSED)
return SMTPCLOSING;
+ /* don't try to read from a non-existant fd */
+ if (mci->mci_in == NULL)
+ {
+ if (mci->mci_errno == 0)
+ mci->mci_errno = EBADF;
+ errno = mci->mci_errno;
+ return -1;
+ }
+
if (mci->mci_out != NULL)
- (void) fflush(mci->mci_out);
+ (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
/* get the line from the other side */
p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
+ save_errno = errno;
mci->mci_lastuse = curtime();
if (p == NULL)
@@ -2281,18 +2871,25 @@ reply(m, mci, e, timeout, pfunc, enhstat)
extern char MsgBuf[];
/* if the remote end closed early, fake an error */
+ errno = save_errno;
if (errno == 0)
-# ifdef ECONNRESET
+ {
+ (void) sm_snprintf(SmtpReplyBuffer,
+ sizeof SmtpReplyBuffer,
+ "421 4.4.1 Connection reset by %s",
+ CURHOSTNAME);
+#ifdef ECONNRESET
errno = ECONNRESET;
-# else /* ECONNRESET */
+#else /* ECONNRESET */
errno = EPIPE;
-# endif /* ECONNRESET */
+#endif /* ECONNRESET */
+ }
mci->mci_errno = errno;
oldholderrs = HoldErrs;
- HoldErrs = TRUE;
+ HoldErrs = true;
usrerr("451 4.4.1 reply: read error from %s",
- CurHostName == NULL ? "NO_HOST" : CurHostName);
+ CURHOSTNAME);
/* errors on QUIT should not be persistent */
if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0)
@@ -2302,35 +2899,31 @@ reply(m, mci, e, timeout, pfunc, enhstat)
if (tTd(18, 100))
(void) pause();
mci->mci_state = MCIS_ERROR;
- save_errno = errno;
smtpquit(m, mci, e);
-# if XDEBUG
+#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;
+ (void) sm_snprintf(p,
+ SPACELEFT(wbuf, p),
+ "%s... ",
+ shortenstring(e->e_to, MAXSHORTSTR));
+ p += strlen(p);
}
- snprintf(p, wbufleft, "reply(%.100s) during %s",
- CurHostName == NULL ? "NO_HOST" : CurHostName,
- SmtpPhase);
+ (void) sm_snprintf(p, SPACELEFT(wbuf, p),
+ "reply(%.100s) during %s",
+ CURHOSTNAME, SmtpPhase);
checkfd012(wbuf);
}
-# endif /* XDEBUG */
- errno = save_errno;
+#endif /* XDEBUG */
HoldErrs = oldholderrs;
+ errno = save_errno;
return -1;
}
- fixcrlf(bufp, TRUE);
+ fixcrlf(bufp, true);
/* EHLO failure is not a real error */
if (e->e_xfp != NULL && (bufp[0] == '4' ||
@@ -2340,17 +2933,20 @@ reply(m, mci, e, timeout, pfunc, enhstat)
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;
+ (void) sm_io_fprintf(CurEnv->e_xfp,
+ SM_TIME_DEFAULT,
+ "... while talking to %s:\n",
+ CURHOSTNAME);
+ SmtpNeedIntro = false;
}
if (SmtpMsgBuffer[0] != '\0')
- fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ ">>> %s\n", SmtpMsgBuffer);
SmtpMsgBuffer[0] = '\0';
/* now log the message as from the other side */
- fprintf(e->e_xfp, "<<< %s\n", bufp);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "<<< %s\n", bufp);
}
/* display the input for verbose mode */
@@ -2370,7 +2966,7 @@ reply(m, mci, e, timeout, pfunc, enhstat)
if (pfunc != NULL)
(*pfunc)(bufp, firstline, m, mci, e);
- firstline = FALSE;
+ firstline = false;
/* decode the reply code */
r = atoi(bufp);
@@ -2393,9 +2989,8 @@ reply(m, mci, e, timeout, pfunc, enhstat)
*/
/* 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);
+ if (SmtpReplyBuffer[0] == '4')
+ (void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof SmtpError);
/* reply code 421 is "Service Shutting Down" */
if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
@@ -2407,7 +3002,7 @@ reply(m, mci, e, timeout, pfunc, enhstat)
return r;
}
- /*
+/*
** SMTPMESSAGE -- send message to server
**
** Parameters:
@@ -2424,36 +3019,36 @@ reply(m, mci, e, timeout, pfunc, enhstat)
/*VARARGS1*/
void
-# ifdef __STDC__
+#ifdef __STDC__
smtpmessage(char *f, MAILER *m, MCI *mci, ...)
-# else /* __STDC__ */
+#else /* __STDC__ */
smtpmessage(f, m, mci, va_alist)
char *f;
MAILER *m;
MCI *mci;
va_dcl
-# endif /* __STDC__ */
+#endif /* __STDC__ */
{
- VA_LOCAL_DECL
+ SM_VA_LOCAL_DECL
- VA_START(mci);
- (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
- VA_END;
+ SM_VA_START(ap, mci);
+ (void) sm_vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
+ SM_VA_END(ap);
if (tTd(18, 1) || Verbose)
nmessage(">>> %s", SmtpMsgBuffer);
if (TrafficLogFile != NULL)
- fprintf(TrafficLogFile, "%05d >>> %s\n",
- (int) getpid(), SmtpMsgBuffer);
+ (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
+ "%05d >>> %s\n", (int) CurrentPid,
+ SmtpMsgBuffer);
if (mci->mci_out != NULL)
{
- fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
- m == NULL ? "\r\n" : m->m_eol);
+ (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
+ SmtpMsgBuffer, m == NULL ? "\r\n"
+ : m->m_eol);
}
else if (tTd(18, 1))
{
- dprintf("smtpmessage: NULL mci_out\n");
+ sm_dprintf("smtpmessage: NULL mci_out\n");
}
}
-
-#endif /* SMTP */
OpenPOWER on IntegriCloud