summaryrefslogtreecommitdiff
path: root/usr.sbin/sendmail/src/queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/sendmail/src/queue.c')
-rw-r--r--usr.sbin/sendmail/src/queue.c754
1 files changed, 627 insertions, 127 deletions
diff --git a/usr.sbin/sendmail/src/queue.c b/usr.sbin/sendmail/src/queue.c
index 1dc56a6839ab..97bf36cd8086 100644
--- a/usr.sbin/sendmail/src/queue.c
+++ b/usr.sbin/sendmail/src/queue.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
*
@@ -36,14 +36,13 @@
#ifndef lint
#ifdef QUEUE
-static char sccsid[] = "@(#)queue.c 8.41.1.3 (Berkeley) 3/5/95 (with queueing)";
+static char sccsid[] = "@(#)queue.c 8.98 (Berkeley) 11/11/95 (with queueing)";
#else
-static char sccsid[] = "@(#)queue.c 8.41.1.3 (Berkeley) 3/5/95 (without queueing)";
+static char sccsid[] = "@(#)queue.c 8.98 (Berkeley) 11/11/95 (without queueing)";
#endif
#endif /* not lint */
# include <errno.h>
-# include <pwd.h>
# include <dirent.h>
# ifdef QUEUE
@@ -55,6 +54,9 @@ static char sccsid[] = "@(#)queue.c 8.41.1.3 (Berkeley) 3/5/95 (without queueing
struct work
{
char *w_name; /* name of control file */
+ char *w_host; /* name of recipient host */
+ bool w_lock; /* is message locked? */
+ bool w_tooyoung; /* is it too young to run? */
long w_pri; /* priority of message, see below */
time_t w_ctime; /* creation time of message */
struct work *w_next; /* next in queue */
@@ -63,13 +65,17 @@ struct work
typedef struct work WORK;
WORK *WorkQ; /* queue of things to be done */
+
+#define QF_VERSION 1 /* version number of this queue format */
+
+#if !defined(NGROUPS_MAX) && defined(NGROUPS)
+# define NGROUPS_MAX NGROUPS /* POSIX naming convention */
+#endif
/*
** QUEUEUP -- queue a message up for future transmission.
**
** Parameters:
** e -- the envelope to queue up.
-** queueall -- if TRUE, queue all addresses, rather than
-** just those with the QQUEUEUP flag set.
** announce -- if TRUE, tell when you are queueing up.
**
** Returns:
@@ -80,9 +86,9 @@ WORK *WorkQ; /* queue of things to be done */
** The queue file is left locked.
*/
-queueup(e, queueall, announce)
+void
+queueup(e, announce)
register ENVELOPE *e;
- bool queueall;
bool announce;
{
char *qf;
@@ -96,6 +102,7 @@ queueup(e, queueall, announce)
MAILER nullmailer;
MCI mcibuf;
char buf[MAXLINE], tf[MAXLINE];
+ extern void printctladdr __P((ADDRESS *, FILE *));
/*
** Create control file.
@@ -157,6 +164,18 @@ queueup(e, queueall, announce)
if (tTd(40, 1))
printf("\n>>>>> queueing %s%s >>>>>\n", e->e_id,
newid ? " (new id)" : "");
+ if (tTd(40, 3))
+ {
+ extern void printenvflags();
+
+ printf(" e_flags=");
+ printenvflags(e);
+ }
+ if (tTd(40, 32))
+ {
+ printf(" sendq=");
+ printaddr(e->e_sendqueue, TRUE);
+ }
if (tTd(40, 9))
{
printf(" tfp=");
@@ -172,17 +191,25 @@ queueup(e, queueall, announce)
** If there is no data file yet, create one.
*/
- if (e->e_df == NULL)
+ if (!bitset(EF_HAS_DF, e->e_flags))
{
register FILE *dfp;
- extern putbody();
+ char dfname[20];
+ struct stat stbuf;
- e->e_df = queuename(e, 'd');
- e->e_df = newstr(e->e_df);
- fd = open(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
+ strcpy(dfname, queuename(e, 'd'));
+ fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL)
syserr("!queueup: cannot create data temp file %s, uid=%d",
- e->e_df, geteuid());
+ dfname, geteuid());
+ if (fstat(fd, &stbuf) < 0)
+ e->e_dfino = -1;
+ else
+ {
+ e->e_dfdev = stbuf.st_dev;
+ e->e_dfino = stbuf.st_ino;
+ }
+ e->e_flags |= EF_HAS_DF;
bzero(&mcibuf, sizeof mcibuf);
mcibuf.mci_out = dfp;
mcibuf.mci_mailer = FileMailer;
@@ -197,16 +224,30 @@ queueup(e, queueall, announce)
** they are required by orderq.
*/
- /* output message priority */
- fprintf(tfp, "P%ld\n", e->e_msgpriority);
+ /* output queue version number (must be first!) */
+ fprintf(tfp, "V%d\n", QF_VERSION);
/* output creation time */
fprintf(tfp, "T%ld\n", e->e_ctime);
- /* output type and name of data file */
+ /* output last delivery time */
+ fprintf(tfp, "K%ld\n", e->e_dtime);
+
+ /* output number of delivery attempts */
+ fprintf(tfp, "N%d\n", e->e_ntries);
+
+ /* output message priority */
+ fprintf(tfp, "P%ld\n", e->e_msgpriority);
+
+ /* output inode number of data file */
+ /* XXX should probably include device major/minor too */
+ if (e->e_dfino != -1)
+ fprintf(tfp, "I%d/%d/%ld\n",
+ major(e->e_dfdev), minor(e->e_dfdev), e->e_dfino);
+
+ /* output body type */
if (e->e_bodytype != NULL)
fprintf(tfp, "B%s\n", e->e_bodytype);
- fprintf(tfp, "D%s\n", e->e_df);
/* message from envelope, if it exists */
if (e->e_message != NULL)
@@ -218,6 +259,8 @@ queueup(e, queueall, announce)
*p++ = 'w';
if (bitset(EF_RESPONSE, e->e_flags))
*p++ = 'r';
+ if (bitset(EF_HAS8BIT, e->e_flags))
+ *p++ = '8';
*p++ = '\0';
if (buf[0] != '\0')
fprintf(tfp, "F%s\n", buf);
@@ -231,33 +274,45 @@ queueup(e, queueall, announce)
fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE));
/* output name of sender */
- fprintf(tfp, "S%s\n", denlstring(e->e_from.q_paddr, TRUE, FALSE));
+ if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
+ p = e->e_sender;
+ else
+ p = e->e_from.q_paddr;
+ fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE));
- /* output list of error recipients */
- printctladdr(NULL, NULL);
- for (q = e->e_errorqueue; q != NULL; q = q->q_next)
- {
- if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
- {
- printctladdr(q, tfp);
- fprintf(tfp, "E%s\n", denlstring(q->q_paddr, TRUE, FALSE));
- }
- }
+ /* output ESMTP-supplied "original" information */
+ if (e->e_envid != NULL)
+ fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE));
/* output list of recipient addresses */
+ printctladdr(NULL, NULL);
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
if (bitset(QQUEUEUP, q->q_flags) ||
- (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags)))
+ !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags))
{
printctladdr(q, tfp);
- fprintf(tfp, "R%s\n", denlstring(q->q_paddr, TRUE, FALSE));
+ if (q->q_orcpt != NULL)
+ fprintf(tfp, "Q%s\n",
+ denlstring(q->q_orcpt, TRUE, FALSE));
+ putc('R', tfp);
+ if (bitset(QPRIMARY, q->q_flags))
+ putc('P', tfp);
+ if (bitset(QPINGONSUCCESS, q->q_flags))
+ putc('S', tfp);
+ if (bitset(QPINGONFAILURE, q->q_flags))
+ putc('F', tfp);
+ if (bitset(QPINGONDELAY, q->q_flags))
+ putc('D', tfp);
+ putc(':', tfp);
+ fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE));
if (announce)
{
e->e_to = q->q_paddr;
message("queued");
if (LogLevel > 8)
- logdelivery(NULL, NULL, "queued", NULL, e);
+ logdelivery(q->q_mailer, NULL, "queued",
+ NULL, (time_t) 0, e);
e->e_to = NULL;
}
if (tTd(40, 1))
@@ -302,7 +357,7 @@ queueup(e, queueall, announce)
/* expand macros; if null, don't output header at all */
if (bitset(H_DEFAULT, h->h_flags))
{
- (void) expand(h->h_value, buf, &buf[sizeof buf], e);
+ (void) expand(h->h_value, buf, sizeof buf, e);
if (buf[0] == '\0')
continue;
}
@@ -347,8 +402,13 @@ queueup(e, queueall, announce)
/*
** Clean up.
+ **
+ ** Write a terminator record -- this is to prevent
+ ** scurrilous crackers from appending any data.
*/
+ fprintf(tfp, ".\n");
+
if (fflush(tfp) < 0 || fsync(fileno(tfp)) < 0 || ferror(tfp))
{
if (newid)
@@ -362,8 +422,8 @@ queueup(e, queueall, announce)
/* rename (locked) tf to be (locked) qf */
qf = queuename(e, 'q');
if (rename(tf, qf) < 0)
- syserr("cannot rename(%s, %s), df=%s, uid=%d",
- tf, qf, e->e_df, geteuid());
+ syserr("cannot rename(%s, %s), uid=%d",
+ tf, qf, geteuid());
/* close and unlock old (locked) qf */
if (e->e_lockfp != NULL)
@@ -378,7 +438,7 @@ queueup(e, queueall, announce)
# ifdef LOG
/* save log info */
if (LogLevel > 79)
- syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
+ syslog(LOG_DEBUG, "%s: queueup, qf=%s", e->e_id, qf);
# endif /* LOG */
if (tTd(40, 1))
@@ -386,6 +446,7 @@ queueup(e, queueall, announce)
return;
}
+void
printctladdr(a, tfp)
register ADDRESS *a;
FILE *tfp;
@@ -422,14 +483,13 @@ printctladdr(a, tfp)
lastuid = uid;
lastctladdr = a;
- if (uid == 0 || (pw = getpwuid(uid)) == NULL)
+ if (uid == 0 || (pw = sm_getpwuid(uid)) == NULL)
uname = "";
else
uname = pw->pw_name;
fprintf(tfp, "C%s:%s\n", uname, denlstring(a->q_paddr, TRUE, FALSE));
}
-
/*
** RUNQUEUE -- run the jobs in the queue.
**
@@ -450,10 +510,13 @@ printctladdr(a, tfp)
ENVELOPE QueueEnvelope; /* the queue run envelope */
+void
runqueue(forkflag)
bool forkflag;
{
register ENVELOPE *e;
+ int njobs;
+ int sequenceno = 0;
extern ENVELOPE BlankEnvelope;
/*
@@ -465,8 +528,14 @@ runqueue(forkflag)
if (shouldqueue(0L, curtime()))
{
+ char *msg = "Skipping queue run -- load average too high";
+
if (Verbose)
- printf("Skipping queue run -- load average too high\n");
+ printf("%s\n", msg);
+#ifdef LOG
+ if (LogLevel > 8)
+ syslog(LOG_INFO, "runqueue: %s", msg);
+#endif
if (forkflag && QueueIntvl != 0)
(void) setevent(QueueIntvl, runqueue, TRUE);
return;
@@ -479,6 +548,7 @@ runqueue(forkflag)
if (forkflag)
{
int pid;
+ extern void intsig();
#ifdef SIGCHLD
extern void reapchild();
@@ -491,18 +561,21 @@ runqueue(forkflag)
/* parent -- pick up intermediate zombie */
#ifndef SIGCHLD
(void) waitfor(pid);
+#else
+ CurChildren++;
#endif /* SIGCHLD */
if (QueueIntvl != 0)
(void) setevent(QueueIntvl, runqueue, TRUE);
return;
}
- /* child -- double fork */
+ /* child -- double fork and clean up signals */
#ifndef SIGCHLD
if (fork() != 0)
exit(EX_OK);
#else /* SIGCHLD */
(void) setsignal(SIGCHLD, SIG_DFL);
#endif /* SIGCHLD */
+ (void) setsignal(SIGHUP, intsig);
}
setproctitle("running queue: %s", QueueDir);
@@ -546,7 +619,7 @@ runqueue(forkflag)
*/
/* order the existing work requests */
- (void) orderq(FALSE);
+ njobs = orderq(FALSE);
/* process them once at a time */
while (WorkQ != NULL)
@@ -559,22 +632,29 @@ runqueue(forkflag)
** Ignore jobs that are too expensive for the moment.
*/
+ sequenceno++;
if (shouldqueue(w->w_pri, w->w_ctime))
{
if (Verbose)
- printf("\nSkipping %s\n", w->w_name + 2);
+ printf("\nSkipping %s (sequence %d of %d)\n",
+ w->w_name + 2, sequenceno, njobs);
}
else
{
pid_t pid;
extern pid_t dowork();
+ if (Verbose)
+ printf("\nRunning %s (sequence %d of %d)\n",
+ w->w_name + 2, sequenceno, njobs);
pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e);
errno = 0;
if (pid != 0)
(void) waitfor(pid);
}
free(w->w_name);
+ if (w->w_host)
+ free(w->w_host);
free((char *) w);
}
@@ -604,6 +684,10 @@ runqueue(forkflag)
# define NEED_R 004
# define NEED_S 010
+static WORK *WorkList = NULL;
+static int WorkListSize = 0;
+
+int
orderq(doall)
bool doall;
{
@@ -611,9 +695,8 @@ orderq(doall)
register WORK *w;
DIR *f;
register int i;
- WORK wlist[QUEUESIZE+1];
int wn = -1;
- extern workcmpf();
+ int wc;
if (tTd(41, 1))
{
@@ -633,6 +716,8 @@ orderq(doall)
WorkQ = nw;
free(w->w_name);
+ if (w->w_host)
+ free(w->w_host);
free((char *) w);
w = nw;
}
@@ -653,9 +738,12 @@ orderq(doall)
{
FILE *cf;
register char *p;
- char lbuf[MAXNAME];
+ char lbuf[MAXNAME + 1];
extern bool strcontainedin();
+ if (tTd(41, 50))
+ printf("orderq: checking %s\n", d->d_name);
+
/* is this an interesting entry? */
if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
continue;
@@ -664,6 +752,7 @@ orderq(doall)
!strcontainedin(QueueLimitId, d->d_name))
continue;
+#ifdef PICKY_QF_NAME_CHECK
/*
** Check queue name for plausibility. This handles
** both old and new type ids.
@@ -682,22 +771,38 @@ orderq(doall)
{
if (Verbose)
printf("orderq: bogus qf name %s\n", d->d_name);
-#ifdef LOG
- if (LogLevel > 3)
- syslog(LOG_CRIT, "orderq: bogus qf name %s",
+# ifdef LOG
+ if (LogLevel > 0)
+ syslog(LOG_ALERT, "orderq: bogus qf name %s",
d->d_name);
-#endif
- if (strlen(d->d_name) >= MAXNAME)
- d->d_name[MAXNAME - 1] = '\0';
+# endif
+ if (strlen(d->d_name) > (SIZE_T) MAXNAME)
+ d->d_name[MAXNAME] = '\0';
strcpy(lbuf, d->d_name);
lbuf[0] = 'Q';
(void) rename(d->d_name, lbuf);
continue;
}
+#endif
- /* yes -- open control file (if not too many files) */
- if (++wn >= QUEUESIZE)
+ /* open control file (if not too many files) */
+ if (++wn >= MaxQueueRun && MaxQueueRun > 0)
+ {
+# ifdef LOG
+ if (wn == MaxQueueRun && LogLevel > 0)
+ syslog(LOG_ALERT, "WorkList for %s maxed out at %d",
+ QueueDir, MaxQueueRun);
+# endif
continue;
+ }
+ if (wn >= WorkListSize)
+ {
+ extern void grow_wlist __P((void));
+
+ grow_wlist();
+ if (wn >= WorkListSize)
+ continue;
+ }
cf = fopen(d->d_name, "r");
if (cf == NULL)
@@ -705,14 +810,17 @@ orderq(doall)
/* this may be some random person sending hir msgs */
/* syserr("orderq: cannot open %s", cbuf); */
if (tTd(41, 2))
- printf("orderq: cannot open %s (%d)\n",
- d->d_name, errno);
+ printf("orderq: cannot open %s: %s\n",
+ d->d_name, errstring(errno));
errno = 0;
wn--;
continue;
}
- w = &wlist[wn];
+ w = &WorkList[wn];
w->w_name = newstr(d->d_name);
+ w->w_host = NULL;
+ w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB);
+ w->w_tooyoung = FALSE;
/* make sure jobs in creation don't clog queue */
w->w_pri = 0x7fffffff;
@@ -722,11 +830,10 @@ orderq(doall)
i = NEED_P | NEED_T;
if (QueueLimitSender != NULL)
i |= NEED_S;
- if (QueueLimitRecipient != NULL)
+ if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL)
i |= NEED_R;
while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
{
- extern long atol();
extern bool strcontainedin();
switch (lbuf[0])
@@ -742,7 +849,10 @@ orderq(doall)
break;
case 'R':
- if (QueueLimitRecipient != NULL &&
+ if (w->w_host == NULL &&
+ (p = strrchr(&lbuf[1], '@')) != NULL)
+ w->w_host = newstr(&p[1]);
+ if (QueueLimitRecipient == NULL ||
strcontainedin(QueueLimitRecipient, &lbuf[1]))
i &= ~NEED_R;
break;
@@ -752,6 +862,16 @@ orderq(doall)
strcontainedin(QueueLimitSender, &lbuf[1]))
i &= ~NEED_S;
break;
+
+ case 'K':
+ if ((curtime() - (time_t) atol(&lbuf[1])) < MinQueueAge)
+ w->w_tooyoung = TRUE;
+ break;
+
+ case 'N':
+ if (atol(&lbuf[1]) == 0)
+ w->w_tooyoung = FALSE;
+ break;
}
}
(void) fclose(cf);
@@ -760,17 +880,78 @@ orderq(doall)
bitset(NEED_R|NEED_S, i))
{
/* don't even bother sorting this job in */
+ if (tTd(41, 49))
+ printf("skipping %s (%x)\n", w->w_name, i);
+ free(w->w_name);
+ if (w->w_host)
+ free(w->w_host);
wn--;
}
}
(void) closedir(f);
wn++;
- /*
- ** Sort the work directory.
- */
+ wc = min(wn, WorkListSize);
+ if (wc > MaxQueueRun && MaxQueueRun > 0)
+ wc = MaxQueueRun;
+
+ if (QueueSortOrder == QS_BYHOST)
+ {
+ extern workcmpf1();
+ extern workcmpf2();
+
+ /*
+ ** Sort the work directory for the first time,
+ ** based on host name, lock status, and priority.
+ */
+
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1);
+
+ /*
+ ** If one message to host is locked, "lock" all messages
+ ** to that host.
+ */
+
+ i = 0;
+ while (i < wc)
+ {
+ if (!WorkList[i].w_lock)
+ {
+ i++;
+ continue;
+ }
+ w = &WorkList[i];
+ while (++i < wc)
+ {
+ if (WorkList[i].w_host == NULL &&
+ w->w_host == NULL)
+ WorkList[i].w_lock = TRUE;
+ else if (WorkList[i].w_host != NULL &&
+ w->w_host != NULL &&
+ strcmp(WorkList[i].w_host, w->w_host) == 0)
+ WorkList[i].w_lock = TRUE;
+ else
+ break;
+ }
+ }
+
+ /*
+ ** Sort the work directory for the second time,
+ ** based on lock status, host name, and priority.
+ */
+
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2);
+ }
+ else
+ {
+ extern workcmpf0();
+
+ /*
+ ** Simple sort based on queue priority only.
+ */
- qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf);
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0);
+ }
/*
** Convert the work list into canonical form.
@@ -778,15 +959,21 @@ orderq(doall)
*/
WorkQ = NULL;
- for (i = min(wn, QUEUESIZE); --i >= 0; )
+ for (i = wc; --i >= 0; )
{
w = (WORK *) xalloc(sizeof *w);
- w->w_name = wlist[i].w_name;
- w->w_pri = wlist[i].w_pri;
- w->w_ctime = wlist[i].w_ctime;
+ w->w_name = WorkList[i].w_name;
+ w->w_host = WorkList[i].w_host;
+ w->w_lock = WorkList[i].w_lock;
+ w->w_tooyoung = WorkList[i].w_tooyoung;
+ w->w_pri = WorkList[i].w_pri;
+ w->w_ctime = WorkList[i].w_ctime;
w->w_next = WorkQ;
WorkQ = w;
}
+ if (WorkList != NULL)
+ free(WorkList);
+ WorkList = NULL;
if (tTd(40, 1))
{
@@ -797,7 +984,59 @@ orderq(doall)
return (wn);
}
/*
-** WORKCMPF -- compare function for ordering work.
+** GROW_WLIST -- make the work list larger
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Adds another QUEUESEGSIZE entries to WorkList if possible.
+** It can fail if there isn't enough memory, so WorkListSize
+** should be checked again upon return.
+*/
+
+void
+grow_wlist()
+{
+ if (tTd(41, 1))
+ printf("grow_wlist: WorkListSize=%d\n", WorkListSize);
+ if (WorkList == NULL)
+ {
+ WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1));
+ WorkListSize = QUEUESEGSIZE;
+ }
+ else
+ {
+ int newsize = WorkListSize + QUEUESEGSIZE;
+ WORK *newlist = (WORK *) realloc((char *)WorkList,
+ (unsigned)sizeof(WORK) * (newsize + 1));
+
+ if (newlist != NULL)
+ {
+ WorkListSize = newsize;
+ WorkList = newlist;
+# ifdef LOG
+ if (LogLevel > 1)
+ {
+ syslog(LOG_NOTICE, "grew WorkList for %s to %d",
+ QueueDir, WorkListSize);
+ }
+ }
+ else if (LogLevel > 0)
+ {
+ syslog(LOG_ALERT, "FAILED to grow WorkList for %s to %d",
+ QueueDir, newsize);
+# endif
+ }
+ }
+ if (tTd(41, 1))
+ printf("grow_wlist: WorkListSize now %d\n", WorkListSize);
+}
+ /*
+** WORKCMPF0 -- simple priority-only compare function.
**
** Parameters:
** a -- the first argument.
@@ -812,7 +1051,8 @@ orderq(doall)
** none.
*/
-workcmpf(a, b)
+int
+workcmpf0(a, b)
register WORK *a;
register WORK *b;
{
@@ -820,11 +1060,93 @@ workcmpf(a, b)
long pb = b->w_pri;
if (pa == pb)
- return (0);
+ return 0;
else if (pa > pb)
- return (1);
+ return 1;
else
- return (-1);
+ return -1;
+}
+ /*
+** WORKCMPF1 -- first compare function for ordering work based on host name.
+**
+** Sorts on host name, lock status, and priority in that order.
+**
+** Parameters:
+** a -- the first argument.
+** b -- the second argument.
+**
+** Returns:
+** <0 if a < b
+** 0 if a == b
+** >0 if a > b
+**
+** Side Effects:
+** none.
+*/
+
+int
+workcmpf1(a, b)
+ register WORK *a;
+ register WORK *b;
+{
+ int i;
+
+ /* host name */
+ if (a->w_host != NULL && b->w_host == NULL)
+ return 1;
+ else if (a->w_host == NULL && b->w_host != NULL)
+ return -1;
+ if (a->w_host != NULL && b->w_host != NULL &&
+ (i = strcmp(a->w_host, b->w_host)))
+ return i;
+
+ /* lock status */
+ if (a->w_lock != b->w_lock)
+ return b->w_lock - a->w_lock;
+
+ /* job priority */
+ return a->w_pri - b->w_pri;
+}
+ /*
+** WORKCMPF2 -- second compare function for ordering work based on host name.
+**
+** Sorts on lock status, host name, and priority in that order.
+**
+** Parameters:
+** a -- the first argument.
+** b -- the second argument.
+**
+** Returns:
+** <0 if a < b
+** 0 if a == b
+** >0 if a > b
+**
+** Side Effects:
+** none.
+*/
+
+int
+workcmpf2(a, b)
+ register WORK *a;
+ register WORK *b;
+{
+ int i;
+
+ /* lock status */
+ if (a->w_lock != b->w_lock)
+ return a->w_lock - b->w_lock;
+
+ /* host name */
+ if (a->w_host != NULL && b->w_host == NULL)
+ return 1;
+ else if (a->w_host == NULL && b->w_host != NULL)
+ return -1;
+ if (a->w_host != NULL && b->w_host != NULL &&
+ (i = strcmp(a->w_host, b->w_host)))
+ return i;
+
+ /* job priority */
+ return a->w_pri - b->w_pri;
}
/*
** DOWORK -- do a work request.
@@ -895,6 +1217,7 @@ dowork(id, forkflag, requeueflag, e)
(void) alarm(0);
clearenvelope(e, FALSE);
e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
+ e->e_sendmode = SM_DELIVER;
e->e_errormode = EM_MAIL;
e->e_id = id;
GrabTo = UseErrorsTo = FALSE;
@@ -928,7 +1251,7 @@ dowork(id, forkflag, requeueflag, e)
eatheader(e, requeueflag);
if (requeueflag)
- queueup(e, TRUE, FALSE);
+ queueup(e, FALSE);
/* do the delivery */
sendall(e, SM_DELIVER);
@@ -964,10 +1287,14 @@ readqf(e)
ADDRESS *ctladdr;
struct stat st;
char *bp;
+ int qfver = 0;
+ register char *p;
+ char *orcpt = NULL;
+ bool nomore = FALSE;
char qf[20];
char buf[MAXLINE];
- extern long atol();
extern ADDRESS *setctluser();
+ extern void loseqfile();
/*
** Read and process the file.
@@ -988,9 +1315,7 @@ readqf(e)
if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
{
/* being processed by another queuer */
- if (tTd(40, 8))
- printf("readqf(%s): locked\n", qf);
- if (Verbose)
+ if (Verbose || tTd(40, 8))
printf("%s: locked\n", e->e_id);
# ifdef LOG
if (LogLevel > 19)
@@ -1014,7 +1339,7 @@ readqf(e)
return FALSE;
}
- if (st.st_uid != geteuid())
+ if (st.st_uid != geteuid() || bitset(S_IWOTH|S_IWGRP, st.st_mode))
{
# ifdef LOG
if (LogLevel > 0)
@@ -1025,7 +1350,7 @@ readqf(e)
# endif /* LOG */
if (tTd(40, 8))
printf("readqf(%s): bogus file\n", qf);
- rename(qf, queuename(e, 'Q'));
+ loseqfile(e, "bogus file uid in mqueue");
fclose(qfp);
return FALSE;
}
@@ -1059,32 +1384,91 @@ readqf(e)
LineNumber = 0;
e->e_flags |= EF_GLOBALERRS;
OpMode = MD_DELIVER;
- if (Verbose)
- printf("\nRunning %s\n", e->e_id);
ctladdr = NULL;
+ e->e_dfino = -1;
+ e->e_msgsize = -1;
while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
{
register char *p;
- struct stat st;
+ u_long qflags;
+ ADDRESS *q;
if (tTd(40, 4))
printf("+++++ %s\n", bp);
+ if (nomore)
+ {
+ /* hack attack */
+ syserr("SECURITY ALERT: extra data in qf: %s", bp);
+ fclose(qfp);
+ loseqfile(e, "bogus queue line");
+ return FALSE;
+ }
switch (bp[0])
{
+ case 'V': /* queue file version number */
+ qfver = atoi(&bp[1]);
+ if (qfver > QF_VERSION)
+ {
+ syserr("Version number in qf (%d) greater than max (%d)",
+ qfver, QF_VERSION);
+ }
+ break;
+
case 'C': /* specify controlling user */
ctladdr = setctluser(&bp[1]);
break;
+ case 'Q': /* original recipient */
+ orcpt = newstr(&bp[1]);
+ break;
+
case 'R': /* specify recipient */
- (void) sendtolist(&bp[1], ctladdr, &e->e_sendqueue, e);
+ p = bp;
+ qflags = 0;
+ if (qfver >= 1)
+ {
+ /* get flag bits */
+ while (*++p != '\0' && *p != ':')
+ {
+ switch (*p)
+ {
+ case 'S':
+ qflags |= QPINGONSUCCESS;
+ break;
+
+ case 'F':
+ qflags |= QPINGONFAILURE;
+ break;
+
+ case 'D':
+ qflags |= QPINGONDELAY;
+ break;
+
+ case 'P':
+ qflags |= QPRIMARY;
+ break;
+ }
+ }
+ }
+ else
+ qflags |= QPRIMARY;
+ q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e);
+ if (q != NULL)
+ {
+ q->q_alias = ctladdr;
+ q->q_flags |= qflags;
+ q->q_orcpt = orcpt;
+ (void) recipient(q, &e->e_sendqueue, 0, e);
+ }
+ orcpt = NULL;
break;
case 'E': /* specify error recipient */
- (void) sendtolist(&bp[1], ctladdr, &e->e_errorqueue, e);
+ /* no longer used */
break;
case 'H': /* header */
- (void) chompheader(&bp[1], FALSE, e);
+ (void) chompheader(&bp[1], FALSE, NULL, e);
break;
case 'M': /* message */
@@ -1100,26 +1484,39 @@ readqf(e)
break;
case 'D': /* data file name */
- e->e_df = newstr(&bp[1]);
- e->e_dfp = fopen(e->e_df, "r");
- if (e->e_dfp == NULL)
- {
- syserr("readqf: cannot open %s", e->e_df);
- e->e_msgsize = -1;
- }
- else if (fstat(fileno(e->e_dfp), &st) >= 0)
- e->e_msgsize = st.st_size;
+ /* obsolete -- ignore */
break;
case 'T': /* init time */
e->e_ctime = atol(&bp[1]);
break;
+ case 'I': /* data file's inode number */
+ if (e->e_dfino == -1)
+ e->e_dfino = atol(&buf[1]);
+ break;
+
+ case 'K': /* time of last deliver attempt */
+ e->e_dtime = atol(&buf[1]);
+ break;
+
+ case 'N': /* number of delivery attempts */
+ e->e_ntries = atoi(&buf[1]);
+ break;
+
case 'P': /* message priority */
e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
break;
case 'F': /* flag bits */
+ if (strncmp(bp, "From ", 5) == 0)
+ {
+ /* we are being spoofed! */
+ syserr("SECURITY ALERT: bogus qf line %s", bp);
+ fclose(qfp);
+ loseqfile(e, "bogus queue line");
+ return FALSE;
+ }
for (p = &bp[1]; *p != '\0'; p++)
{
switch (*p)
@@ -1131,22 +1528,31 @@ readqf(e)
case 'r': /* response */
e->e_flags |= EF_RESPONSE;
break;
+
+ case '8': /* has 8 bit data */
+ e->e_flags |= EF_HAS8BIT;
+ break;
}
}
break;
+ case 'Z': /* original envelope id from ESMTP */
+ e->e_envid = newstr(&bp[1]);
+ break;
+
case '$': /* define macro */
define(bp[1], newstr(&bp[2]), e);
break;
- case '\0': /* blank line; ignore */
+ case '.': /* terminate file */
+ nomore = TRUE;
break;
default:
syserr("readqf: %s: line %d: bad line \"%s\"",
qf, LineNumber, bp);
fclose(qfp);
- rename(qf, queuename(e, 'Q'));
+ loseqfile(e, "unrecognized line");
return FALSE;
}
@@ -1163,7 +1569,49 @@ readqf(e)
{
errno = 0;
e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
+ return TRUE;
+ }
+
+ /* if this has been tried recently, let it be */
+ if (e->e_ntries > 0 && (curtime() - e->e_dtime) < MinQueueAge)
+ {
+ char *howlong = pintvl(curtime() - e->e_dtime, TRUE);
+ extern void unlockqueue();
+
+ if (Verbose || tTd(40, 8))
+ printf("%s: too young (%s)\n",
+ e->e_id, howlong);
+#ifdef LOG
+ if (LogLevel > 19)
+ syslog(LOG_DEBUG, "%s: too young (%s)",
+ e->e_id, howlong);
+#endif
+ e->e_id = NULL;
+ unlockqueue(e);
+ return FALSE;
+ }
+
+ /*
+ ** Arrange to read the data file.
+ */
+
+ p = queuename(e, 'd');
+ e->e_dfp = fopen(p, "r");
+ if (e->e_dfp == NULL)
+ {
+ syserr("readqf: cannot open %s", p);
+ }
+ else
+ {
+ e->e_flags |= EF_HAS_DF;
+ if (fstat(fileno(e->e_dfp), &st) >= 0)
+ {
+ e->e_msgsize = st.st_size;
+ e->e_dfdev = st.st_dev;
+ e->e_dfino = st.st_ino;
+ }
}
+
return TRUE;
}
/*
@@ -1179,6 +1627,7 @@ readqf(e)
** Prints a listing of the mail queue on the standard output.
*/
+void
printqueue()
{
register WORK *w;
@@ -1193,9 +1642,9 @@ printqueue()
if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
{
struct stat st;
-# ifdef NGROUPS
+# ifdef NGROUPS_MAX
int n;
- GIDSET_T gidset[NGROUPS];
+ GIDSET_T gidset[NGROUPS_MAX];
# endif
if (stat(QueueDir, &st) < 0)
@@ -1203,14 +1652,14 @@ printqueue()
syserr("Cannot stat %s", QueueDir);
return;
}
-# ifdef NGROUPS
- n = getgroups(NGROUPS, gidset);
+# ifdef NGROUPS_MAX
+ n = getgroups(NGROUPS_MAX, gidset);
while (--n >= 0)
{
if (gidset[n] == st.st_gid)
break;
}
- if (n < 0)
+ if (n < 0 && RealGid != st.st_gid)
# else
if (RealGid != st.st_gid)
# endif
@@ -1241,8 +1690,8 @@ printqueue()
CurrentLA = getla(); /* get load average */
printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
- if (nrequests > QUEUESIZE)
- printf(", only %d printed", QUEUESIZE);
+ if (nrequests > WorkListSize)
+ printf(", only %d printed", WorkListSize);
if (Verbose)
printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
else
@@ -1251,10 +1700,11 @@ printqueue()
{
struct stat st;
auto time_t submittime = 0;
- long dfsize = -1;
+ long dfsize;
int flags = 0;
- char message[MAXLINE];
- char bodytype[MAXNAME];
+ int qfver;
+ char statmsg[MAXLINE];
+ char bodytype[MAXNAME + 1];
printf("%8s", w->w_name + 2);
f = fopen(w->w_name, "r");
@@ -1264,15 +1714,23 @@ printqueue()
errno = 0;
continue;
}
- if (!lockfile(fileno(f), w->w_name, NULL, LOCK_SH|LOCK_NB))
+ w->w_name[0] = 'd';
+ if (stat(w->w_name, &st) >= 0)
+ dfsize = st.st_size;
+ else
+ dfsize = -1;
+ if (w->w_lock)
printf("*");
+ else if (w->w_tooyoung)
+ printf("-");
else if (shouldqueue(w->w_pri, w->w_ctime))
printf("X");
else
printf(" ");
errno = 0;
- message[0] = bodytype[0] = '\0';
+ statmsg[0] = bodytype[0] = '\0';
+ qfver = 0;
while (fgets(buf, sizeof buf, f) != NULL)
{
register int i;
@@ -1281,11 +1739,15 @@ printqueue()
fixcrlf(buf, TRUE);
switch (buf[0])
{
+ case 'V': /* queue file version */
+ qfver = atoi(&buf[1]);
+ break;
+
case 'M': /* error message */
- if ((i = strlen(&buf[1])) >= sizeof message)
- i = sizeof message - 1;
- bcopy(&buf[1], message, i);
- message[i] = '\0';
+ if ((i = strlen(&buf[1])) >= sizeof statmsg)
+ i = sizeof statmsg - 1;
+ bcopy(&buf[1], statmsg, i);
+ statmsg[i] = '\0';
break;
case 'B': /* body type */
@@ -1306,11 +1768,11 @@ printqueue()
else
printf("%8ld %.16s %.45s", dfsize,
ctime(&submittime), &buf[1]);
- if (message[0] != '\0' || bodytype[0] != '\0')
+ if (statmsg[0] != '\0' || bodytype[0] != '\0')
{
printf("\n %10.10s", bodytype);
- if (message[0] != '\0')
- printf(" (%.60s)", message);
+ if (statmsg[0] != '\0')
+ printf(" (%.60s)", statmsg);
}
break;
@@ -1321,21 +1783,24 @@ printqueue()
break;
case 'R': /* recipient name */
+ p = &buf[1];
+ if (qfver >= 1)
+ {
+ p = strchr(p, ':');
+ if (p == NULL)
+ break;
+ p++;
+ }
if (Verbose)
- printf("\n\t\t\t\t\t %.38s", &buf[1]);
+ printf("\n\t\t\t\t\t %.38s", p);
else
- printf("\n\t\t\t\t %.45s", &buf[1]);
+ printf("\n\t\t\t\t %.45s", p);
break;
case 'T': /* creation time */
submittime = atol(&buf[1]);
break;
- case 'D': /* data file name */
- if (stat(&buf[1], &st) >= 0)
- dfsize = st.st_size;
- break;
-
case 'F': /* flag bits */
for (p = &buf[1]; *p != '\0'; p++)
{
@@ -1388,7 +1853,7 @@ queuename(e, type)
static char c2;
time_t now;
struct tm *tm;
- static char buf[MAXNAME];
+ static char buf[MAXNAME + 1];
if (e->e_id == NULL)
{
@@ -1480,6 +1945,7 @@ queuename(e, type)
** unlocks the queue for `e'.
*/
+void
unlockqueue(e)
ENVELOPE *e;
{
@@ -1545,7 +2011,7 @@ setctluser(user)
p = strchr(user, ':');
if (p != NULL)
*p++ = '\0';
- if (*user != '\0' && (pw = getpwnam(user)) != NULL)
+ if (*user != '\0' && (pw = sm_getpwnam(user)) != NULL)
{
if (strcmp(pw->pw_dir, "/") == 0)
a->q_home = "";
@@ -1553,13 +2019,15 @@ setctluser(user)
a->q_home = newstr(pw->pw_dir);
a->q_uid = pw->pw_uid;
a->q_gid = pw->pw_gid;
- a->q_user = newstr(user);
a->q_flags |= QGOODUID;
}
+
+ if (*user != '\0')
+ a->q_user = newstr(user);
+ else if (p != NULL)
+ a->q_user = newstr(p);
else
- {
a->q_user = newstr(DefUser);
- }
a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */
a->q_mailer = LocalMailer;
@@ -1569,3 +2037,35 @@ setctluser(user)
a->q_paddr = newstr(p);
return a;
}
+ /*
+** LOSEQFILE -- save the qf as Qf and try to let someone know
+**
+** Parameters:
+** e -- the envelope (e->e_id will be used).
+** why -- reported to whomever can hear.
+**
+** Returns:
+** none.
+*/
+
+void
+loseqfile(e, why)
+ register ENVELOPE *e;
+ char *why;
+{
+ char *p;
+ char buf[40];
+
+ if (e == NULL || e->e_id == NULL)
+ return;
+ if (strlen(e->e_id) > sizeof buf - 4)
+ return;
+ strcpy(buf, queuename(e, 'q'));
+ p = queuename(e, 'Q');
+ if (rename(buf, p) < 0)
+ syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid());
+#ifdef LOG
+ else if (LogLevel > 0)
+ syslog(LOG_ALERT, "Losing %s: %s", buf, why);
+#endif
+}