baseline commit of sendmail release 8 version 8.1B

(this is the sendmail stuff as it will appear in 4.4BSD)
This commit is contained in:
glass 1993-06-18 20:41:58 +00:00
parent b580eb544e
commit 100f4f365f
7 changed files with 4763 additions and 0 deletions

View File

@ -0,0 +1,10 @@
Everything in this directory (except this file) has been contributed.
We will not fix bugs in these programs. Contact the original author
for assistance.
Some of these are patches to sendmail itself. You may need to take
care -- some of the patches may be out of date with the latest release
of sendmail. Also, the previous comment applies -- patches belong to
the original author, not to me.
Eric Allman, 26 May 1993

View File

@ -0,0 +1,409 @@
/*
* By John G. Myers, jgm+@cmu.edu
* Version 1.1
*
* Process a BITNET "internet.listing" file, producing output
* suitable for input to makemap.
*
* The input file can be obtained via anonymous FTP to bitnic.educom.edu.
* Change directory to "netinfo" and get the file internet.listing
* The file is updated monthly.
*
* Feed the output of this program to "makemap hash /etc/bitdomain.db"
* to create the table used by the "FEATURE(bitdomain)" config file macro.
* If your sendmail does not have the db library compiled in, you can instead
* use "makemap dbm /etc/bitdomain" and
* "FEATURE(bitdomain,`dbm -o /etc/bitdomain')"
*
* The bitdomain table should be rebuilt monthly.
*/
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <netdb.h>
#include <ctype.h>
#include <string.h>
/* don't use sizeof because sizeof(long) is different on 64-bit machines */
#define SHORTSIZE 2 /* size of a short (really, must be 2) */
#define LONGSIZE 4 /* size of a long (really, must be 4) */
typedef union
{
HEADER qb1;
char qb2[PACKETSZ];
} querybuf;
extern int h_errno;
extern char *malloc();
extern char *optarg;
extern int optind;
char *lookup();
main(argc, argv)
int argc;
char **argv;
{
int opt;
while ((opt = getopt(argc, argv, "o:")) != EOF) {
switch (opt) {
case 'o':
if (!freopen(optarg, "w", stdout)) {
perror(optarg);
exit(1);
}
break;
default:
fprintf(stderr, "usage: %s [-o outfile] [internet.listing]\n",
argv[0]);
exit(1);
}
}
if (optind < argc) {
if (!freopen(argv[optind], "r", stdin)) {
perror(argv[optind]);
exit(1);
}
}
readfile(stdin);
finish();
exit(0);
}
/*
* Parse and process an input file
*/
readfile(infile)
FILE *infile;
{
int skippingheader = 1;
char buf[1024], *node, *hostname, *p;
while (fgets(buf, sizeof(buf), infile)) {
for (p = buf; *p && isspace(*p); p++);
if (!*p) {
skippingheader = 0;
continue;
}
if (skippingheader) continue;
node = p;
for (; *p && !isspace(*p); p++) {
if (isupper(*p)) *p = tolower(*p);
}
if (!*p) {
fprintf(stderr, "%-8s: no domain name in input file\n", node);
continue;
}
*p++ = '\0';
for (; *p && isspace(*p); p++) ;
if (!*p) {
fprintf(stderr, "%-8s no domain name in input file\n", node);
continue;
}
hostname = p;
for (; *p && !isspace(*p); p++) {
if (isupper(*p)) *p = tolower(*p);
}
*p = '\0';
/* Chop off any trailing .bitnet */
if (strlen(hostname) > 7 &&
!strcmp(hostname+strlen(hostname)-7, ".bitnet")) {
hostname[strlen(hostname)-7] = '\0';
}
entry(node, hostname, sizeof(buf)-(hostname - buf));
}
}
/*
* Process a single entry in the input file.
* The entry tells us that "node" expands to "domain".
* "domain" can either be a domain name or a bitnet node name
* The buffer pointed to by "domain" may be overwritten--it
* is of size "domainlen".
*/
entry(node, domain, domainlen)
char *node;
char *domain;
char *domainlen;
{
char *otherdomain, *p, *err;
/* See if we have any remembered information about this node */
otherdomain = lookup(node);
if (otherdomain && strchr(otherdomain, '.')) {
/* We already have a domain for this node */
if (!strchr(domain, '.')) {
/*
* This entry is an Eric Thomas FOO.BITNET kludge.
* He doesn't want LISTSERV to do transitive closures, so we
* do them instead. Give the the domain expansion for "node"
* (which is in "otherdomian") to FOO (which is in "domain")
* if "domain" doesn't have a domain expansion already.
*/
p = lookup(domain);
if (!p || !index(p, '.')) remember(domain, otherdomain);
}
}
else {
if (!strchr(domain, '.') || valhost(domain, domainlen)) {
remember(node, domain);
if (otherdomain) {
/*
* We previously mapped the node "node" to the node
* "otherdomain". If "otherdomain" doesn't already
* have a domain expansion, give it the expansion "domain".
*/
p = lookup(otherdomain);
if (!p || !index(p, '.')) remember(otherdomain, domain);
}
}
else {
switch (h_errno) {
case HOST_NOT_FOUND:
err = "not registered in DNS";
break;
case TRY_AGAIN:
err = "temporary DNS lookup failure";
break;
case NO_RECOVERY:
err = "non-recoverable nameserver error";
break;
case NO_DATA:
err = "registered in DNS, but not mailable";
break;
default:
err = "unknown nameserver error";
break;
}
fprintf(stderr, "%-8s %s %s\n", node, domain, err);
}
}
}
/*
* Validate whether the mail domain "host" is registered in the DNS.
* If "host" is a CNAME, it is expanded in-place if the expansion fits
* into the buffer of size "hbsize". Returns nonzero if it is, zero
* if it is not. A BIND error code is left in h_errno.
*/
int
valhost(host, hbsize)
char *host;
int hbsize;
{
register u_char *eom, *ap;
register int n;
HEADER *hp;
querybuf answer;
int ancount, qdcount;
int ret;
int type;
int qtype;
char nbuf[1024];
if ((_res.options & RES_INIT) == 0 && res_init() == -1)
return (0);
_res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
_res.retrans = 30;
_res.retry = 10;
qtype = T_ANY;
for (;;) {
h_errno = NO_DATA;
ret = res_querydomain(host, "", C_IN, qtype,
&answer, sizeof(answer));
if (ret <= 0)
{
if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
{
/* the name server seems to be down */
h_errno = TRY_AGAIN;
return 0;
}
if (h_errno != HOST_NOT_FOUND)
{
/* might have another type of interest */
if (qtype == T_ANY)
{
qtype = T_A;
continue;
}
else if (qtype == T_A)
{
qtype = T_MX;
continue;
}
}
/* otherwise, no record */
return 0;
}
/*
** This might be a bogus match. Search for A, MX, or
** CNAME records.
*/
hp = (HEADER *) &answer;
ap = (u_char *) &answer + sizeof(HEADER);
eom = (u_char *) &answer + ret;
/* skip question part of response -- we know what we asked */
for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ)
{
if ((ret = dn_skipname(ap, eom)) < 0)
{
return 0; /* ???XXX??? */
}
}
for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n)
{
n = dn_expand((u_char *) &answer, eom, ap,
(u_char *) nbuf, sizeof nbuf);
if (n < 0)
break;
ap += n;
GETSHORT(type, ap);
ap += SHORTSIZE + LONGSIZE;
GETSHORT(n, ap);
switch (type)
{
case T_MX:
case T_A:
return 1;
case T_CNAME:
/* value points at name */
if ((ret = dn_expand((u_char *)&answer,
eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0)
break;
if (strlen(nbuf) < hbsize) {
(void)strcpy(host, nbuf);
}
return 1;
default:
/* not a record of interest */
continue;
}
}
/*
** If this was a T_ANY query, we may have the info but
** need an explicit query. Try T_A, then T_MX.
*/
if (qtype == T_ANY)
qtype = T_A;
else if (qtype == T_A)
qtype = T_MX;
else
return 0;
}
}
struct entry {
struct entry *next;
char *node;
char *domain;
};
struct entry *firstentry;
/*
* Find any remembered information about "node"
*/
char *lookup(node)
char *node;
{
struct entry *p;
for (p = firstentry; p; p = p->next) {
if (!strcmp(node, p->node)) {
return p->domain;
}
}
return 0;
}
/*
* Mark the node "node" as equivalent to "domain". "domain" can either
* be a bitnet node or a domain name--if it is the latter, the mapping
* will be written to stdout.
*/
remember(node, domain)
char *node;
char *domain;
{
struct entry *p;
if (strchr(domain, '.')) {
fprintf(stdout, "%-8s %s\n", node, domain);
}
for (p = firstentry; p; p = p->next) {
if (!strcmp(node, p->node)) {
p->domain = malloc(strlen(domain)+1);
if (!p->domain) {
goto outofmemory;
}
strcpy(p->domain, domain);
return;
}
}
p = (struct entry *)malloc(sizeof(struct entry));
if (!p) goto outofmemory;
p->next = firstentry;
firstentry = p;
p->node = malloc(strlen(node)+1);
p->domain = malloc(strlen(domain)+1);
if (!p->node || !p->domain) goto outofmemory;
strcpy(p->node, node);
strcpy(p->domain, domain);
return;
outofmemory:
fprintf(stderr, "Out of memory\n");
exit(1);
}
/*
* Walk through the database, looking for any cases where we know
* node FOO is equivalent to node BAR and node BAR has a domain name.
* For those cases, give FOO the same domain name as BAR.
*/
finish()
{
struct entry *p;
char *domain;
for (p = firstentry; p; p = p->next) {
if (!strchr(p->domain, '.') && (domain = lookup(p->domain))) {
remember(p->node, domain);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,297 @@
(Message /home/auspex/a/staff/eric/.mh/inbox:2575)
From: John Gardiner Myers <jgm+@cmu.edu>
Subject: contrib/rcpt-streaming
Date: Fri, 4 Jun 1993 13:54:06 -0400 (EDT)
To: sendmail@cs.berkeley.edu
This patch implements "RCPT streaming" in sendmail version 6. This
patch is not an official part of sendmail. Please report all problems
with this patch to jgm+@cmu.edu.
RCPT streming avoids network round trips by sending all RCPT commands
for a single SMTP transaction together. Sendmail then waits for all
the replies, matching them up with the apropriate addresses.
Apply to the sendmail src directory (your line numbers may vary) and
compile with -DRCPTSTREAM
diff -cr src.orig/deliver.c src/deliver.c
*** src.orig/deliver.c Thu May 27 14:38:22 1993
--- src/deliver.c Fri Jun 4 13:50:02 1993
***************
*** 1325,1330 ****
--- 1325,1345 ----
register int i;
/* send the recipient list */
+ #ifdef RCPTSTREAM
+ /***********************************************************************
+ *
+ * RCPT streaming code by John G Myers, jgm+@cmu.edu
+ * This is not supported by the maintainer of sendmail.
+ * Report all bugs concerning RCPT streaming to jgm+@cmu.edu
+ *
+ ***********************************************************************
+ */
+ for (to = tochain; to != NULL; to = to->q_tchain)
+ {
+ smtpstreammessage("RCPT To:<%s>", m, mci,
+ to->q_user);
+ }
+ #endif
tobuf[0] = '\0';
for (to = tochain; to != NULL; to = to->q_tchain)
{
diff -cr src.orig/usersmtp.c src/usersmtp.c
*** src.orig/usersmtp.c Thu May 27 14:38:09 1993
--- src/usersmtp.c Fri Jun 4 13:48:24 1993
***************
*** 44,49 ****
--- 44,61 ----
# include <sysexits.h>
# include <errno.h>
+ #ifdef RCPTSTREAM
+ /***********************************************************************
+ *
+ * RCPT streaming code by John G Myers, jgm+@cmu.edu
+ * This is not supported by the maintainer of sendmail.
+ * Report all bugs concerning RCPT streaming to jgm+@cmu.edu
+ *
+ ***********************************************************************
+ */
+ # include <sys/types.h>
+ # include <sys/time.h>
+ #endif
# ifdef SMTP
***************
*** 62,67 ****
--- 74,87 ----
char SmtpError[MAXLINE] = ""; /* save failure error messages */
int SmtpPid; /* pid of mailer */
+ #ifdef RCPTSTREAM
+ char *SmtpStreamBuf; /* buffer for streaming output */
+ int SmtpStreamBufSize = 0; /* allocated size of buffer */
+ char *SmtpStreamStart; /* pointer to text not yet written */
+ int SmtpStreamLen = 0; /* # chars not yet written */
+ #endif
+
+
#ifdef __STDC__
extern smtpmessage(char *f, MAILER *m, MCI *mci, ...);
#endif
***************
*** 404,410 ****
--- 424,432 ----
{
register int r;
+ #ifndef RCPTSTREAM
smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
+ #endif
SmtpPhase = mci->mci_phase = "RCPT wait";
setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
***************
*** 626,631 ****
--- 648,657 ----
bool firstline = TRUE;
char junkbuf[MAXLINE];
+ #ifdef RCPTSTREAM
+ extern char MsgBuf[]; /* err.c */
+ #endif
+
if (mci->mci_out != NULL)
(void) fflush(mci->mci_out);
***************
*** 641,646 ****
--- 667,709 ----
register char *p;
extern time_t curtime();
+ #ifdef RCPTSTREAM
+ if (SmtpStreamLen > 0) {
+ int outfd;
+
+ outfd = fileno(mci->mci_out);
+
+ nonblock(outfd, TRUE);
+ r = write(outfd, SmtpStreamStart, SmtpStreamLen);
+ nonblock(outfd, FALSE);
+ if (r == -1 && errno != EAGAIN
+ #ifdef EWOULDBLOCK
+ && errno != EWOULDBLOCK
+ #endif
+ ) {
+
+ mci->mci_errno = errno;
+ message("451 streamreply: write error to %s",
+ mci->mci_host);
+
+ /* if debugging, pause so we can see state */
+ if (tTd(18, 100))
+ pause();
+ # ifdef LOG
+ if (LogLevel > 0)
+ syslog(LOG_INFO, "%s", &MsgBuf[4]);
+ # endif /* LOG */
+ /* stop trying to write output */
+ SmtpStreamLen = 0;
+ continue;
+ }
+ if (r > 0) {
+ SmtpStreamStart += r;
+ SmtpStreamLen -= r;
+ }
+ }
+ #endif /* RCPTSTREAM */
+
/* actually do the read */
if (e->e_xfp != NULL)
(void) fflush(e->e_xfp); /* for debugging */
***************
*** 742,747 ****
--- 805,880 ----
return (r);
}
+
+ #ifdef RCPTSTREAM
+ /*
+ ** SMTPSTREAMMESSAGE -- buffer message to be streamed to server
+ **
+ ** Parameters:
+ ** f -- format
+ ** m -- the mailer to control formatting.
+ ** a, b, c -- parameters
+ **
+ ** Returns:
+ ** none.
+ **
+ ** Side Effects:
+ ** stores message in SmtpStreamBuf
+ */
+
+ /*VARARGS1*/
+ #ifdef __STDC__
+ smtpstreammessage(char *f, MAILER *m, MCI *mci, ...)
+ #else
+ smtpstreammessage(f, m, mci, va_alist)
+ char *f;
+ MAILER *m;
+ MCI *mci;
+ va_dcl
+ #endif
+ {
+ VA_LOCAL_DECL
+ int len;
+
+ VA_START(mci);
+ (void) vsprintf(SmtpMsgBuffer, f, ap);
+ VA_END;
+
+ if (tTd(18, 1) || Verbose)
+ nmessage(">>> %s", SmtpMsgBuffer);
+
+ if (mci->mci_out == NULL) {
+ if (tTd(18, 1)) printf("smtpstreammessage: NULL mci_out\n");
+ return;
+ }
+
+ strcat(SmtpMsgBuffer, m == NULL ? "\r\n" : m->m_eol);
+ len = strlen(SmtpMsgBuffer);
+
+ if (SmtpStreamLen == 0) {
+ if (SmtpStreamBufSize == 0) {
+ SmtpStreamBufSize = MAXLINE;
+ SmtpStreamBuf = xalloc(SmtpStreamBufSize);
+ }
+ SmtpStreamStart = SmtpStreamBuf;
+ }
+
+ if (SmtpStreamBufSize - SmtpStreamLen < len + 1) {
+ int start = SmtpStreamStart - SmtpStreamBuf;
+ SmtpStreamBufSize += MAXLINE;
+ SmtpStreamBuf = realloc(SmtpStreamBuf, SmtpStreamBufSize);
+ if (!SmtpStreamBuf) {
+ syserr("Out of memory!!");
+ abort();
+ /* exit(EX_UNAVAILABLE); */
+ }
+ SmtpStreamStart = SmtpStreamBuf + start;
+ }
+
+ strcpy(SmtpStreamBuf + SmtpStreamLen, SmtpMsgBuffer);
+ SmtpStreamLen += len;
+ }
+ #endif /* RCPTSTREAM */
/*
** SMTPMESSAGE -- send message to server
**
Only in src: usersmtp.c.orig
Only in src: usersmtp.c~
Only in src: usersmtp.o
diff -cr src.orig/util.c src/util.c
*** src.orig/util.c Thu May 27 14:38:20 1993
--- src/util.c Wed Jun 2 16:39:15 1993
***************
*** 955,960 ****
--- 955,1004 ----
return (FALSE);
return (TRUE);
}
+
+ #ifdef RCPTSTREAM
+ /***********************************************************************
+ *
+ * RCPT streaming code by John G Myers, jgm+@cmu.edu
+ * This is not supported by the maintainer of sendmail.
+ * Report all bugs concerning RCPT streaming to jgm+@cmu.edu
+ *
+ ***********************************************************************
+ */
+
+ #include <fcntl.h>
+
+ /*
+ ** NONBLOCK -- set or clear non-blocking mode on file descriptor
+ **
+ ** Parameters:
+ ** fd -- the file descriptor
+ ** mode -- TRUE to set non-blocking mode
+ ** FALSE to clear non-blocking mode
+ **
+ ** Returns:
+ ** none
+ **
+ ** Side Effects:
+ ** modifies nonblocking status of fd
+ */
+
+ nonblock(fd, mode)
+ int fd;
+ bool mode;
+ {
+ int flags;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (mode) {
+ flags |= FNONBIO;
+ }
+ else {
+ flags &= ~FNONBIO;
+ }
+ fcntl(fd, F_SETFL, flags);
+ }
+ #endif
/*
** STRCONTAINEDIN -- tell if one string is contained in another
**
Only in src: util.c.orig
Only in src: util.o
Only in src: version.o

View File

@ -0,0 +1,207 @@
XLA - Extended Load Average design for Sendmail R6
--------------------------------------------------
Christophe Wolfhugel - Herve Schauer Consultants
wolf@grasp.insa-lyon.fr, wolf@hsc-sec.fr
WARNING: this extension is supplied as a contribution to Sendmail.
Should you have trouble, questions, please contact me directly, and
*not* the Sendmail development team.
ABSTRACT
Sendmail currently furnishes a limitation mecanism which is based on
the system load average, when available. Experience has prooven that
this was not sufficiant for some particular situations, for example
if you have slow and/or overloaded links. This can easily cause both
system and network congestions with Sendmail having to handle a large
number of simultaneous sessions on the same overloaded link, causing
most of the SMTP sessions to timeout after a long time. The system
load average is also generally too slow to react when your system
gets a burst of incoming or outgoing SMTP sessions which on some
stations can easily cause system unavailabilities.
The extended load average module has been designed in order to furnish
a way of limitation the load generated by Sendmail to both your
system and your network. This design can be used either alone or as
complementary to the system load average if your system supports it.
Limitation is based on the number of incoming/outgoing SMTP sessions,
and remote hosts are classified in classes. The system administrator
will define a maximum number of incoming SMTP sessions as well as
a maximum total (incoming + outgoing) sessions for each class of
hosts. A class can be either an individual machine or a network.
When the limit is reached for a given class, all incoming SMTP
connections will be politely refused. When the limit is reached for
all classes, the SMTP connections will be refused by the system
(which one could consider as less politely :)).
On outgoing mail, messages will be queued for delayed processing.
The extended load average parameters are given in the Sendmail
configuration file, and when not present, Sendmail behaves the
usual way.
COMPILATION
Copy the xla.c module in the src sub-directory, edit the Makefile
in order to define XLA (-DXLA). Also add the xla.[co] module name
in the list of files so that it gets compiled.
Regenerate sendmail by removing all objects, or at least those
containing references to XLA (this list may vary, so use grep to
get the module list). This will generate a new sendmail executable
containing the xla code.
Debugging level 59 has been assigned to this module and when used
it provides some output (sendmail -d59.x). Please check the source
code to see which levels are supported.
CONFIGURATION
The extended average uses a new set of configuration lines in the
sendmail.cf file. All newly introduced line begin with the letter L
(capital L).
Before detailling the syntax, first an example (this can be placed
at any section of the sendmail.cf file, note that the order is
important). Fields are separated by (one or more) tabs/spaces.
# File name used to store the counters
L/etc/sendmail.la
# Classes definition
# Lname #queue #reject
L*.insa-lyon.fr 8 3
L*.univ-lyon1.fr 6 4
L* 15 16
The first line defines the working file which will be used in order
to have the occurences of Sendmail read and update the counters. The
format of this file is described in the "Design" section.
This line is mandatory and the specified file must be absolute (ie
begin with a slash).
Then you can specify one or more classes. The last class (*) is also
mandatory and should be in last position as the first match will stop
the search and if there is no match the behavior of Sendmail is unknown.
Each class has three fields separated by one or more tabs/spaces.
L{mask} {queue_#} {refuse_#}
The {mask} is a simple mask. It can be either an explicit host name
(like grasp.insa-lyon.fr) or a mask starting with "*." or just "*".
No other variants are allowed.
Lgrasp.insa-lyon.fr will match exactely any session to/from this host.
L*.insa-lyon.fr will match any session to/from any machine in the
insa-lyon.fr domain as well as from the machine
named "insa-lyon.fr" if it exists.
L* will match any session, and thus should also be
last in the list to act as a catchup line.
The {queue_#} is the maximum number of SMTP sessions in the given class
for both incoming and outgoing messages. The {refuse_#} indicates when
to refuse incoming messages for this class. The interaction between
those counters is somewhat subtle. It seems logical that a standard
configuration has {queue_#} >= {refuse_#}, and in fact in most
configurations they can be equal (that's why what I use in my environment).
Thus, this is not mandatory. If {queue_#} < {refuse_#} outgoing messages
will be lower priority than incoming messages and once a class gets loaded
the outgoing messages are blocked first.
I use very low values in some situations, for example I have a customer
connected to the Internet via a 9600 bps line, they also have internal
users sending burst of messages (10, 20 small messages coming in just
one or two seconds). Both situations were unsupportable. The line is
too slow to handle many simultaneous connections and the mail server
does not have the ressources to handle such a heavy load (it's a 12 Megs
Sun 3 also doing Usenet news).
I have defined following section in the configuration file, and experience
shows the benefits for everyone. Fake domain for the example: customer.fr.
L/etc/sendmail.la
L*.customer.fr 8 8
L* 3 3
This means that there might not be more than 8 simultaneous SMTP sessions
between the mail server and any other internal host. This is to protect
the station from heavy loads like users (or applications !) sending
several tenths of messages in just a few seconds).
No more than 3 SMTP sessions are authorized with any other host, this is
to save the load of the slow 9600 line to the Internet.
Drawback is that is you have 3 * 2 Megs sessions established from/to the
outside, all your mail will be held until one slot gets available, on
a 9600 bps line just make your counts, il blocks your line during over
one hour.
DESIGN
Sendmail will analyze the "L" lines in the configuration file during
startup (or read the initialized structure from the frozen file).
When started in daemon mode (and only there), any existing working file
will be cleared and a new one is created. Each class gets a record in
the sendmail.la work file. The size of this record is a short integer
(generally two bytes) and represents the count of active sessions in
the given class. Read/Write operations in this file are done in
one operation (as anyway the size is far below one disk sector). The
file is locked with Sendmail's lockfile() function priori to any
access.
Handling incoming SMTP sessions.
There is interaction is two points in the Sendmail source code. First
on the listen system call: if all slots in all classes are in use,
a listen(0) is done so that the system rejects any incoming SMTP session.
This avois to fork and then reject the connexion.
If there are some free slots, nothing better than accepting the
connection, then forking can be done. The child process then checks if
the adequate class is full or not. If full, it rejects the connection
with a "421 Too many sessions" diagnostic to the sender (which should
then appear when the remote users do a mailq). If the treshold {reject_#}
is not reached, the connection is accepted and the counter is sendmail.la
is updated.
Handling outgoing SMTP sessions.
As soon as Sendmail needs to connect to a distant host, the adequate class
is checked against {queue_#} and if no slots are available, the message is
queued for further processing.
Sendmail's connection caching.
Sendmail-R6 introduces a new design: connection caching, ie several SMTP
sessions can be opened at the same time. This could cause some problems
when sending mail, as after having a few connections opened, all slots
could be in use and generate a partial delivery of the message. In
order to deal with this, xla.c uses following design "for a given
sendmail process, only the first connection in a given class is counted".
This can be done because sendmail does not do parralel message sending
on the different channels.
End of connection.
As soon as a connection is closed, the counters will be automatically
updated.
Please look at the code to understand of all this works. Comments,
suggestions, questions welcome.
Christophe Wolfhugel
Herve Schauer Consultants
Paris, France
May 23, 1993

View File

@ -0,0 +1,528 @@
/*
* (C) Copyright 1993, Herve Schauer Consultants
*
* This module written by Christophe.Wolfhugel@hsc-sec.fr
* is to be used under the same conditions and terms (and absence
* or warranties) than the other modules of the Sendmail package.
*
* ABSOLUTELY NO WARRANTY. USE AT YOUR OWN RISKS.
*
*/
#ifdef XLA
#ifndef MAXLARULES
# define MAXLARULES 20
#endif
# include "sendmail.h"
typedef struct {
short queue; /* # of connexions to have queueing */
short reject; /* # of conn. to reject */
short num; /* # of increments this process */
char *mask; /* Mask (domain) */
} XLARULE;
char *XlaFname; /* Work file name */
char XlaHostName[1024]; /* Temporary */
int XlaNext; /* # of XlaRules */
pid_t XlaPid; /* Pid updating the tables */
XLARULE XlaRules[MAXLARULES]; /* The rules themselves */
short XlaCtr[MAXLARULES]; /* Counter (for work file only) */
extern bool lockfile();
/*
** XLAMATCH -- Matches a fnmatch like expression.
**
** Parameters:
** mask -- mask to match the string too;
** name -- string.
**
** Mask can either be a plain string or a simplified fnmatch like mask:
** *.string or string.*
** No other alternatives are accepted.
**
** Returns:
** none.
**
** Side Effects:
** none.
*/
bool
XlaMatch(mask, name)
char *mask, *name;
{
int l1, l2;
l1 = strlen(mask); l2 = strlen(name);
if (l1 == 1 && mask[0] == '*') return(TRUE);
if (mask[0] == '*' && mask[1] == '.') {
if (l2 < (l1 - 2)) return(FALSE);
if (strcasecmp(&mask[2], name) == 0) return(TRUE);
if (strcasecmp(&mask[1], name + l2 - l1 + 1) == 0) return(TRUE);
return(FALSE);
}
if (l1 < 3) return(FALSE);
if (mask[l1 -1] == '*') {
if (l2 < l1 - 1) return(FALSE);
if (strncasecmp(mask, name, l1 - 1) == 0) return(TRUE);
return(FALSE);
}
if (strcasecmp(mask, name) == 0) return(TRUE);
return(FALSE);
}
/*
** XLAZERO -- Zeroes the used variables
**
** Just initializes some variables, called once at sendmail
** startup.
**
** Parameters:
** none.
**
** Returns:
** none.
**
** Side Effects:
** none.
*/
xla_zero()
{
if (tTd(59, 1)) {
printf("xla_zero\n");
}
XlaFname = NULL;
XlaNext = 0;
XlaPid = 0;
memset(&XlaRules[0], 0, sizeof(XLARULE) * MAXLARULES);
}
/*
** XLAINIT -- initialized extended load average stuff
**
** This routine handles the L lines appearing in the configuration
** file.
**
** L/etc/sendmail.la indicates the working file
** Lmask #1 #2 Xtended LA to apply to mask
** #1 = Queueing # of connections
** #2 = Reject connections.
**
** Parameters:
** line -- the cf file line to parse.
**
** Returns:
** none.
**
** Side Effects:
** Builds several internal tables.
*/
xla_init(line)
char *line;
{
char *s;
if (tTd(59, 1)) {
printf("xla_init line: %s\n", line);
}
if (XlaFname == NULL && *line == '/') { /* Work file name */
XlaFname = newstr(line);
if (tTd(59, 10))
printf("xla_init: fname = %s\n", XlaFname);
return;
}
if (XlaNext == MAXLARULES) {
syserr("too many xla rules defined (%d max)", MAXLARULES);
return;
}
s = strtok(line, " \t");
if (s == NULL) {
syserr("xla: line unparseable");
return;
}
XlaRules[XlaNext].mask = newstr(s);
s = strtok(NULL, " \t");
if (s == NULL) {
syserr("xla: line unparseable");
return;
}
XlaRules[XlaNext].queue = atoi(s);
s = strtok(NULL, " \t");
if (s == NULL) {
syserr("xla: line unparseable");
return;
}
XlaRules[XlaNext].reject = atoi(s);
if (tTd(59, 10))
printf("xla_init: rule #%d = %s q=%d r=%d\n", XlaNext,
XlaRules[XlaNext].mask,
XlaRules[XlaNext].queue, XlaRules[XlaNext].reject);
XlaNext++;
}
/*
** XLACREATEFILE -- Create the working file
**
** Tries to create the working file, called each time sendmail is
** invoked with the -bd option.
**
** Parameters:
** none.
**
** Returns:
** none.
**
** Side Effects:
** Creates the working file (sendmail.la) and zeroes it.
*/
xla_create_file()
{
int fd, i;
if (tTd(59, 1))
printf("xla_create_file:\n");
if (XlaFname == NULL) return;
fd = open(XlaFname, O_RDWR|O_CREAT, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_create_file: open failed");
return;
}
if (!lockfile(fd, XlaFname, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_create_file: can't set lock");
return;
}
if (ftruncate(fd, 0) == -1) {
close(fd);
XlaFname = NULL;
syserr("xla_create_file: can't truncate XlaFname");
return;
}
if (write(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
XlaFname == NULL;
syserr("xla_create_file: can't write XlaFname");
}
close(fd);
}
/*
** XLASMTPOK -- Checks if all slots are in use
**
** Check is there are still some slots available for an SMTP
** connection.
**
** Parameters:
** none.
**
** Returns:
** TRUE -- slots are available;
** FALSE -- no more slots.
**
** Side Effects:
** Reads a file, uses a lock and updates sendmail.la if a slot
** is free for use.
*/
bool
xla_smtp_ok()
{
int fd, i;
if (tTd(59, 1))
printf("xla_smtp_ok:\n");
if (XlaFname == NULL) return(TRUE);
fd = open(XlaFname, O_RDWR, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_smtp_ok: open failed");
return(TRUE);
}
if (!lockfile(fd, XlaFname, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_smtp_ok: can't set lock");
return(TRUE);
}
if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
close(fd);
XlaFname = NULL;
syserr("xla_smtp_ok: can't read XlaFname");
return(TRUE);
}
close(fd);
for (i = 0; i < XlaNext; i++) {
if (XlaCtr[i] < XlaRules[i].reject)
return(TRUE);
}
return(FALSE);
}
/*
** XLAHOSTOK -- Can we accept a connection from this host
**
** Check the quota for the indicated host
**
** Parameters:
** name -- host name or IP# (string)
**
** Returns:
** TRUE -- we can accept the connection;
** FALSE -- we must refuse the connection.1
**
** Side Effects:
** Reads and writes a file, uses a lock and still updates
** sendmail.la is a slot gets assigned.
*/
bool
xla_host_ok(name)
char *name;
{
int fd, i;
if (tTd(59, 1))
printf("xla_host_ok:\n");
if (XlaFname == NULL) return(TRUE);
fd = open(XlaFname, O_RDWR, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_host_ok: open failed");
return(TRUE);
}
XlaPid = getpid();
if (!lockfile(fd, XlaFname, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_host_ok: can't set lock");
return(TRUE);
}
if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
close(fd);
XlaFname = NULL;
syserr("xla_smtp_ok: can't read XlaFname");
return(TRUE);
}
strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
XlaHostName[sizeof(XlaHostName) -1] = 0;
i = strlen(name) - 1;
if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
for (i = 0; i < XlaNext; i++) {
if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
if (XlaCtr[i] < XlaRules[i].reject) {
if (XlaRules[i].num++ == 0) {
XlaCtr[i]++;
lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) != sizeof(XlaCtr[i]))
XlaFname = NULL;
}
close(fd);
return(TRUE);
}
close(fd);
return(FALSE);
}
}
close(fd);
return(TRUE);
}
/*
** XLANOQUEUEOK -- Can we sent this message to the remote host
**
** Check if we can send to the remote host
**
** Parameters:
** name -- host name or IP# (string)
**
** Returns:
** TRUE -- we can send the message to the remote site;
** FALSE -- we can't connect the remote host, queue.
**
** Side Effects:
** Reads and writes a file, uses a lock.
** And still updates the sendmail.la file.
*/
bool
xla_noqueue_ok(name)
char *name;
{
int fd, i;
if (tTd(59, 1))
printf("xla_noqueue_ok:\n");
if (XlaFname == NULL) return(TRUE);
fd = open(XlaFname, O_RDWR, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_noqueue_ok: open failed");
return(TRUE);
}
if (!lockfile(fd, XlaFname, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_noqueue_ok: can't set lock");
return(TRUE);
}
XlaPid = getpid();
if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
close(fd);
XlaFname = NULL;
syserr("xla_noqueue_ok: can't read XlaFname");
return(TRUE);
}
strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
XlaHostName[sizeof(XlaHostName) -1] = 0;
i = strlen(name) - 1;
if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
for (i = 0; i < XlaNext; i++) {
if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
if (XlaCtr[i] < XlaRules[i].queue) {
if (XlaRules[i].num++ == 0) {
XlaCtr[i]++;
lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) != sizeof(XlaCtr[i]))
XlaFname = NULL;
}
close(fd);
return(TRUE);
}
close(fd);
return(FALSE);
}
}
close(fd);
return(TRUE);
}
/*
** XLAHOSTEND -- Notice that a connection is terminated.
**
** Updates the counters to reflect the end of an SMTP session
** (in or outgoing).
**
** Parameters:
** name -- host name or IP# (string)
**
** Returns:
** none.
**
** Side Effects:
** Reads and writes a file, uses a lock.
** And still updates sendmail.la.
*/
xla_host_end(name)
char *name;
{
int fd, i;
if (tTd(59, 1))
printf("xla_host_end:\n");
if (XlaFname == NULL || XlaPid != getpid()) return;
fd = open(XlaFname, O_RDWR, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_host_end: open failed");
return;
}
if (!lockfile(fd, XlaFname, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_host_end: can't set lock");
return;
}
if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
close(fd);
XlaFname = NULL;
syserr("xla_host_end: can't read XlaFname");
return(TRUE);
}
strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
XlaHostName[sizeof(XlaHostName) -1] = 0;
i = strlen(name) - 1;
if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
for (i = 0; i < XlaNext; i++) {
if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
if (XlaRules[i].num > 0 && XlaRules[i].num-- == 1) {
if (XlaCtr[i]) XlaCtr[i]--;
lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i]))
!= sizeof(XlaCtr[i]))
XlaFname = NULL;
}
close(fd);
return;
}
}
close(fd);
}
/*
** XLAALLEND -- Mark all connections as closed.
**
** Generally due to an emergency exit.
**
** Parameters:
** name -- host name or IP# (string)
**
** Returns:
** none.
**
** Side Effects:
** Reads and writes a file, uses a lock.
** And guess what: updates sendmail.la.
*/
xla_all_end()
{
int fd, i;
if (tTd(59, 1))
printf("xla_all_end:\n");
if (XlaFname == NULL || XlaPid != getpid()) return;
fd = open(XlaFname, O_RDWR, 0644);
if (fd == -1) {
XlaFname = NULL;
syserr("xla_all_end: open failed");
return;
}
if (!lockfile(fd, XlaFname, LOCK_EX)) {
close(fd);
XlaFname = NULL;
syserr("xla_all_end: can't set lock");
return;
}
if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
close(fd);
XlaFname = NULL;
syserr("xla_all_end: can't read XlaFname");
return(TRUE);
}
for (i = 0; i < XlaNext; i++) {
if (XlaCtr[i] > 0 && XlaRules[i].num > 0) {
XlaCtr[i]--; XlaRules[i].num = 0;
}
}
lseek(fd, 0, SEEK_SET);
if (write(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
XlaFname = NULL;
}
close(fd);
}
#endif /* XLA */