diff options
Diffstat (limited to 'usr.sbin/sendmail/contrib')
-rw-r--r-- | usr.sbin/sendmail/contrib/README | 10 | ||||
-rw-r--r-- | usr.sbin/sendmail/contrib/bitdomain.c | 409 | ||||
-rw-r--r-- | usr.sbin/sendmail/contrib/converting.sun.configs | 446 | ||||
-rwxr-xr-x | usr.sbin/sendmail/contrib/expn.pl | 1365 | ||||
-rw-r--r-- | usr.sbin/sendmail/contrib/mail.local.linux | 205 | ||||
-rw-r--r-- | usr.sbin/sendmail/contrib/mh.patch | 193 | ||||
-rw-r--r-- | usr.sbin/sendmail/contrib/mmuegel | 2079 | ||||
-rw-r--r-- | usr.sbin/sendmail/contrib/oldbind.compat.c | 79 | ||||
-rw-r--r-- | usr.sbin/sendmail/contrib/rcpt-streaming | 305 | ||||
-rw-r--r-- | usr.sbin/sendmail/contrib/xla/README | 207 | ||||
-rw-r--r-- | usr.sbin/sendmail/contrib/xla/xla.c | 532 |
11 files changed, 5830 insertions, 0 deletions
diff --git a/usr.sbin/sendmail/contrib/README b/usr.sbin/sendmail/contrib/README new file mode 100644 index 0000000..dcf5c8f --- /dev/null +++ b/usr.sbin/sendmail/contrib/README @@ -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 diff --git a/usr.sbin/sendmail/contrib/bitdomain.c b/usr.sbin/sendmail/contrib/bitdomain.c new file mode 100644 index 0000000..4fad761 --- /dev/null +++ b/usr.sbin/sendmail/contrib/bitdomain.c @@ -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); + } + } +} + diff --git a/usr.sbin/sendmail/contrib/converting.sun.configs b/usr.sbin/sendmail/contrib/converting.sun.configs new file mode 100644 index 0000000..0fcd919 --- /dev/null +++ b/usr.sbin/sendmail/contrib/converting.sun.configs @@ -0,0 +1,446 @@ + + Converting Standard Sun Config + Files to Sendmail Version 8 + + Rick McCarty + Texas Instruments Inc. + Latest Update: 08/25/93 - RJMc + +This document details the changes necessary to continue using your +current SunOS sendmail.cf with sendmail version 8. In the longer term, +it is recommended that one move to using an m4 based configuration such +as those shipped with sendmail, but if you're like me and have made +enough modifications to your .cf file that you'd rather put that task +off until later, here's the sum total of my experience to get you to +version 8 with minimal pain. I'll cover .cf as well as build issues. + +Some background - as many are surely aware, Sun has some "special" +features in the sendmail they ship ($%x, %y LHS lookup, NIS alias DB +search, etc.). (Some of those features can be had in alternative forms +in IDA sendmail, but v8 has picked up some IDA capabilities as well as +new ones, making it IMHO a most desirable version to go to.) What I +will explain below includes v8 functional "equivalences" to these Sun +sendmail features. + +So with that out of the way, let's begin. + +First, some assumptions: + + 1) I'm going to assume you've got sendmail version 8.6 or + later in hand - if not, grab it from ftp.cs.berkeley.edu + in the ucb/sendmail directory. There are bugs in earlier + versions which affect some of the needed functionality. + + 2) Second, I'm going to detail this based upon the + "sendmail.main.cf" configuration. (BTW, if you attempt + to move to using an m4 generated config in the future, + MAIL_HUB is the feature which should provide similar + functionality). + + In general, the changes will be similar for a subsidiary + file, but since we (my TI group) funnel all non-local mail + through our mailhost, we're not as interested in getting v8 + to run on such systems and I haven't tried it. + + 3) You're using DNS and sendmail.mx. If you're not, you ought + to be, even if you're also running it along with NIS (which + we do - except for gethostbyxxx() lookups, which I'll be + talking about later). I would imagine you could get things + running OK without DNS support, but I haven't tried it myself. + + 4) You're not mounting /var/spool/mail from other systems. + I haven't found a v8 feature to guarantee this will work + correctly. Anyway, in the past, we've tried doing that + here and found it to be a rather "ugly" feature, though + Sun ostensibly supports it ("R" option). Perhaps v8 + will one day have a similar feature, but for now, bottom + line, I would recommend against it. + + 5) You're not on Solaris or using NIS+. I'm on 4.1.3. I've + looked at Solaris briefly and have noted that things are + pretty much similar there except that they've moved some + things into the /etc/mail directory. I'd guess the + executables aren't functionally all that different from + what they had before - the configs are roughly the same. + So I'd bet most of what I say in here will apply to + Solaris. + +OK, let's configure our sendmail.cf! I'll just go from the top down... + + VARIOUS DECLARATIONS + +1) For v8, you need to define your .cf as AT LEAST a version level 4 + configuration. Add the following line: + + V4 + + There are some issues regarding certain predefined macros - $w, $j, and + $m. With a V4 configuration: + + $w is defined to be the hostname, which will usually be fully + qualified (i.e. "firefly.add.itg.ti.com"). + + $j should have the same value as $w. + + $m will be predefined as the domain portion of $w + (ex. "add.itg.ti.com"). + + One note about this - if your configuration relies on the "w" macro to + be the "simple" hostname (as mine does)... + + If the configuration version is 5 or larger: + + $w is supposed to be the "simple" name (ex. "firefly") + + $j should be the fully qualified name (i.e. "firefly.add.itg.ti.com") + + $m will be predefined as the domain portion of $j + (ex. "add.itg.ti.com"). + + I have not experimented with the various combinations, so I cannot + guarantee you that the above definitions will always come out as + expected. Bottom line: if your sendmail.cf depends on $w being the + simple hostname, test it carefully or define the name explicitly, + for example: + + Dwfirefly + +2) To replace the Sun's "%y" feature, we must use a hostname mapping + feature in v8. If you want to do similar lookups with v8, you need + to define the following map (we'll go over the rules that use this + map later): + + Khostlookup host -f -m -a. + + This will define a "lookup only" map that is otherwise the same as + sendmail version 8's built-in "host" map (see the "Sendmail + Installation and Operation Guide" for details on this map.). + + An important note: Whether or not these lookups will be done via + NIS is a function of what gethostbyxxx() functions you link into + your sendmail. DO NOT redefine your host mapping to use NIS + explicitly within sendmail - there can be unexpected behaviour if + you do so (if you do any canonicalization in your .cf, you can get + incorrect results, for one thing). + + For example, DO NOT TRY: + + Khost nis -f -a. hosts.byname + +3) If you're doing reverse alias mapping as done in ruleset 22, instead of: + + DZmail.byaddr + + you'll need to declare the following: + + Kaliasrev nis -f -N mail.byaddr + +4) If you are doing any other NIS map lookups, you'll need to define the + map as done in the below example. I have a "mailhosts" map, which I + use to distinguish between local and non-local hosts. Look at the + sendmail doc for details on this stuff. + + Kmailhosts nis -f -m -a. mailhosts + +5) You might wish to add the following line to support Errors-To: headers. + I don't. + + Ol + +6) Comment out/remove the following line: + + OR + + The R option means something different under v8 - check the documentation + if you're interested in using it. + +7) If you're running NIS and have a separate alias map, BELOW the + following line where the alias file is declared: + + OA/etc/aliases + + ADD the following: + + OAnis:mail.aliases + + This will set things up so v8 will look at the local alias DB first, + then the NIS map, just as Sun sendmail does. + +8) Though you don't have to, I'd suggest changing: + + OT3d + + to use v8's warning feature, which allows a warning message to be + sent if a message cannot be delivered within a specified period. + I use: + + OT5d/4h + + which says - bounce after 5 days, warn after 4 hours. + +9) I set the following option to be explicit about how I want DNS + handled: + + OI +DNSRCH +DEFNAMES + +10) The following line: + + T root daemon uucp + + may be deleted, though it will be ignored if you leave it around. + +11) It would probably be good to change the version macro value (which + shows up in "Received:" headers) so no one debugging mail problems + gets the wrong idea about what config you're running under. Look + for something like: + + DVSMI-4.1 + + Mine, for example is: + + DVADD-HUB-2.1 + + RULESETS + +1) In ruleset 3, BELOW this rule: + + # basic textual canonicalization + R$*<$+>$* $2 basic RFC822 parsing + + +I add the following rule to remove a trailing dot in the domain spec so +it won't interfere with v8 mapping features, etc. (Having a trailing dot is +not RFC-compliant anyway.): + + R$+. $1 + +2) Because ruleset 5 is special in v8, I rename it to S95 and also change + all RHS expressions containing ">5" to use ">95" instead. In v8, + 5 is executed against addresses which resolve to the local mailer and + are not an alias. If you don't change S5 to something else, you might + get a surprise! + +3) If you're doing any lookups via the generalized NIS "$%x/$!x" + mechanisms (such as with the mailhost map I referred to earlier) it's + done differently under v8. For example: + + DMmailhosts + ... + R$*<@$%M.uucp>$* $#ether $@$2 $:$1<@$2>$3 + + takes a different map definition and two rules under version 8: + + Kmailhosts nis -f -m -a. mailhosts + ... + R$*<@$+.uucp>$* $: $1<@$(mailhosts $2 $).uucp>$3 + R$*<@$+..uucp>$* $#ether $@$2 $:$1<@$2>$3 + +4) Sun has a special case of the "$%x" feature for host lookups - "%y" is + automagically defined to do an NIS "hosts.byname" search with no other + definition, as done in the below example: + + R$*<@$%y.LOCAL>$* $#ether $@$2 $:$1<@$2>$3 + + (Sun does this in more than one place. But the above syntax is almost + identical in each - mostly a case of changing names to protect the + innocent.) + + In version 8, the predefined "host" map can be used to do essentially + the same thing. (However, whether or not it does an NIS lookup is + a function of what gethostbyxxx() functions are linked in.) + + Recall the map definition I mentioned earlier in the DECLARATIONS + section: + + Khostlookup host -f -m -a. + + Here's where we will use it. It will take two rules: + + R$*<@$+.LOCAL>$* $: $1<@$(hostlookup $2 $).LOCAL>$3 + R$*<@$+..LOCAL>$* $#ether $@$2 $:$1<@$2>$3 + + Note that this is almost verbatim the same change as was used in the + previous "mailhosts" example. + +5) Although Sun's default configs don't do this, because I mentioned + canonicalization earlier, it deserves an example, as it's illustrative + of the functional difference in the map definitions I discussed before. + This stuff is also convered in the "Sendmail Installation and Operation + Guide". + + Remember the built-in "host" map definition? As you'll recall, unlike + the "hostlookup" map we defined, "host" will actually CHANGE the + hostname in addition to appending a dot. "hostlookup" only appends a + dot if the name is found and doesn't change it otherwise. Anyway, + here's the example: + + R$*<@$+>$* $: $1<@$(host $2 $)>$3 canonicalize + R$*<@$+.>$* $1<@$2>$3 remove trailing dot + + Using the above, say you had input of: + + joe<@tilde> + + OR + + joe<@[128.247.160.56]> + + Assuming "tilde" or the IP address is found, it might be + canonicalized as: + + joe<@tilde.csc.ti.com> + +6) As another instance of the NIS lookup feature, with a slightly + different twist, Sun implements reverse alias mapping in ruleset 22 + with the below: + + DZmail.byaddr + ... + R$-<@$-> $:$>3${Z$1@$2$} invert aliases + + To use this feature under v8, change the above rule a (remember to + define the alias map as I showed earlier): + + R$-<@$-> $:$>3$(aliasrev $1@$2 $) invert aliases + + + MAILER DEFINITIONS + +1) Where "TCP" is defined in the "P=" and "A=" parameters of mailers, I + changed it to "IPC". Version 8 will accept "TCP", but "IPC" is + preferred. + +2) On all IPC mailers, I also defined "E=\r\n" and added an "L=1000" as + in the below example: + + Mether, P=[IPC], F=mDFMuCX, S=11, R=21, L=1000, E=\r\n, A=IPC $h + + The "E=\r\n" will save you headaches interoperating with such things as + VMS TCP products. + + The "L=1000" is for RFC821 compatibility. Not strictly necessary. + + I also removed the "s" (strip quotes) mailer flag Sun puts in for + these mailers. Stripping quotes violates protocols, which say + clearly that you can't touch the local-part (left hand side of + the @) until you are on the delivering host. + +NOW. If I haven't left anything out, you should be able to run through +your Sun sendmail.cf file and convert it to run under v8. + + BUILD ISSUES + +Some important notes on building v8 on SunOS: + +Makefile + +The default makefile in the version 8 source (src) directory assumes the +new Berkeley make. Unless you want to go to the trouble of building it, +you can use your regular make, but you need to use a different makefile. +You can use "Makefile.dist" or "Makefile.SunOS" in the src directory. I +made changes to get it to build so it is as compatible as possible with +the file/directory locations Sun uses. Here are some relevant sections +out of my makefile: + + CC=gcc + + # use O=-O (usual) or O=-g (debugging) + O= -O + + # define the database mechanisms available for map & alias lookups: + # -DNDBM -- use new DBM + # -DNEWDB -- use new Berkeley DB + # -DNDBM -DNEWDB -DYPCOMPAT -- use both plus YP compatility + # -DNIS -- include client NIS support + # The really old (V7) DBM library is no longer supported. + # See READ_ME for a description of how these flags interact. + #DBMDEF= -DNDBM -DNEWDB + DBMDEF= -DNDBM -DNIS + + # environment definitions (e.g., -D_AIX3) + ENVDEF= + + # see also conf.h for additional compilation flags + + # library directories + LIBDIRS=-L/usr/local/lib + + # libraries required on your system + #LIBS= -ldb -ldbm + LIBS= -ldbm -lresolv + + # location of sendmail binary (usually /usr/sbin or /usr/lib) + BINDIR= ${DESTDIR}/usr/lib + + # location of sendmail.st file (usually /var/log or /usr/lib) + STDIR= ${DESTDIR}/etc + + # location of sendmail.hf file (usually /usr/share/misc or /usr/lib) + HFDIR= ${DESTDIR}/usr/lib + +For the resolver library, you can use the one shipped with Sun if you +want. But I'd recommend using another version of the resolver library +(such as the one with Bind 4.8.3 or 4.9). Sun's resolver stuff (at +least with 4.1.x) is quite old - I believe it is of 4.3.1 vintage. (Do +you get the impression I don't TRUST what Sun ships with their systems?) + +If you want NIS host lookup while maintaining DNS capability, you might +take a look at resolv+, which has NIS capable gethostbyxxx() functions +in it. My recommendation, however, is to avoid doing NIS host lookups +in sendmail altogether, and to use a "pure" version of the resolver +library. + +There are probably no situations (at least I think so) where it makes +any sense to link in Sun's NIS gethostbyxxx() functions from libc. +You could, I guess do it (I haven't tried it) and wind up with a +sendmail equivalent to the non-mx version Sun ships. You'd need to +insure that NAMED_BIND is not defined in the build. (If you do +this and have the "-b" DNS passthru option set in NIS, remember that +while you have some DNS functionality you'll not have any MX support. +(This, IMO, is what makes this a non-optimal choice.) + + INSTALLATION/TESTING ISSUES + +The sendmail.hf file in the src directory should replace the one currently +in /usr/lib. You also might choose to edit it a bit to "localize" what it +says. + +The sendmail executable goes, of course, in /usr/lib in place of the current +one. What I did was create a subdirectory in /usr/lib and put all of the +Sun sendmail stuff in there. I named the v8 sendmail executable to be +sendmail.v8.mx and then symbolically linked it to sendmail. + +One other thing. If you use address test mode, keep in mind that +Version 8 is like IDA in that it does not automatically execute ruleset +3 first. So say you're playing around with things testing addresses and +you're used to things like: + + 0 jimbob@good.old.boy.com + +under v8 you need to say instead: + + 3,0 jimbob@good.old.boy.com + + INTEROPERABILITY ISSUES YOU MIGHT ENCOUNTER + +Be aware that sendmail v8 issues a multi-line SMTP welcome (220) +response upon a client connection. Most systems in your network should +handle it OK, but there are some that choke on it, because whoever wrote +the clients assumed only a single line. THIS IS NOT SENDMAIL's FAULT. +A multi-line 220 response is perfectly valid. A likely place you'll +encounter this problem is with non-Un*x SMTP clients. If you do run +into it, you should report it to the vendor. + +A final note about version 8 - if you follow the above configuration +scenario, you'll notice it doesn't like to get envelope sender +addresses it doesn't know how to get back to. Sun sendmail would take +anything, even though it might not be able to bounce the message back +should something happen downstream. So if another sendmail on a host +that's not locally known is trying to pump mail through your v8 host, +the ENVELOPE sender it gives had better be fully qualified. This is +a GREAT thing, because it helps clear up problems we've had with not +being able to get things back to the sender, resulting in an +overburdened postmaster. + +I hope this helps those running Sun sendmail feel more at ease with moving +on to v8. It's really worth going to. diff --git a/usr.sbin/sendmail/contrib/expn.pl b/usr.sbin/sendmail/contrib/expn.pl new file mode 100755 index 0000000..495db73 --- /dev/null +++ b/usr.sbin/sendmail/contrib/expn.pl @@ -0,0 +1,1365 @@ +#!/usr/local/bin/perl +'di '; +'ds 00 \\"'; +'ig00 '; +# +# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin. +# + +# hardcoded constants, should work fine for BSD-based systems +$AF_INET = 2; +$SOCK_STREAM = 1; +$sockaddr = 'S n a4 x8'; + +# system requirements: +# must have 'nslookup' and 'hostname' programs. + +# $Header: /home/muir/bin/RCS/expn,v 3.6 1994/02/23 22:26:19 muir Exp muir $ + +# TODO: +# less magic should apply to command-line addresses +# less magic should apply to local addresses +# add magic to deal with cross-domain cnames + +# Checklist: (hard addresses) +# 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us> +# harry@hofmann.cs.Berkeley.EDU -> harry@tenet (.berkeley.edu) [dead] +# bks@cs.berkeley.edu -> shiva.CS (.berkeley.edu) [dead] +# dan@tc.cornell.edu -> brown@tiberius (.tc.cornell.edu) + +############################################################################# +# +# Copyright (c) 1993 David Muir Sharnoff +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the David Muir Sharnoff. +# 4. The name of David Sharnoff may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL DAVID MUIR SHARNOFF BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# This copyright notice derrived from material copyrighted by the Regents +# of the University of California. +# +# Contributions accepted. +# +############################################################################# + +# overall structure: +# in an effort to not trace each address individually, but rather +# ask each server in turn a whole bunch of questions, addresses to +# be expanded are queued up. +# +# This means that all account w.r.t. an address must be stored in +# various arrays. Generally these arrays are indexed by the +# string "$addr *** $server" where $addr is the address to be +# expanded "foo" or maybe "foo@bar" and $server is the hostname +# of the SMTP server to contact. +# + +# important global variables: +# +# @hosts : list of servers still to be contacted +# $server : name of the current we are currently looking at +# @users = $users{@hosts[0]} : addresses to expand at this server +# $u = $users[0] : the current address being expanded +# $names{"$users[0] *** $server"} : the 'name' associated with the address +# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion +# $mx_secondary{$server} : other mx relays at the same priority +# $domainify_fallback{"$users[0] *** $server"} : alternative names to try +# instead of $server if $server doesn't work +# $temporary_redirect{"$users[0] *** $server"} : when trying alternates, +# temporarily channel all tries along current path +# $giveup{$server} : do not bother expanding addresses at $server +# $verbose : -v +# $watch : -w +# $vw : -v or -w +# $debug : -d +# $valid : -a +# $levels : -1 +# S : the socket connection to $server + +$have_nslookup = 1; # we have the nslookup program +$port = 'smtp'; +$av0 = $0; +$0 = "$av0 - running hostname"; +$ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,; +chop($hostname = `hostname`); +select(STDERR); + +$usage = "Usage: $av0 [-1avwd] user[@host] [user2[host2] ...]"; +$0 = "$av0 - parsing args"; +for $a (@ARGV) { + die $usage if $a eq "-"; + while ($a =~ s/^(-.*)([1avwd])/$1/) { + eval '$'."flag_$2 += 1"; + } + next if $a eq "-"; + die $usage if $a =~ /^-/; + &expn(&parse($a,$hostname,undef,1)); +} +$verbose = $flag_v; +$watch = $flag_w; +$vw = $flag_v + $flag_w; +$debug = $flag_d; +$valid = $flag_a; +$levels = $flag_1; + +die $usage unless @hosts; +if ($valid) { + if ($valid == 1) { + $validRequirement = 0.8; + } elsif ($valid == 2) { + $validRequirement = 1.0; + } elsif ($valid == 3) { + $validRequirement = 0.9; + } else { + $validRequirement = (1 - (1/($valid-3))); + print "validRequirement = $validRequirement\n" if $debug; + } +} + +$0 = "$av0 - building local socket"; +($name,$aliases,$proto) = getprotobyname('tcp'); +($name,$aliases,$port) = getservbyname($port,'tcp') + unless $port =~ /^\d+/; +($name,$aliases,$type,$len,$thisaddr) = gethostbyname($hostname); +$this = pack($sockaddr, $AF_INET, 0, $thisaddr); + +HOST: +while (@hosts) { + $server = shift(@hosts); + @users = split(' ',$users{$server}); + delete $users{$server}; + + # is this server already known to be bad? + $0 = "$av0 - looking up $server"; + if ($giveup{$server}) { + &giveup('mx domainify',$giveup{$server}); + next; + } + + # do we already have an mx record for this host? + next HOST if &mxredirect($server,*users); + + # look it up, or try for an mx. + $0 = "$av0 - gethostbyname($server)"; + + ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server); + # if we can't get an A record, try for an MX record. + unless($thataddr) { + &mxlookup(1,$server,"$server: could not resolve name",*users); + next HOST; + } + + # get a connection, or look for an mx + $0 = "$av0 - socket to $server"; + $that = pack($sockaddr, $AF_INET, $port, $thataddr); + socket(S, $AF_INET, $SOCK_STREAM, $proto) + || die "socket: $!"; + $0 = "$av0 - bind to $server"; + bind(S, $this) + || die "bind $hostname,0: $!"; + $0 = "$av0 - connect to $server"; + print "debug = $debug server = $server\n" if $debug > 8; + if (! connect(S, $that) || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) { + $0 = "$av0 - $server: could not connect: $!\n"; + $emsg = $!; + unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) { + &giveup('mx',"$server: Could not connect: $emsg"); + } + next HOST; + } + select((select(S),$| = 1)[0]); # don't buffer output to S + + # read the greeting + $0 = "$av0 - talking to $server"; + &alarm("greeting with $server",''); + while(<S>) { + alarm(0); + print if $watch; + if (/^(\d+)([- ])/) { + if ($1 != 220) { + $0 = "$av0 - bad numeric responce from $server"; + &alarm("giving up after bet responce from $server",''); + &read_response($2,$watch); + alarm(0); + print STDERR "$server: NOT 220 greeting: $_" + if ($debug || $vw); + if (&mxlookup(0,$server,"$server: did not respond with a 220 greeting",*users)) { + close(S); + next HOST; + } + } + last if ($2 eq " "); + } else { + $0 = "$av0 - bad responce from $server"; + print STDERR "$server: NOT 220 greeting: $_" + if ($debug || $vw); + unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) { + &giveup('',"$server: did not talk SMTP"); + } + close(S); + next HOST; + } + &alarm("greeting with $server",''); + } + alarm(0); + + # if this causes problems, remove it + $0 = "$av0 - sending helo to $server"; + &alarm("sending helo to $server",""); + &ps("helo $hostname"); + while(<S>) { + print if $watch; + last if /^\d+ /; + } + alarm(0); + + # try the users, one by one + USER: + while(@users) { + $u = shift(@users); + $0 = "$av0 - expanding $u [\@$server]"; + + # do we already have a name for this user? + $oldname = $names{"$u *** $server"}; + + print &compact($u,$server)." ->\n" if ($verbose && ! $valid); + if ($valid) { + # + # when running with -a, we delay taking any action + # on the results of our query until we have looked + # at the complete output. @toFinal stores expansions + # that will be final if we take them. @toExpn stores + # expnansions that are not final. @isValid keeps + # track of our ability to send mail to each of the + # expansions. + # + @isValid = (); + @toFinal = (); + @toExpn = (); + } + + ($ecode,@expansion) = &expn_vrfy($u,$server); + if ($ecode) { + &giveup('',$ecode,$u); + last USER; + } + + for $s (@expansion) { + $s =~ s/[\n\r]//g; + $0 = "$av0 - parsing $server: $s"; + + $skipwatch = $watch; + + if ($s =~ /^[25]51([- ]).*<(.+)>/) { + print "$s" if $watch; + print "(pretending 250$1<$2>)" if ($debug && $watch); + print "\n" if $watch; + $s = "250$1<$2>"; + $skipwatch = 0; + } + + if ($s =~ /^250([- ])(.+)/) { + print "$s\n" if $skipwatch; + ($done,$addr) = ($1,$2); + ($newhost, $newaddr, $newname) = &parse($addr,$server,$oldname, $#expansion == 0); + print "($newhost, $newaddr, $newname) = &parse($addr, $server, $oldname)\n" if $debug; + if (! $newhost) { + # no expansion is possible w/o a new server to call + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toFinal,$newaddr,$server,$newname); + } else { + &verbose(&final($newaddr,$server,$newname)); + } + } else { + $newmxhost = &mx($newhost,$newaddr); + print "$newmxhost = &mx($newhost)\n" + if ($debug && $newhost ne $newmxhost); + $0 = "$av0 - parsing $newaddr [@$newmxhost]"; + print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1); + # If the new server is the current one, + # it would have expanded things for us + # if it could have. Mx records must be + # followed to compare server names. + # We are also done if the recursion + # count has been exceeded. + if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) { + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toFinal,$newaddr,$newmxhost,$newname); + } else { + &verbose(&final($newaddr,$newmxhost,$newname)); + } + } else { + # more work to do... + if ($valid) { + push(@isValid, &validAddr($newaddr)); + push(@toExpn,$newmxhost,$newaddr,$newname,$level{"$u *** $server"}); + } else { + &verbose(&expn($newmxhost,$newaddr,$newname,$level{"$u *** $server"})); + } + } + } + last if ($done eq " "); + next; + } + # 550 is a known code... Should the be + # included in -a output? Might be a bug + # here. Does it matter? Can assume that + # there won't be UNKNOWN USER responces + # mixed with valid users? + if ($s =~ /^(550)([- ])/) { + if ($valid) { + print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n"; + } else { + &verbose(&final($u,$server,$oldname,"USER UNKNOWN")); + } + last if ($2 eq " "); + next; + } + # 553 is a known code... + if ($s =~ /^(553)([- ])/) { + if ($valid) { + print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n"; + } else { + &verbose(&final($u,$server,$oldname,"USER AMBIGUOUS")); + } + last if ($2 eq " "); + next; + } + # 252 is a known code... + if ($s =~ /^(252)([- ])/) { + if ($valid) { + print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n"; + } else { + &verbose(&final($u,$server,$oldname,"REFUSED TO VRFY")); + } + last if ($2 eq " "); + next; + } + &giveup('',"$server: did not grok '$s'",$u); + last USER; + } + + if ($valid) { + # + # now we decide if we are going to take these + # expansions or roll them back. + # + $avgValid = &average(@isValid); + print "avgValid = $avgValid\n" if $debug; + if ($avgValid >= $validRequirement) { + print &compact($u,$server)." ->\n" if $verbose; + while (@toExpn) { + &verbose(&expn(splice(@toExpn,0,4))); + } + while (@toFinal) { + &verbose(&final(splice(@toFinal,0,3))); + } + } else { + print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug)); + print &compact($u,$server)." ->\n" if $verbose; + &verbose(&final($u,$server,$newname)); + } + } + } + + &alarm("sending 'quit' to $server",''); + $0 = "$av0 - sending 'quit' to $server"; + &ps("quit"); + while(<S>) { + print if $watch; + last if /^\d+ /; + } + close(S); + alarm(0); +} + +$0 = "$av0 - printing final results"; +print "----------\n" if $vw; +select(STDOUT); +for $f (sort @final) { + print "$f\n"; +} +unlink("/tmp/expn$$"); +exit(0); + + +# abandon all attempts deliver to $server +# register the current addresses as the final ones +sub giveup +{ + local($redirect_okay,$reason,$user) = @_; + local($us,@so,$nh,@remaining_users); + + $0 = "$av0 - giving up on $server: $reason"; + # + # add back a user if we gave up in the middle + # + push(@users,$user) if $user; + # + # don't bother with this system anymore + # + unless ($giveup{$server}) { + $giveup{$server} = $reason; + print STDERR "$reason\n"; + } + print "Giveup!!! redirect okay = $redirect_okay; $reason\n" if $debug; + # + # Wait! + # Before giving up, see if there is a chance that + # there is another host to redirect to! + # (Kids, don't do this at home! Hacking is a dangerous + # crime and you could end up behind bars.) + # + for $u (@users) { + if ($redirect_okay =~ /\bmx\b/) { + next if &try_fallback('mx',$u,*server, + *mx_secondary, + *already_mx_fellback); + } + if ($redirect_okay =~ /\bdomainify\b/) { + next if &try_fallback('domainify',$u,*server, + *domainify_fallback, + *already_domainify_fellback); + } + push(@remaining_users,$u); + } + @users = @remaining_users; + for $u (@users) { + print &compact($u,$server)." ->\n" if ($verbose && $valid && $u); + &verbose(&final($u,$server,$names{"$u *** $server"},$reason)); + } +} +# +# This routine is used only within &giveup. It checks to +# see if we really have to giveup or if there is a second +# chance because we did something before that can be +# backtracked. +# +# %fallback{"$user *** $host"} tracks what is able to fallback +# %fellback{"$user *** $host"} tracks what has fallen back +# +# If there is a valid backtrack, then queue up the new possibility +# +sub try_fallback +{ + local($method,$user,*host,*fall_table,*fellback) = @_; + local($us,$fallhost,$oldhost,$ft,$i); + + if ($debug > 8) { + print "Fallback table $method:\n"; + for $i (sort keys %fall_table) { + print "\t'$i'\t\t'$fall_table{$i}'\n"; + } + print "Fellback table $method:\n"; + for $i (sort keys %fellback) { + print "\t'$i'\t\t'$fellback{$i}'\n"; + } + print "U: $user H: $host\n"; + } + + $us = "$user *** $host"; + if (defined $fellback{$us}) { + # + # Undo a previous fallback so that we can try again + # Nest fallbacks are avoided because they could + # lead to infinite loops + # + $fallhost = $fellback{$us}; + print "Already $method fell back from $us -> \n" if $debug; + $us = "$user *** $fallhost"; + $oldhost = $fallhost; + } elsif (($method eq 'mx') && (defined $mxbacktrace{$us}) && (defined $mx_secondary{$mxbacktrace{$us}})) { + print "Fallback an MX expansion $us -> \n" if $debug; + $oldhost = $mxbacktrace{$us}; + } else { + print "Oldhost($host, $us) = " if $debug; + $oldhost = $host; + } + print "$oldhost\n" if $debug; + if (((defined $fall_table{$us}) && ($ft = $us)) || ((defined $fall_table{$oldhost}) && ($ft = $oldhost))) { + print "$method Fallback = ".$fall_table{$ft}."\n" if $debug; + local(@so,$newhost); + @so = split(' ',$fall_table{$ft}); + $newhost = shift(@so); + print "Falling back ($method) $us -> $newhost (from $oldhost)\n" if $debug; + if ($method eq 'mx') { + if (! defined ($mxbacktrace{"$user *** $newhost"})) { + if (defined $mxbacktrace{"$user *** $oldhost"}) { + print "resetting oldhost $oldhost to the original: " if $debug; + $oldhost = $mxbacktrace{"$user *** $oldhost"}; + print "$oldhost\n" if $debug; + } + $mxbacktrace{"$user *** $newhost"} = $oldhost; + print "mxbacktrace $user *** $newhost -> $oldhost\n" if $debug; + } + $mx{&trhost($oldhost)} = $newhost; + } else { + $temporary_redirect{$us} = $newhost; + } + if (@so) { + print "Can still $method $us: @so\n" if $debug; + $fall_table{$ft} = join(' ',@so); + } else { + print "No more fallbacks for $us\n" if $debug; + delete $fall_table{$ft}; + } + if (defined $create_host_backtrack{$us}) { + $create_host_backtrack{"$user *** $newhost"} + = $create_host_backtrack{$us}; + } + $fellback{"$user *** $newhost"} = $oldhost; + &expn($newhost,$user,$names{$us},$level{$us}); + return 1; + } + delete $temporary_redirect{$us}; + $host = $oldhost; + return 0; +} +# return 1 if you could send mail to the address as is. +sub validAddr +{ + local($addr) = @_; + $res = &do_validAddr($addr); + print "validAddr($addr) = $res\n" if $debug; + $res; +} +sub do_validAddr +{ + local($addr) = @_; + local($urx) = "[-A-Za-z_.0-9+]+"; + + # \u + return 0 if ($addr =~ /^\\/); + # ?@h + return 1 if ($addr =~ /.\@$urx$/); + # @h:? + return 1 if ($addr =~ /^\@$urx\:./); + # h!u + return 1 if ($addr =~ /^$urx!./); + # u + return 1 if ($addr =~ /^$urx$/); + # ? + print "validAddr($addr) = ???\n" if $debug; + return 0; +} +# Some systems use expn and vrfy interchangeably. Some only +# implement one or the other. Some check expn against mailing +# lists and vrfy against users. It doesn't appear to be +# consistent. +# +# So, what do we do? We try everything! +# +# +# Ranking of result codes: good: 250, 251/551, 252, 550, anything else +# +# Ranking of inputs: best: user@host.domain, okay: user +# +# Return value: $error_string, @responces_from_server +sub expn_vrfy +{ + local($u,$server) = @_; + local(@c) = ('expn', 'vrfy'); + local(@try_u) = $u; + local(@ret,$code); + + if (($u =~ /(.+)@(.+)/) && (&trhost($2) eq &trhost($server))) { + push(@try_u,$1); + } + + TRY: + for $c (@c) { + for $try_u (@try_u) { + &alarm("$c'ing $try_u on $server",'',$u); + &ps("$c $try_u"); + alarm(0); + $s = <S>; + if ($s eq '') { + return "$server: lost connection"; + } + if ($s !~ /^(\d+)([- ])/) { + return "$server: garbled reply to '$c $try_u'"; + } + if ($1 == 250) { + $code = 250; + @ret = ("",$s); + push(@ret,&read_response($2,$debug)); + return @ret; + } + if ($1 == 551 || $1 == 251) { + $code = $1; + @ret = ("",$s); + push(@ret,&read_response($2,$debug)); + next; + } + if ($1 == 252 && ($code == 0 || $code == 550)) { + $code = 252; + @ret = ("",$s); + push(@ret,&read_response($2,$watch)); + next; + } + if ($1 == 550 && $code == 0) { + $code = 550; + @ret = ("",$s); + push(@ret,&read_response($2,$watch)); + next; + } + &read_response($2,$watch); + } + } + return "$server: expn/vrfy not implemented" unless @ret; + return @ret; +} +# sometimes the old parse routine (now parse2) didn't +# reject funky addresses. +sub parse +{ + local($oldaddr,$server,$oldname,$one_to_one) = @_; + local($newhost, $newaddr, $newname, $um) = &parse2($oldaddr,$server,$oldname,$one_to_one); + if ($newaddr =~ m,^["/],) { + return (undef, $oldaddr, $newname) if $valid; + return (undef, $um, $newname); + } + return ($newhost, $newaddr, $newname); +} + +# returns ($new_smtp_server,$new_address,$new_name) +# given a responce from a SMTP server ($newaddr), the +# current host ($server), the old "name" and a flag that +# indicates if it is being called during the initial +# command line parsing ($parsing_args) +sub parse2 +{ + local($newaddr,$context_host,$old_name,$parsing_args) = @_; + local(@names) = $old_name; + local($urx) = "[-A-Za-z_.0-9+]+"; + local($unmangle); + + # + # first, separate out the address part. + # + + # + # [NAME] <ADDR [(NAME)]> + # [NAME] <[(NAME)] ADDR + # ADDR [(NAME)] + # (NAME) ADDR + # [(NAME)] <ADDR> + # + if ($newaddr =~ /^\<(.*)\>$/) { + print "<A:$1>\n" if $debug; + $newaddr = &trim($1); + print "na = $newaddr\n" if $debug; + } + if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) { + # address has a < > pair in it. + print "N:$1 <A:$2> N:$3\n" if $debug; + $newaddr = &trim($2); + unshift(@names, &trim($3,$1)); + print "na = $newaddr\n" if $debug; + } + if ($newaddr =~ /^([^\(\)]*)\(([^\(\)]*)\)([^\(\)]*)$/) { + # address has a ( ) pair in it. + print "A:$1 (N:$2) A:$3\n" if $debug; + unshift(@names,&trim($2)); + local($f,$l) = (&trim($1),&trim($3)); + if (($f && $l) || !($f || $l)) { + # address looks like: + # foo (bar) baz or (bar) + # not allowed! + print STDERR "Could not parse $newaddr\n" if $vw; + return(undef,$newaddr,&firstname(@names)); + } + $newaddr = $f if $f; + $newaddr = $l if $l; + print "newaddr now = $newaddr\n" if $debug; + } + # + # @foo:bar + # j%k@l + # a@b + # b!a + # a + # + $unmangle = $newaddr; + if ($newaddr =~ /^\@($urx)\:(.+)$/) { + print "(\@:)" if $debug; + # this is a bit of a cheat, but it seems necessary + return (&domainify($1,$context_host,$2),$2,&firstname(@names),$unmangle); + } + if ($newaddr =~ /^(.+)\@($urx)$/) { + print "(\@)" if $debug; + return (&domainify($2,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle); + } + if ($parsing_args) { + if ($newaddr =~ /^($urx)\!(.+)$/) { + return (&domainify($1,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle); + } + if ($newaddr =~ /^($urx)$/) { + return ($context_host,$newaddr,&firstname(@names),$unmangle); + } + print STDERR "Could not parse $newaddr\n"; + } + print "(?)" if $debug; + return(undef,$newaddr,&firstname(@names),$unmangle); +} +# return $u (@$server) unless $u includes reference to $server +sub compact +{ + local($u, $server) = @_; + local($se) = $server; + local($sp); + $se =~ s/(\W)/\\$1/g; + $sp = " (\@$server)"; + if ($u !~ /$se/i) { + return "$u$sp"; + } + return $u; +} +# remove empty (spaces don't count) members from an array +sub trim +{ + local(@v) = @_; + local($v,@r); + for $v (@v) { + $v =~ s/^\s+//; + $v =~ s/\s+$//; + push(@r,$v) if ($v =~ /\S/); + } + return(@r); +} +# using the host part of an address, and the server name, add the +# servers' domain to the address if it doesn't already have a +# domain. Since this sometimes failes, save a back reference so +# it can be unrolled. +sub domainify +{ + local($host,$domain_host,$u) = @_; + local($domain,$newhost); + + # cut of trailing dots + $host =~ s/\.$//; + $domain_host =~ s/\.$//; + + if ($domain_host !~ /\./) { + # + # domain host isn't, keep $host whatever it is + # + print "domainify($host,$domain_host) = $host\n" if $debug; + return $host; + } + + # + # There are several weird situtations that need to be + # accounted for. They have to do with domain relay hosts. + # + # Examples: + # host server "right answer" + # + # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu + # shiva cs.berkeley.edu shiva.cs.berekley.edu + # cumulus reed.edu @reed.edu:cumulus.uucp + # tiberius tc.cornell.edu tiberius.tc.cornell.edu + # + # The first try must always be to cut the domain part out of + # the server and tack it onto the host. + # + # A reasonable second try is to tack the whole server part onto + # the host and for each possible repeated element, eliminate + # just that part. + # + # These extra "guesses" get put into the %domainify_fallback + # array. They will be used to give addresses a second chance + # in the &giveup routine + # + + local(%fallback); + + local($long); + $long = "$host $domain_host"; + $long =~ tr/A-Z/a-z/; + print "long = $long\n" if $debug; + if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) { + # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu + print "condensed fallback $host $domain_host -> $long\n" if $debug; + $fallback{$long} = 9; + } + + local($fh); + $fh = $domain_host; + while ($fh =~ /\./) { + print "FALLBACK $host.$fh = 1\n" if $debug > 7; + $fallback{"$host.$fh"} = 1; + $fh =~ s/^[^\.]+\.//; + } + + $fallback{"$host.$domain_host"} = 2; + + ($domain = $domain_host) =~ s/^[^\.]+//; + $fallback{"$host$domain"} = 6 + if ($domain =~ /\./); + + if ($host =~ /\./) { + # + # Host is already okay, but let's look for multiple + # interpretations + # + print "domainify($host,$domain_host) = $host\n" if $debug; + delete $fallback{$host}; + $domainify_fallback{"$u *** $host"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback; + return $host; + } + + $domain = ".$domain_host" + if ($domain !~ /\..*\./); + $newhost = "$host$domain"; + + $create_host_backtrack{"$u *** $newhost"} = $domain_host; + print "domainify($host,$domain_host) = $newhost\n" if $debug; + delete $fallback{$newhost}; + $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback; + if ($debug) { + print "fallback = "; + print $domainify_fallback{"$u *** $newhost"} + if defined($domainify_fallback{"$u *** $newhost"}); + print "\n"; + } + return $newhost; +} +# return the first non-empty element of an array +sub firstname +{ + local(@names) = @_; + local($n); + while(@names) { + $n = shift(@names); + return $n if $n =~ /\S/; + } + return undef; +} +# queue up more addresses to expand +sub expn +{ + local($host,$addr,$name,$level) = @_; + if ($host) { + $host = &trhost($host); + + if (($debug > 3) || (defined $giveup{$host})) { + unshift(@hosts,$host) unless $users{$host}; + } else { + push(@hosts,$host) unless $users{$host}; + } + $users{$host} .= " $addr"; + $names{"$addr *** $host"} = $name; + $level{"$addr *** $host"} = $level + 1; + print "expn($host,$addr,$name)\n" if $debug; + return "\t$addr\n"; + } else { + return &final($addr,'NONE',$name); + } +} +# compute the numerical average value of an array +sub average +{ + local(@e) = @_; + return 0 unless @e; + local($e,$sum); + for $e (@e) { + $sum += $e; + } + $sum / @e; +} +# print to the server (also to stdout, if -w) +sub ps +{ + local($p) = @_; + print ">>> $p\n" if $watch; + print S "$p\n"; +} +# return case-adjusted name for a host (for comparison purposes) +sub trhost +{ + # treat foo.bar as an alias for Foo.BAR + local($host) = @_; + local($trhost) = $host; + $trhost =~ tr/A-Z/a-z/; + if ($trhost{$trhost}) { + $host = $trhost{$trhost}; + } else { + $trhost{$trhost} = $host; + } + $trhost{$trhost}; +} +# re-queue users if an mx record dictates a redirect +# don't allow a user to be redirected more than once +sub mxredirect +{ + local($server,*users) = @_; + local($u,$nserver,@still_there); + + $nserver = &mx($server); + + if (&trhost($nserver) ne &trhost($server)) { + $0 = "$av0 - mx redirect $server -> $nserver\n"; + for $u (@users) { + if (defined $mxbacktrace{"$u *** $nserver"}) { + push(@still_there,$u); + } else { + $mxbacktrace{"$u *** $nserver"} = $server; + print "mxbacktrace{$u *** $nserver} = $server\n" + if ($debug > 1); + &expn($nserver,$u,$names{"$u *** $server"}); + } + } + @users = @still_there; + if (! @users) { + return $nserver; + } else { + return undef; + } + } + return undef; +} +# follow mx records, return a hostname +# also follow temporary redirections comming from &domainify and +# &mxlookup +sub mx +{ + local($h,$u) = @_; + + for (;;) { + if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) { + $0 = "$av0 - mx expand $h"; + $h = $mx{&trhost($h)}; + return $h; + } + if ($u) { + if (defined $temporary_redirect{"$u *** $h"}) { + $0 = "$av0 - internal redirect $h"; + print "Temporary redirect taken $u *** $h -> " if $debug; + $h = $temporary_redirect{"$u *** $h"}; + print "$h\n" if $debug; + next; + } + $htr = &trhost($h); + if (defined $temporary_redirect{"$u *** $htr"}) { + $0 = "$av0 - internal redirect $h"; + print "temporary redirect taken $u *** $h -> " if $debug; + $h = $temporary_redirect{"$u *** $htr"}; + print "$h\n" if $debug; + next; + } + } + return $h; + } +} +# look up mx records with the name server. +# re-queue expansion requests if possible +# optionally give up on this host. +sub mxlookup +{ + local($lastchance,$server,$giveup,*users) = @_; + local(*T); + local(*NSLOOKUP); + local($nh, $pref,$cpref); + local($o0) = $0; + local($nserver); + local($name,$aliases,$type,$len,$thataddr); + local(%fallback); + + return 1 if &mxredirect($server,*users); + + if ((defined $mx{$server}) || (! $have_nslookup)) { + return 0 unless $lastchance; + &giveup('mx domainify',$giveup); + return 0; + } + + $0 = "$av0 - nslookup of $server"; + open(T,">/tmp/expn$$") || die "open > /tmp/expn$$: $!\n"; + print T "set querytype=MX\n"; + print T "$server\n"; + close(T); + $cpref = 1.0E12; + undef $nserver; + open(NSLOOKUP,"nslookup < /tmp/expn$$ 2>&1 |") || die "open nslookup: $!"; + while(<NSLOOKUP>) { + print if ($debug > 2); + if (/mail exchanger = ([-A-Za-z_.0-9+]+)/) { + $nh = $1; + if (/preference = (\d+)/) { + $pref = $1; + if ($pref < $cpref) { + $nserver = $nh; + $cpref = $pref; + } elsif ($pref) { + $fallback{$pref} .= " $nh"; + } + } + } + if (/Non-existent domain/) { + # + # These addresss are hosed. Kaput! Dead! + # However, if we created the address in the + # first place then there is a chance of + # salvation. + # + 1 while(<NSLOOKUP>); + close(NSLOOKUP); + return 0 unless $lastchance; + &giveup('domainify',"$server: Non-existent domain",undef,1); + return 0; + } + + } + close(NSLOOKUP); + unlink("/tmp/expn$$"); + unless ($nserver) { + $0 = "$o0 - finished mxlookup"; + return 0 unless $lastchance; + &giveup('mx domainify',"$server: Could not resolve address"); + return 0; + } + + # provide fallbacks in case $nserver doesn't work out + if (defined $fallback{$cpref}) { +# for $u (@users) { +# print "mx_secondary{$u *** $nserver} = ".$fallback{$cpref}."\n" +# if $debug; +# $mx_secondary{"$u *** $nserver"} = $fallback{$cpref}; +# } + $mx_secondary{$server} = $fallback{$cpref}; + } + + $0 = "$av0 - gethostbyname($nserver)"; + ($name,$aliases,$type,$len,$thataddr) = gethostbyname($nserver); + + unless ($thataddr) { + $0 = $o0; + return 0 unless $lastchance; + &giveup('mx domainify',"$nserver: could not resolve address"); + return 0; + } + print "MX($server) = $nserver\n" if $debug; + print "$server -> $nserver\n" if $vw && !$debug; + $mx{&trhost($server)} = $nserver; + # redeploy the users + unless (&mxredirect($server,*users)) { + return 0 unless $lastchance; + &giveup('mx domainify',"$nserver: only one level of mx redirect allowed"); + return 0; + } + $0 = "$o0 - finished mxlookup"; + return 1; +} +# if mx expansion did not help to resolve an address +# (ie: foo@bar became @baz:foo@bar, then undo the +# expansion). +# this is only used by &final +sub mxunroll +{ + local(*host,*addr) = @_; + local($r) = 0; + print "looking for mxbacktrace{$addr *** $host}\n" + if ($debug > 1); + while (defined $mxbacktrace{"$addr *** $host"}) { + print "Unrolling MX expnasion: \@$host:$addr -> " + if ($debug || $verbose); + $host = $mxbacktrace{"$addr *** $host"}; + print "\@$host:$addr\n" + if ($debug || $verbose); + $r = 1; + } + return 1 if $r; + $addr = "\@$host:$addr" + if ($host =~ /\./); + return 0; +} +# register a completed expnasion. Make the final address as +# simple as possible. +sub final +{ + local($addr,$host,$name,$error) = @_; + local($he); + local($hb,$hr); + local($au,$ah); + + if ($error =~ /Non-existent domain/) { + # + # If we created the domain, then let's undo the + # damage... + # + if (defined $create_host_backtrack{"$addr *** $host"}) { + while (defined $create_host_backtrack{"$addr *** $host"}) { + print "Un&domainifying($host) = " if $debug; + $host = $create_host_backtrack{"$addr *** $host"}; + print "$host\n" if $debug; + } + $error = "$host: could not locate"; + } else { + # + # If we only want valid addresses, toss out + # bad host names. + # + if ($valid) { + print STDERR "\@$host:$addr ($name) Non-existent domain\n"; + return ""; + } + } + } + + MXUNWIND: { + $0 = "$av0 - final parsing of \@$host:$addr"; + ($he = $host) =~ s/(\W)/\\$1/g; + if ($addr !~ /@/) { + # addr does not contain any host + $addr = "$addr@$host"; + } elsif ($addr !~ /$he/i) { + # if host part really something else, use the something + # else. + if ($addr =~ m/(.*)\@([^\@]+)$/) { + ($au,$ah) = ($1,$2); + print "au = $au ah = $ah\n" if $debug; + if (defined $temporary_redirect{"$addr *** $ah"}) { + $addr = "$au\@".$temporary_redirect{"$addr *** $ah"}; + print "Rewrite! to $addr\n" if $debug; + next MXUNWIND; + } + } + # addr does not contain full host + if ($valid) { + if ($host =~ /^([^\.]+)(\..+)$/) { + # host part has a . in it - foo.bar + ($hb, $hr) = ($1, $2); + if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) { + # addr part has not . + # and matches beginning of + # host part -- tack on a + # domain name. + $addr .= $hr; + } else { + &mxunroll(*host,*addr) + && redo MXUNWIND; + } + } else { + &mxunroll(*host,*addr) + && redo MXUNWIND; + } + } else { + $addr = "${addr}[\@$host]" + if ($host =~ /\./); + } + } + } + $name = "$name " if $name; + $error = " $error" if $error; + if ($valid) { + push(@final,"$name<$addr>"); + } else { + push(@final,"$name<$addr>$error"); + } + "\t$name<$addr>$error\n"; +} + +sub alarm +{ + local($alarm_action,$alarm_redirect,$alarm_user) = @_; + alarm(3600); + $SIG{ALRM} = 'handle_alarm'; +} +# this involves one GREAT hack. +# the "next HOST" has to unwind the stack! +sub handle_alarm +{ + &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user); + next HOST; +} + +# read the rest of the current smtp daemon's responce (and toss it away) +sub read_response +{ + local($done,$watch) = @_; + local(@resp); + print $s if $watch; + while(($done eq "-") && ($s = <S>) && ($s =~ /^\d+([- ])/)) { + print $s if $watch; + $done = $1; + push(@resp,$s); + } + return @resp; +} +# print args if verbose. Return them in any case +sub verbose +{ + local(@tp) = @_; + print "@tp" if $verbose; +} +# to pass perl -w: +@tp; +$flag_a; +$flag_d; +$flag_1; +%already_domainify_fellback; +%already_mx_fellback; +&handle_alarm; +################### BEGIN PERL/TROFF TRANSITION +.00; + +'di \\ " finish diversion--previous line must be blank +.nr nl 0-1 \\ " fake up transition to first page again +.nr % 0 \\ " start at page 1 +.\\"'; __END__ +.\" ############## END PERL/TROFF TRANSITION +.TH EXPN 1 "March 11, 1993" +.AT 3 +.SH NAME +expn \- recursively expand mail aliases +.SH SYNOPSIS +.B expn +.RI [ -a ] +.RI [ -v ] +.RI [ -w ] +.RI [ -d ] +.RI [ -1 ] +.IR user [@ hostname ] +.RI [ user [@ hostname ]]... +.SH DESCRIPTION +.B expn +will use the SMTP +.B expn +and +.B vrfy +commands to expand mail aliases. +It will first look up the addresses you provide on the command line. +If those expand into addresses on other systems, it will +connect to the other systems and expand again. It will keep +doing this until no further expansion is possible. +.SH OPTIONS +The default output of +.B expn +can contain many lines which are not valid +email addresses. With the +.I -aa +flag, only expansions that result in legal addresses +are used. Since many mailing lists have an illegal +address or two, the single +.IR -a , +address, flag specifies that a few illegal addresses can +be mixed into the results. More +.I -a +flags vary the ratio. Read the source to track down +the formula. With the +.I -a +option, you should be able to construct a new mailing +list out of an existing one. +.LP +If you wish to limit the number of levels deep that +.B expn +will recurse as it traces addresses, use the +.I -1 +option. For each +.I -1 +another level will be traversed. So, +.I -111 +will traverse no more than three levels deep. +.LP +The normal mode of operation for +.B expn +is to do all of its work silently. +The following options make it more verbose. +It is not necessary to make it verbose to see what it is +doing because as it works, it changes its +.BR argv [0] +variable to reflect its current activity. +To see how it is expanding things, the +.IR -v , +verbose, flag will cause +.B expn +to show each address before +and after translation as it works. +The +.IR -w , +watch, flag will cause +.B expn +to show you its conversations with the mail daemons. +Finally, the +.IR -d , +debug, flag will expose many of the inner workings so that +it is possible to eliminate bugs. +.SH ENVIRONMENT +No enviroment variables are used. +.SH FILES +.PD 0 +.B /tmp/expn$$ +.B temporary file used as input to +.BR nslookup . +.SH SEE ALSO +.BR aliases (5), +.BR sendmail (8), +.BR nslookup (8), +RFC 823, and RFC 1123. +.SH BUGS +Not all mail daemons will implement +.B expn +or +.BR vrfy . +It is not possible to verify addresses that are served +by such daemons. +.LP +When attempting to connect to a system to verify an address, +.B expn +only tries one IP address. Most mail daemons +will try harder. +.LP +It is assumed that you are running domain names and that +the +.BR nslookup (8) +program is available. If not, +.B expn +will not be able to verify many addresses. It will also pause +for a long time unless you change the code where it says +.I $have_nslookup = 1 +to read +.I $have_nslookup = +.IR 0 . +.LP +Lastly, +.B expn +does not handle every valid address. If you have an example, +please submit a bug report. +.SH CREDITS +In 1986 or so, Jon Broome wrote a program of the same name +that did about the same thing. It has since suffered bit rot +and Jon Broome has dropped off the face of the earth! +(Jon, if you are out there, drop me a line) +.SH AVAILABILITY +The latest version of +.B expn +is available through anonymous ftp to +.IR idiom.berkeley.ca.us . +.SH AUTHOR +.I David Muir Sharnoff\ \ \ \ <muir@idiom.berkeley.ca.us> diff --git a/usr.sbin/sendmail/contrib/mail.local.linux b/usr.sbin/sendmail/contrib/mail.local.linux new file mode 100644 index 0000000..42d2c3c --- /dev/null +++ b/usr.sbin/sendmail/contrib/mail.local.linux @@ -0,0 +1,205 @@ +From: Karl London <karl@borg.demon.co.uk> +Message-Id: <199308111712.SAA05454@borg.demon.co.uk> +Subject: Final port of mail.local to Linux +To: eric@cs.berkeley.edu +Date: Wed, 11 Aug 1993 18:12:27 +0100 (BST) +X-Mailer: ELM [version 2.4 PL21] +MIME-Version: 1.0 +Content-Type: text/plain; charset=US-ASCII +Content-Transfer-Encoding: 7bit +Content-Length: 11415 + +Hi, + Sorry about this.. This is a final version of mail.local for +linux.. + +This is what I would like to see distributed with 8.6 if poss... + +Karl + +-------------- + +begin 600 mail.local.linux.tar.Z +M'YV0;<*D8>."S9LQ8=B\`,"PH<.'$"-*G$BQHL6*(&C`N`$#!@@`($#$N%'# +M(TB1)$V&7,D2A$<0-6C,D$%3QHT8+V/HI$$#9(V+0(,*'4HT8ITY=,+("0E@ +MC5(V12<ZE0,UJM6K6+-JW<KU:L"!!0\F?`$G#!TT;L*T*3/'!9JN<*WBA*%1 +M)<J2'T..Q'NRY0P;,6:$+`DC!@V:,VK(T#MCQDV0,.)*GFP4J5*F4ZMNS4RY +ML^?/H#^_4-%"`0@5((:\@9-'3IHS:.B`0#$FA<@<.3Q205,&A)0R9\JXH3,' +MQ!LS(,[VKN(FC9TR<N:DH9/'./(A"=.8>2.G>1@7IE$'8<,&A&O8Q,VSA?Z< +M#/C3X7V7(9,&J6LQ=>BD>>,&1!@W9(!P5&]I]#?'&W7(,49O_P4H1H%*5;>= +M'&W,P0((=TR'AG%+97@6@G3$U\8;])F1!D+Z\7>A4KW!`5T;T]$Q'PAPR/&& +M'6F0,>-99B7'&PC;D?=&AFZ<`<(8_-&7HAMSQ,<B"&O1H4-\,;@@'WWVI8'? +MDL4=!\*!"2YX)(F]M7&4;'*4D52!/C(HQHV](<G:>;'%MY(;;^BWX(5GU0<" +M&_7)YB62`$ZW'Y/^`=@FD&\(2:216([!AD!KR?$>:C)8^1N6=-R7WZ'%L?E@ +M6G)(R%T;4)ZI7HTDUB&F<OZ]^=R8<[Y6YVDLX:EG&7RBX2>@2%DW9J%<)AH@ +MK$$:]"@(D4Z:1J4@L*F<G2&1<5`=:PUGUJ'&OL"=<<HM%9",KB54'*LXZAB@ +MAQO"RJFG2UX*P@Q6CE=>&&0\)X=^TA4)I5G0I6$NE,(M6:"19I1A5H)L<2C@ +M'+UYV6=Q!YI!QQTL4IMJL%C",6EUR#:J[,'^C;$&GG>P,5]PV4H97@*[^<DJ +M&:[*5J"D=>A(\7$7/ZGC<P:YZ"#(/S+G''3246?=R]@!.J%W%PH!W1IEJ%Q= +M@]&F1VBG6N;'75OQT6"E$V5H"-VB::TE+*Q&ZYMT=7@N!6O:#7LY77%;P_NU +MQ@%5)T9O`Q[[!@C"61M=Q$NQ.J*,--I(\QCIZ>A:>T#:B.K$7_*,<9H:LPOB +MERZ.H=V)C>_7H6MTR-B?BQ36)QU_E\9'!1))3`'"%$\80<4504A1!`BU@P"% +M%$]8D00111`!@A!9@##[[[X?48035-@>A!/*#_$$]5(D(4055#PAA>U@@!&$ +M[;6?<`((UQ,1W_7-%X'%\$5,8;OXP#<!!1-))`\"[U*0PO6HT+\I7"@)3A@" +M$ZI`!`0>(6K@`X$3GD`%$/"O"4F@@O_"=Z'GQ2<)^N.?_P`H0.H5$`2Y`T$3 +MBB"%(2!A@$$00A+X1X7FM0\$1LB@$^IG.R/@+PC"ZQT!AU`%)O1.>%60`A2> +M,(4BO,]W(&C@%!08!!`FSTK`<X($GP""(EAA>A6<P@N9P`3G(0%Z19`>]>XG +MA=1LCPK=^U[XQK>\W_$OADQP(JY\V$;X13$)OAL"%0Z(/4`609"$',+QP!@$ +M)EQH"E`X9!(:>2'Y%2&$O<O"A?"G/2<T,0I5`.,DF1`?(@2A"4&0GNU0@$`% +M,M"!$*S@!"MXP0QN\`D7&MX3B.B[%5(/A4:X716$,`4"4@%\OSO"$YY`A"G$ +M!W]-E(+QAE"_'5B0B?<+9A6:>"%34B$(FVRC+G-8/6OB[WM30"`/LZC!`%8! +M"@3<7@KB@X0G7,&++$Q-$+;IOQMNCWU.:-[SQ->\%-Y1AC34Y/_.^+PV(M"- +MW`L"(F\'QR0(,CX'G6$&%8J_.5:0E0E<8`.=<`0)II%_:J0F"MM(P3-*X0JU +M*X)M>E<[!V81H,WC71:>&<$4/@\$_`2F&8.'.]T!L`B5_*(6DQ#,(!#!>$U4 +MGD_/*#QL>D^C-01F?*90!1=&\92I+$+L3I,`(*!@!"DHRUGHUA8T)*`&+A`, +M"J0F!ZI9S38Q>,%(7I"#&(3G!0H8P<W8D+/>\$"M:&BK#Q006!V9R`V]^0(4 +M@C"[+S!AEU307P)$\`(ZM`$.+S`(0@B"A=*:5@0*"(UJ5_L5@HAV+*T-RVA= +M,(;5@F8N=<G+7>RR%[NP9"0PJ$$-!B.3FMBD(XSAR0T@8]OF#N5,E_D(9[@R +M7>=:][K8E<QH2H,KU=0*/;.IS6URX[P?_28XP^D2<MC6'+=-ISI>:IIVN.,= +M>=G+/+9*3YH@)H?VC!4UFPH4O+B%M0%%RT`(4A"#%#6J"#&*0A;"D(8<YKG\ +MB(A$HT/1H5:4)AJ]*$8S0E>.=H2&'H7,420CE))`Y:0.1VE*N*K2E03<M6)Y +M"4P*'I..-H8F-0FD/[`*@ZSBM)K6Y%=CNCH1KWSTJT`)2\6&XD]QL';BD?FK +M692"CKPR->,L;0E4!P9!@TOU8%29*5AIFIFK>A/D(=/*R.A!<IZ4W*LF!VM0 +M28HRHJC\HV0-B618?M;9I,4;C5EK#-@JV+;XTZUOY8DWX@)8N=AP+ANE:T;L +M6M2[:GPH>=&+?>3Q3[Z@PR^2C2M@`VO9H4B6L(7MUV$&EIBO=F:QS96!;ZKJ +MV,<6Y>=EA<%D*%,9&5A6,!BK`&:S;ERK('=@G.DL<[7V61F`MIH9B8%HRVDO +MTMZ[-++*]VD"B]K4JE:&JRGJ;L/BVI:^)B^Q2;!LX4*;6B*VWJ)I.SK<CIN\ +MUZ+>K.&-/^KV6G3X%@:_`0YB@B,<@+Z&.&4OKD6.JUEQ)'>T`)G!<DRF=<\X +MAZN55%@V<PA=ADOWK3N@3G4>;MT<7N>&_P[5=D7=7>]^%SQ=&@]YRF.>&=&H +MQNH!-'MOC"/XQ$<^\Z%O"NK[^?L"VL7Y^<Y^*\W?_OJG/!(.L(")%"DLEQ?! +M65H0A+94'@=W_L$04OU_O2LA`>LGU!6V\(74BR%6;8@]'.IPG7QD7Q"E,,0B +M'A$*25QB$Y_X.RE2T8I$P.)#)]A%I89QC&7\:?3`R$:(5E2.1*_CU_&H1]3D +MW8\-#.0@LQCZ0XZ^E8N,NR-O%TE%4K+IE]Q?)L,)T4^&TH2-+"5853F;5FJ= +MI+'<(BW!KD&QXU)XQ./E)<$H5*X2TYC(!($RF>E,7$&3A=.LYC6AGD)^=I.R +MX(SZ.#,X!7.V$9WJA#H"VRF%=\;3"?/$53WO^<4V#F&?4?TY"K7HQX%*H:#! +ME%$)=2%7P%`N=5.=!$<2-7K%U#T7A2L"N%&TYU&]%U*O1%(F=00H-3TJA3\M +MQ4(PU40SU3WIA($/Y4<ZQ5,5-%6_$U13152Y(W.^DU33`SQ-]50Q)57!]%." +METX(M5%:A2M<Y56FA$K2<RF`%5C:`2!E@!R`,AP*,`8EMA1R`F>QL05=``(] +MH``B8%9HE1I%1B>R01MXA1NZ85[`43#]QEY'@V]*$U_9`6[?P05NP`6F<5]B +M6!S[Q1[SX0)T*`([$%B%HQT@,!H@H"M_4B"RH0))*%AFP(1.J(@*8!EZ<B13 +M^"5C,`;20098J(4@T(5GE0*Q]5H$,09OY0(V,!MT95?E9ALVH%<YP%<Q`(B" +M6"C(88B(^(2+V(B#55@@P`-SD`=S0!9*H19NL5B"Y0;.9EC!.(R4>(Q*J(R$ +MM6/`*(PO<"`FHR;0F(S+^(O-^`(FHC+;V(O4"%GZ88XO4"#C*(V^6(US8!!G +ML([=R`-F,`;#01!H@(SD:%CF2`9B((_3:%AP<`?ND8_1.(_ZL18`V8YUT!Q( +M49#ZR([4"!W=\08+28T/N1\7R8QT0`:`\H\&R8T!Z8U<4R0;^8F(Q59N@5J- +MU80%4@8)8`3@QP0)D`!^-0*.]9(),$LR^4TT"0.,I8@)H".`HB\@\`62A0(H +MH(@7(H67H0(ID`*!:`=OD",)0)%'F91+.1Q-*66RX91+H0(7X@)D&953695D +MD`!)9@9Y\"!F@!Q("05*"9:G898*()1(P1V1I95T"952J0!4:95'$0;!D95R +MB0*!209VJ0!?X08HH!1G,`8<=@9V,$\)H(C^(0>1&8@)T)<J`)EV$(A[H`!7 +MB0?DTA^8N1KG2`:<60:F"1W]T9>J"9F<:1\U0R-AL'($>1H#R9F8*8478@9D +M<"'3EA"<60<Y\@6R@9RL29I]>7%OT`:!2)K5Y@;PB`(B,(IBP08B<"&7=022 +MQ4+$(P7>^03@B4HS])>D"9VHT@,25$1,P)EWX"LJ,QNTL2'N&1QTH)J/J9F2 +MF9F4>2$B0`9FH`-RH`,B$)4@$`+N602Y8YD),`<>(H7W:1NC69,(`3$@<`)D +M<`(Z4).&*`:_M@:;$R!(\EG;(@;UR8BD69-BD"9AL`:<V9FYV1LG8`8>VJ(9 +M:J-RX*$@BAHB:C(E.B8HJA\JVALL6I.7B1PHP)X0F@`L09$HT).OEYUUP`;Z +MX3&]T0+(H9J@DJ`SF@#LZ8FSJ9EA^J(*(Z,Z6J,;^@,Y6I..%097ZC)*.IC! +M@0)_69-]0)J0.08@T`+NZ:4`PIF@"0(K$*APL)K3N:2S$0*?J9F5V:)V6@9X +MNJBC09JXD@3(,::(&')E(#HF,A\78F!S,V^5$YT@H)_P6"!X&BWU=FM0BAK, +M^2]T((5L<2&/!AT9`C&CJJ&EJC9C"BMEH9O<87$#`:NX@B1RL%]PD&?^LI^+ +MPIS@$:N`E0"SFI]JPIR52IJ$B`(A,*8F8`*-VJ08AZUTL*J.J:!\P`>8NA(A +M@`(#Z8GZ.9!I0Z[1J:Y\0"-WT`(^,)!?,*L,*B`Y$I4M.J;N":]W(*]J,I#: +MRIP*^@/ZRJ_^2C<@H`.?^`,82XOK&2#NF9=I8J]MD*<3,AO%61[N"0/6]*B4 +M:4TKL`*@":$E"P)\X)Y$V89-.IRG\;*MB0?3@0(E^Y=[.HG[^;%.ZIR7J`+L +M*9JDF4-Y=!IF``><F9!EH)S)80?&R:W#`20X6S4OR9E]20=N<"%/6`9;(`-T +M@0-=L*A@ZXGV03-P@`*215E(8%F8I3]YVJTWZXEML`9(40:?A0)@JZ`]X)XM +M$`,RFZ_O^K2>*)S5>;,">@<KD*`$6Y-22J6K)P(-*63U":W5F1Q^VZQRX&#A +M6`9@2IJ(B98IT)!/N`:`ZP8BFR:4&KB+>KH#*[4H8`)T8+5LD*>TJYA/ZQK# +M809-"@<":@085P+%@;S=>:IMT)2VB[NZNY@),+9;``-9Z)XGP`4P<`*<.;(^ +M.[:>&`/69`;Z.0<H,+87(AUZ4`;'<;XOF0+IVY$%(I46VJ+=2KW6JX782X<G +M\*1<"UGA.Z-5HZ$76I/=^K^]$:Z)"%G5>[W8:P3KH\`A(`9C\+?H^XG&BZK+ +M6P.3JZ2]FU;Y,08H<`(^<`+!"0=Y2KG@>[(SNJ<U^<&_JXC"^[0"JKQB^[XS +MVJT)LZS<,;P=G`!H&J.<&;0)8(B:2C`K1YB]@8B0E3(OZ;FMDQ8R@K.0-2/_ +M03BF&;I6DJ3=&@(%HB-XX+Z0=2'9ZP;]"Z$?#`<A/,+\>\)Y:HA/D!]J+!MA +M<(AEX,2094VZLF2?JS3\-@=*/&4=EA#*TH>G4:UIO,9E;,)`@L*SFY@I\):$ +M-0=HX,.^R:0[;"-RX,,PNZQ3.I,"*B.?Q1VB>ZP8@CJ]09'<4;H)D"9TD"". +M*9Q`RU@UJR^.>XCS9IF8*9Q>>[1TH[01VBFW28E?(@:U*<S,-JP2&B`JT)M8 +M^Y4P.L47T@9B()P7X@9R<,UW@*MO>2']=;6=>8EN:085<@9;,!==<"'X809; +M@`,J@!,R0`/IC)MGL06H-#]RFT=.D+;4^994BV@VTLU_`C%EP+KJ6<0J@*FH +M003U0<A#PBQ<JR\@,S@->3)#TA]L]:<M`#J?*C#ET5IO@@<-@Q#]\3<*#0(E +M!B#"%F:P`C$`TEK^`2@U6ARC.ZV'S*U,^J[Q:J[TJA8H0#=16;^4Z\D\"<J? +M6-$H@]'S9K$VC,MKD<*N#,LH$`-Y2L0?''+`2P?"BU@U/(Q-';>5A9Y,$'K7 +MG,N+VL6T$<TSXIZ4EA1T`*]F@0878@)S(`8*&J[M&A(H4-<N@!1?8)T%L@8+ +MZIZ&NZZW\P6UPP1.L`1[_8]^/2(Z$M0@4,!26M0^V=46N[HS,KK+BUA0G:W= +M,=55S5@(?=*[`1UE<`)39AZ_UAN0MJ4;?3&#PRH+LG(-TR=N(-C*X3>/=B1J +M308GC2S'BD4Q4QQK\1_%(=SU2=*(^#<"`MBYW8<*7:UX2\W"J85Y;1PNXIA< +MC4)?$`10$$G8PP=/\`5#X#N41=Y?<`7$XP1,H%!3@-CM-P52P`?QG017L$U2 +MH*`\X!)"?95$34&6B]D@T-3=;1^JO,D4B2=V69-1'=I4/<2D_<TFZQ*<:8BF +M9;%/L-U?@@8(P@8!XM`):P:BM08G0#+*70;61-`9X0(T0$R)=].,VJ0EC@+6 +MC;.7-01+\`7RHZ"4'>!4,."?B+Q,/0>='=?QFR8\K.#+RN!0K;L!W*)GD"># +M(Z+-F0!$#-#'@1QL3="L>^,7`@.KUT1%L./30P2\"\E8+<,H,,[EW-5`4`(@ +M_H=EO19-F2!;?K=,2FEE4-"W+.:/5`1E_@5-1`6V,;BSL>7*F0*%^]^5:]0B +M(,J@6\HJ4^3+B^!+[K--_@8-WLI03M@S.N70:N423IKS:<I*B<V>"*-D<,OK +MG+YIL+[MN\Z2[0/^7;#?@@);[HDH:QW(T=_8K,=+`:B'N,V^;JCNZ09W\*1X +MJ^R>:'+30:E@+F9U@!PKX.O7+`?\[=^3W:)#O<F67:5$7N!&?B$'WBD)KND5 +MV>DU*>J#(\S*&*8NW*VJWM\PX.B>+.21_KFD3&:C:^E)GNX+SNE_">]C\*$) +M`,,&#S`V7LTXJ^5O^>0)$>583MJ7&JNWHR:+<M%G@S5G8"-U``=8A`2!PATG +M0LAYH,X%`M-IIC!3G''^$2(8KS!2^"\#`=%%"1UY@$57L,2#8Q!%<C;6DG$K +MLG+8@N(E9C.O>M(<'QV^`@<.PSHPLG)@=@>H;8G_$1S'\MH8DIO^H1ZYR6@C +M:RT'<]('8M,L:N4?VJUC\-NVT:(P+(47W?#6'+']>@?_FB/FOJ]W_P5G,+"/ +MC+IF$(S*2/>*N0,_&D7\<0*R@3'3\6`"`@=D`#!;C,B0+"EO`#&&+Y6)GP2? +M!2BBLYS6>1!K4/FDW<44#J%KV9;:(;QN/@=GD*</[IBI'XA!FY@*L/KC;..P +M;YE]6<[`3(FD8YO,AHU.009D(`=?P";XDOS'O"U^NLN>6+C'+`>WZ>%]F[4J +M@`902YK$#W)\J/TAYYM9JS)N<-8YW?R6TB]?8`9J,1!Y\-^&*#5?<JQ92XCC +M_"7LH62'F"?^8;4#07.5`=-GP'+:7H-ZYHJ_V(%K0S>PTSA;7IB+#,`!R?6D +M9E\.,X#<3V%]"*3``.<-=B)%V(\.4,!NIZ0JFX"#="`P\TD)<G?IT%VF&WCL +MS@*V*!>6`-1?7RL0[>_]L8'JX)ZX'[]"`U]`_5$'%S&C*%B100$^T`<`0?4W +MUVP@^V."*`T._,`O8/[.P%E(84X0!X(NV="QI*`/F`-?8`M*N!GWZUP""%!@ +M38IMD3XU@0*"@!%`;#N$`>V2'4<$CH``:0)A3G`1+JI&`@$<N#N!E^T3&3\U +M`?"^A`O49.K.R<TH&5CQII=P8%N=POSQOMAGJ$3")9L-+HT,[*<F-<U@G]@2 +M#F'N0NPU9`9R2)_ZJTLF``JJ+_8EO-3?Y&()`<O\=;(_&.0@728<=:VO$&(Z +M1`@#9QE@0DL*8%+A*05PH1Y=(,1<@"PX6"SM-%I`P!;@4LPK"PV(I4"67$#I +M"EJ.*"L5$R(P!)#201I)P*@C0:9M-,!NC4AJ1U8K=&DFQ5*+2@1CP7W9$"EM +MPV[X!12`E,),]<'])04VT)68Q%<Z6N2,#HRELB2(^)P]]&3YT"PDA.#4!NB` +M+KM_<T`?@K/GU!`YD]7Z`F1`4K1#[4`,2=-%!!9TK/O%0T)'!;BA-TP`%]$R +M[(O'1+R`1$/\2R.@''K$,/`%5.);"P..;".:`8_8C*[3=^)Q`80A"D3_D!-1 +M(DTL'"Q1S\V&A+@/81;/>FL13@'LJ>Q"%:NB5;R*6#$K:L6M*!&8X5AH`C'* +M):D,KJ@5<`MR.0F]1;>DQ;[0$F2`3B`N,Z$F<(2<(`-L@&)@+F2QN4"7I2!= +MGD)7J"YY,3`*1LDP`LI**`*+5&-TG2*YLHK(3?P3"7KE!L@BQJ)+CD`/2`!> +MD0TH`%3B!'#`9<R,+@`HR9`)<@6<P&6T$7E"`8S&)L!,BD`/H`'"I0:H1@02 +M>GK`"Q@5C,4%[*-?)`;F@'M@%?%H;RV6P4@<BZ-Q/(Z",3.^`-"(`Y!C13"+ +MO"4EJ$7IR!97@@VP`3>`!@R&CF`#8*,,4`RI2"0TAL?@$IPC9=B+F,$O4A?U +M:![;HWMD"'Y(!("A[Q(;PDL9(B^G33Z@E_0@:[)-&WH;PN+;T!>!`1[B(Z@I +M#WE(/2A`Z18?%8"!##!>YE,PF@*CH=@$CA$36&/,F`H(<R$R3<G1$"#"07(! +M^0C9,LRB"5N9J45\F-018BS-B-EZ)J;/B(P_XR^@#)<0D?+Q2;P8'"D2-,5\ +MH#%?1LH(BPM)9';,F>DQ:P+(_`@A`R?>C!CBD7=BSNP)F`<2GTR>*19\IC?T +M&D!3'R1%EK$4/)++0,B`4RQ$!83P=Z>"QZR*B/,JEJ2;J4)/TD!&R5U19XI# +ME<0SQ`+,9$E&@6*N3)=T%M""T-P:.5DMKD7+.)&-9BGDJDA#+CQ:I;D1+U+" +MG`5-`R0E9,OAD9_FON`+?5%J_,6IF33&K6"L&G_1:E[9J_D6L:;>^(F*L7$& +MY8AD"4>26=0'CU%P>,V,]#7`9D@(&V(S'*9$?$0V,H--V@R)U#!8I:V!:-1& +M:(@9;`-4[@V`/`XX,@$(R.X0;I;'N+$:QL+?I!N]$1T*Y*MT-V3#;,B-'T&Q +M^F.S_(_YYEO\JKJ!'-!-WJ@Q>X-0_@N#\S!VQ.`H'`PGZEF./`%QEDWD"!B4 +M(U@E&V.9,>3EQ^%HH(ITU`C3<<I`S.KX,%0/=N!('CD[8)!1F3G`PW;8G$62 +M<P0*59D\:T3_)""A,T>*SOG(F$FG??!(/R(_Z`?W<2AF9X2DG:M3/[+.!7H@ +M7$>64)"O@T&*C_,X/A[$0(*0J2,S`PC-S"8JA(6X$!CR@[+*#2$G.P3J?)Z] +MTW>,B#@)/$S$B1A(C&EXC`CB43Q:A/'@DU\B1AI)Y/F8:83R1)V1Z3V&#AT1 +M`G9DD@B!/,(C/P_3*3T3A38:DKR90%)/((0DDN3U6!),\G]H3R>Q/:(D]QC( +M(A165HGON9G!Q^O4$I\Y=G2)\O$E*RB8.)]BDD&BS_1I)CSR^D@3BZ)]+@OW +MT2;<Y*M\$]HS?LI)U$$_41-]4`_QY'Z2@#SAD?,'G]@?_--/ZLX_Z3]GA*`( +ME0A40PB0`<HG#V5D+J!'4E$NBH$DGAREC5`@D.)*1DI)V2$:*`FDE-_A@1I* +M")(I[(,$V903Q'12D(%\`CUE!U$5%Z0^+:8,0BJ-IP8Q%?:!@_(/"ZHJ]N.J +M)!2M8B"'$!+X*D9(K$S,7^F%1)%`<"W;R07@@`1@`Q2H*KJ6K2@CO`";,!D; +MI`L@`@$B""S,\F`3+D0,P`U^Y8+*!K'F`G)<(P$!.,!!/H$FX0*FP(9P`J=$ +M:SH!,W-`94M"<)!.($!XK#)Q0,-,'0-I;P`/.$@7>CNRP`2!`NG$F;B`&6KS +M$.AH4:%0SPB4!^00!)8">W*05;1>4D.R-$0W!/*8(MT#GMA.)X!#4058!`ND +M2`&PNN3V(RP#@%`*`:)`S+'(EQP&QQ4K'"W@.'`I4X8U<.)V(P.A0C9`*YJ' +M!K#H4IB&&XJ%0H$?>D`50$U3`*?-D&I1QJ0JG%L=TUTY0HLZM0'H(*$`'("D +M/\)+"<DGX?5ZS1SPE4*@/+2`I&`D6D"&T(0;`@_(TBOJ`I*`;)"B0()]6%'+ +MH0"F@,AA2XLB$T('U;9)"V01T(PNP),J@"10*-)%'9AX,.V/!3*6]B.`*!Y( +MD3CO642[H9$H",=GH0X*`'SU&FMS-198;_@;?\\--`=_D6E:6DDR$N4C@Z$* +M]0$>@`CXVAH_!NGUABSA+^(IQE$?O6*?%HYA6APH5A]-#H+&$*J%3QHLR\`? +MM9>$X]=LB%J&V@)$-&4+R9"3`A$5]1\$&_CR$WYTH"8<0HK$,FH]355J30ZL +MTI2&]4(7Y#@;**!\E+#^%2TJC4,5J74T452'CZHDRP1&56(*X-35/"1QI1Q$ +MF0@48;$_C*PZYD]15=*I63""7)C3`JE,\R-,&UV"+)7A`9PA'8#&3EV#ZV+" +M.$@L8$5+'`B0`4&5/O'0FP=2X<!#=0^=]),>,5A1/L99TE&`_,]/S#YR.E:7 +M0OEB#QU0;0@&%J``8$7^6Y!+P4^LI30P([P$14T3%K6&QE50.BB9:&8,AOH! +M4"@-UX1N/`*CF0.NHK9%&*SA`SP"(;JC2\XX9*($`39:Z(:8'L:CO6%.!YE* +M_Q0K_5.O=%)^L8+A(&UI%[$#SD,+*(`J@'!R*L00I$O2K=J(A0DP$*K:T`/\ +M(6+T5.?*%I+"9S&FR)2(,JWZ05M5Z6UUI3EB4G:6SQ):$JAI,2U_ZD25!<C1 +M6V7#(BU$G@6TD"(7<%Y+2V'E=^I00APK%NI;W^L+2(?+\8"^@&D8#/D+,;6F +MLC)-0`[ND`<<Y#'MHK=#T+$/)H`[^FH3%0D@@+#2UZ6`!S(AAM6P9!5(F-7% +M`&+]JII0@(&U-PQ6"WNB`)ELP`$9UL)F0IB60ETK"*`=Q82@*``@`EI9QW%= +M&DN4AJ+1[:0`D&"#>$)&`L&R.I+A]:Q>J/%Z,.VQQC\%$%)91!QU`UA4-MB! +M&P`"P(/7$U8VXN.IA3)Z8<'#>SRS:#;-JMDURV;;K)M]LW`VSLK9.4MGZZR= +HO;-X-L_JV3W+9_NLG_VS@#;0"MI!2V@+K:$]M(@VT2K:1<MH&^UD`$MG +` +end diff --git a/usr.sbin/sendmail/contrib/mh.patch b/usr.sbin/sendmail/contrib/mh.patch new file mode 100644 index 0000000..7b23a5b --- /dev/null +++ b/usr.sbin/sendmail/contrib/mh.patch @@ -0,0 +1,193 @@ +Message-Id: <199309031900.OAA19417@ignatz.acs.depaul.edu> +To: bug-mh@ics.uci.edu +cc: mh-users@ics.uci.edu, eric@cs.berkeley.edu +Subject: MH-6.8.1/Sendmail 8.X (MH patch) updated +Date: Fri, 03 Sep 1993 14:00:46 -0500 +From: Dave Nelson <dcn@ignatz.acs.depaul.edu> + + + This patch will fix the "X-auth..." warnings from the newer +Sendmails (8.X) while continuing to work with the old sendmails. + + I think the following patch will make everyone happy. + + 1) Anybody with MH-6.8.1 can install this. It doesn't matter + what version of sendmail you're running. It doesn't matter + if you're not running sendmail (but it won't fix anything + for you). + + 2) No configuration file hacks. If the -client switch is + absent (the default), the new sendmails will get an EHLO + using what LocalName() returns as the hostname. On my systems, + this returns the FQDN. If the EHLO fails with a result between + 500 and 599 and the -client switch is not set, we give up on + sending EHLO/HELO and just go deliver the mail. + + 3) No new configuration options. + + 4) Retains the undocumented -client switch. One warning: it + is possible using the -client switch to cause the old sendmails + to return "I refuse to talk to myself". You could do this under + the old code as well. This will happen if you claim to be the + same system as the sendmail you're sending to is running on. + That's pointless, but possible. If you do this, just like under + the old code, you will get an error. + + 5) If you're running a site with both old and new sendmails, you only + have to build MH once. The code's the same; works with them + both. + + If you decide to install this, make sure that you look the patch +over and that you agree with what it is doing. It works for me, but I +can't test it on every possible combination. Make sure that it works +before you really install it for your users, if any. No promises. + + To install this, save this to a file in the mts/sendmail directory. +Feed it to patch. Patch will ignore the non-patch stuff. You should have +"mts sendmail/smtp" in your configuration file. This works with old and +new sendmails. Using "mts sendmail" will cause the new sendmails to +print an "X-auth..." warning about who owns the process piping the mail +message. I don't know of anyway of getting rid of these. + + mh-config (if necessary), make, make inst-all. + + +I hope this helps people. + +/dcn + +Dave Nelson +Academic Computer Services +DePaul University, Chicago + +*** smail.c Fri Sep 3 11:58:05 1993 +--- smail.c Fri Sep 3 11:57:27 1993 +*************** +*** 239,261 **** + return RP_RPLY; + } + +! if (client && *client) { +! doingEHLO = 1; +! result = smtalk (SM_HELO, "EHLO %s", client); +! doingEHLO = 0; + +! if (500 <= result && result <= 599) + result = smtalk (SM_HELO, "HELO %s", client); +! +! switch (result) { + case 250: +! break; + + default: + (void) sm_end (NOTOK); + return RP_RPLY; + } + } + + #ifndef ZMAILER + if (onex) +--- 239,276 ---- + return RP_RPLY; + } + +! doingEHLO = 1; +! result = smtalk (SM_HELO, "EHLO %s", +! (client && *client) ? client : LocalName()); +! doingEHLO = 0; +! +! switch (result) +! { +! case 250: +! break; + +! default: +! if (!(500 <= result && result <= 599)) +! { +! (void) sm_end (NOTOK); +! return RP_RPLY; +! } +! +! if (client && *client) +! { + result = smtalk (SM_HELO, "HELO %s", client); +! switch (result) +! { + case 250: +! break; + + default: + (void) sm_end (NOTOK); + return RP_RPLY; ++ } + } + } ++ + + #ifndef ZMAILER + if (onex) +*************** +*** 357,380 **** + return RP_RPLY; + } + +! if (client && *client) { +! doingEHLO = 1; +! result = smtalk (SM_HELO, "EHLO %s", client); +! doingEHLO = 0; + +! if (500 <= result && result <= 599) + result = smtalk (SM_HELO, "HELO %s", client); +! +! switch (result) { +! case 250: + break; + +! default: + (void) sm_end (NOTOK); + return RP_RPLY; + } + } +! + send_options: ; + if (watch && EHLOset ("XVRB")) + (void) smtalk (SM_HELO, "VERB on"); +--- 372,409 ---- + return RP_RPLY; + } + +! doingEHLO = 1; +! result = smtalk (SM_HELO, "EHLO %s", +! (client && *client) ? client : LocalName()); +! doingEHLO = 0; +! +! switch (result) +! { +! case 250: +! break; +! +! default: +! if (!(500 <= result && result <= 599)) +! { +! (void) sm_end (NOTOK); +! return RP_RPLY; +! } + +! if (client && *client) +! { + result = smtalk (SM_HELO, "HELO %s", client); +! switch (result) +! { +! case 250: + break; + +! default: + (void) sm_end (NOTOK); + return RP_RPLY; ++ } + } + } +! + send_options: ; + if (watch && EHLOset ("XVRB")) + (void) smtalk (SM_HELO, "VERB on"); diff --git a/usr.sbin/sendmail/contrib/mmuegel b/usr.sbin/sendmail/contrib/mmuegel new file mode 100644 index 0000000..6db4a45 --- /dev/null +++ b/usr.sbin/sendmail/contrib/mmuegel @@ -0,0 +1,2079 @@ +From: "Michael S. Muegel" <mmuegel@cssun6.corp.mot.com> +Message-Id: <199307280818.AA08111@cssun6.corp.mot.com> +Subject: Re: contributed software +To: eric@cs.berkeley.edu (Eric Allman) +Date: Wed, 28 Jul 1993 03:18:02 -0500 (CDT) +In-Reply-To: <199307221853.LAA04266@mastodon.CS.Berkeley.EDU> from "Eric Allman" at Jul 22, 93 11:53:47 am +X-Mailer: ELM [version 2.4 PL22] +Mime-Version: 1.0 +Content-Type: text/plain; charset=US-ASCII +Content-Transfer-Encoding: 7bit +Content-Length: 69132 + +OK. Here is a new shell archive. + +Cheers, +-Mike + +---- Cut Here and feed the following to sh ---- +#!/bin/sh +# This is a shell archive (produced by shar 3.49) +# To extract the files from this archive, save it to a file, remove +# everything above the "!/bin/sh" line above, and type "sh file_name". +# +# made 07/28/1993 08:13 UTC by mmuegel@mot.com (Michael S. Muegel) +# Source directory /home/ustart/NeXT/src/mail-tools/dist/foo +# +# existing files will NOT be overwritten unless -c is specified +# +# This shar contains: +# length mode name +# ------ ---------- ------------------------------------------ +# 4308 -r--r--r-- README +# 12339 -r--r--r-- libs/date.pl +# 3198 -r--r--r-- libs/elapsed.pl +# 4356 -r--r--r-- libs/mail.pl +# 6908 -r--r--r-- libs/mqueue.pl +# 7024 -r--r--r-- libs/newgetopts.pl +# 4687 -r--r--r-- libs/strings1.pl +# 1609 -r--r--r-- libs/timespec.pl +# 5212 -r--r--r-- man/cqueue.1 +# 2078 -r--r--r-- man/postclip.1 +# 6647 -r-xr-xr-x src/cqueue +# 1836 -r-xr-xr-x src/postclip +# +# ============= README ============== +if test -f 'README' -a X"$1" != X"-c"; then + echo 'x - skipping README (File already exists)' +else +echo 'x - extracting README (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'README' && +------------------------------------------------------------------------------- +Document Revision Control Information: +X mmuegel +X /usr/local/ustart/src/mail-tools/dist/foo/README,v +X 1.1 of 1993/07/28 08:12:53 +------------------------------------------------------------------------------- +X +1. Introduction +--------------- +X +These tools may be of use to those sites using sendmail. Both are written in +Perl. Our site, Mot.COM, receives a ton of mail being a top-level domain +gateway. We have over 24 domains under us. Needless to say, we must have +a robust mail system or my head, and others, would be on the chopping block. +X +2. Description +-------------- +X +The first tool, cqueue, checks the sendmail queue for problems. We use +it to flag problems with subdomain mail servers (and even our own servers +once in a while ;-). We run it via a cron job every hour during the day. +You may find this too frequent, however. +X +The other program, postclip, is used to "filter" non-deliverable NDNs that +get sent to our Postmaster account now and then. This ensures privacy of +e-mail and helps avoid disk problems from huge NDNs. It is different than +a brute force "just keep the header" approach because it tries hard to keep +other parts of the message that look like non-delivery information. +X +Both have been used for some time at our site with no problems. Everything +you need should be in this distribution: source, manual pages, and support +libs. See the manual pages for a complete description of each tool. +X +3. Installation +--------------- +X +No fancy Makefile simply because these tools are all under a large +hierarchy at my site. Installation should be a snap, however. Install +the nroff(1) man(5) manual pages from the man subdirectory to the +appropriate directory on your system. This might be something like +/usr/local/man/man1. +X +Next, install all of the Perl libraries located in the lib subdirectory +to your Perl library area. /usr/local/lib/perl is a good bet. The person +who installed Perl at your site will be able to tell you for sure. +X +Finally, you need to install the programs. Note that cqueue wants to +run setuid root by default. This is because the sendmail queue is normally +only readable by root or some special group. In order to let any user +run this suidperl is used. suidperl allows a Perl program to run with the +privileges of another user. +X +You will have to edit both the cqueue and postclip programs to change +the #! line at the top of each. Just change the pathname to whatever is +appropriate on your system. Note that Larry Wall's fixin program from +the Camel book can also be used to do this. It is very handy. It changes +#! lines by looking at your PATH. +X +If you do not have suidperl on your system change the #! line in cqueue +to reference perl instead of suidperl. +X +You may also wish to change some constants in cqueue. $DEF_QUEUE should be +changed to your queue directory if it is not /usr/spool/mqueue. $DEF_TIME +could be changed easy enough also. It is the time spec for the time duration +after which a mail message will be reported on if the -a option has not been +specified. See the manual page for more information and the format of this +constant (same as the -t argument). Then again, neither of these has to +be changed. Command line options are there to override their default +values. +X +After you have edited the programs as necessary, all that remains is to +install them to some executable directory. Install postclip mode 555 +and cqueue mode 4555 with owner root (if using suidperl) or mode 555 +(if not using suidperl). +X +4. Gripes, Comments, Etc +------------------------ +X +If you start using either of these let me know. I have other mail tools I +will likely post in the future if these prove useful. Also, if you think +something is just plain dumb/wrong/stupid let me know! +X +Cheers, +-Mike +X +-- ++----------------------------------------------------------------------------+ +| Michael S. Muegel | Internet E-Mail: mmuegel@mot.com | +| UNIX Applications Startup Group | Moto Dist E-Mail: X10090 | +| Corporate Information Office | Voice: (708) 576-0507 | +| Motorola | Fax: (708) 576-4153 | ++----------------------------------------------------------------------------+ +SHAR_EOF +chmod 0444 README || +echo 'restore of README failed' +Wc_c="`wc -c < 'README'`" +test 4308 -eq "$Wc_c" || + echo 'README: original size 4308, current size' "$Wc_c" +fi +# ============= libs/date.pl ============== +if test ! -d 'libs'; then + echo 'x - creating directory libs' + mkdir 'libs' +fi +if test -f 'libs/date.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/date.pl (File already exists)' +else +echo 'x - extracting libs/date.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/date.pl' && +;# +;# Name +;# date.pl - Perl emulation of (the output side of) date(1) +;# +;# Synopsis +;# require "date.pl"; +;# $Date = &date(time); +;# $Date = &date(time, $format); +;# +;# Description +;# This package implements the output formatting functions of date(1) in +;# Perl. The format options are based on those supported by Ultrix 4.0 +;# plus a couple of additions from SunOS 4.1.1 and elsewhere: +;# +;# %a abbreviated weekday name - Sun to Sat +;# %A full weekday name - Sunday to Saturday +;# %b abbreviated month name - Jan to Dec +;# %B full month name - January to December +;# %c date and time in local format [+] +;# %C date and time in long local format [+] +;# %d day of month - 01 to 31 +;# %D date as mm/dd/yy +;# %e day of month (space padded) - ` 1' to `31' +;# %E day of month (with suffix: 1st, 2nd, 3rd...) +;# %f month of year (space padded) - ` 1' to `12' +;# %h abbreviated month name - Jan to Dec +;# %H hour - 00 to 23 +;# %i hour (space padded) - ` 1' to `12' +;# %I hour - 01 to 12 +;# %j day of the year (Julian date) - 001 to 366 +;# %k hour (space padded) - ` 0' to `23' +;# %l date in ls(1) format +;# %m month of year - 01 to 12 +;# %M minute - 00 to 59 +;# %n insert a newline character +;# %p ante-meridiem or post-meridiem indicator (AM or PM) +;# %r time in AM/PM notation +;# %R time as HH:MM +;# %S second - 00 to 59 +;# %t insert a tab character +;# %T time as HH:MM:SS +;# %u date/time in date(1) required format +;# %U week number, Sunday as first day of week - 00 to 53 +;# %V date-time in SysV touch format (mmddHHMMyy) +;# %w day of week - 0 (Sunday) to 6 +;# %W week number, Monday as first day of week - 00 to 53 +;# %x date in local format [+] +;# %X time in local format [+] +;# %y last 2 digits of year - 00 to 99 +;# %Y all 4 digits of year ~ 1700 to 2000 odd ? +;# %z time zone from TZ environment variable w/ a trailing space +;# %Z time zone from TZ environment variable +;# %% insert a `%' character +;# %+ insert a `+' character +;# +;# [+]: These may need adjustment to fit local conventions, see below. +;# +;# For the sake of compatibility, a leading `+' in the format +;# specificaiton is removed if present. +;# +;# Remarks +;# This is version 3.4 of date.pl +;# +;# An extension of `ctime.pl' by Waldemar Kebsch (kebsch.pad@nixpbe.UUCP), +;# as modified by Marion Hakanson (hakanson@ogicse.ogi.edu). +;# +;# Unlike date(1), unknown format tags are silently replaced by "". +;# +;# defaultTZ is a blatant hack, but I wanted to be able to get date(1) +;# like behaviour by default and there does'nt seem to be an easy (read +;# portable) way to get the local TZ name back... +;# +;# For a cheap date, try... +;# +;# #!/usr/local/bin/perl +;# require "date.pl"; +;# exit print (&date(time, shift @ARGV) . "\n") ? 0 : 1; +;# +;# This package is redistributable under the same terms as apply to +;# the Perl 4.0 release. See the COPYING file in your Perl kit for +;# more information. +;# +;# Please send any bug reports or comments to tmcgonigal@gallium.com +;# +;# Modification History +;# Nmemonic Version Date Who +;# +;# NONE 1.0 02feb91 Terry McGonigal (tmcgonigal@gallium.com) +;# Created from ctime.pl +;# +;# NONE 2.0 07feb91 tmcgonigal +;# Added some of Marion Hakanson (hakanson@ogicse.ogi.edu)'s ctime.pl +;# TZ handling changes. +;# +;# NONE 2.1 09feb91 tmcgonigal +;# Corrected week number calculations. +;# +;# NONE 2.2 21oct91 tmcgonigal +;# Added ls(1) date format, `%l'. +;# +;# NONE 2.3 06nov91 tmcgonigal +;# Added SysV touch(1) date-time format, `%V' (pretty thin as +;# mnemonics go, I know, but `t' and `T' were both gone already!) +;# +;# NONE 2.4 05jan92 tmcgonigal +;# Corrected slight (cosmetic) problem with %V replacment string +;# +;# NONE 3.0 09jul92 tmcgonigal +;# Fixed a couple of problems with &ls as pointed out by +;# Thomas Richter (richter@ki1.chemie.fu-berlin.de), thanks Thomas! +;# Also added a couple of SunOS 4.1.1 strftime-ish formats, %i and %k +;# for space padded hours (` 1' to `12' and ` 0' to `23' respectivly), +;# and %C for locale long date/time format. Changed &mH to take a +;# pad char parameter to make to evaled code for %i and %k simpler. +;# Added %E for suffixed day-of-month (ie 1st, 3rd, 4th etc). +;# +;# NONE 3.1 16jul92 tmcgonigal +;# Added `%u' format to generate date/time in date(1) required +;# format (ie '%y%m%d%H%M.%S'). +;# +;# NONE 3.2 23jan93 tmcgonigal +;# Added `%f' format to generate space padded month numbers, added +;# `%E' to the header comments, it seems to have been left out (and +;# I'm sure I wanted to use it at some point in the past...). +;# +;# NONE 3.3 03feb93 tmcgonigal +;# Corrected some problems with AM/PM handling pointed out by +;# Michael S. Muegel (mmuegel@mot.com). Thanks Michael, I hope +;# this is the behaviour you were looking for, it seems more +;# correct to me... +;# +;# NONE 3.4 26jul93 tmcgonigal +;# Incorporated some fixes provided by DaviD W. Sanderson +;# (dws@ssec.wisc.edu): February was spelled incorrectly and +;# &wkno() was always using the current year while calculating +;# week numbers, regardless of year implied by the time value +;# passed to &date(). DaviD also contributed an improved &date() +;# test script, thanks DaviD, I appreciate the effort. Finally, +;# changed my mailling address from @gvc.com to @gallium.com +;# to reflect, well, my new address! +;# +;# SccsId = "%W% %E%" +;# +require 'timelocal.pl'; +package date; +X +# Months of the year +@MoY = ('January', 'February', 'March', 'April', 'May', 'June', +X 'July', 'August', 'September','October', 'November', 'December'); +X +# days of the week +@DoW = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', +X 'Thursday', 'Friday', 'Saturday'); +X +# CUSTOMIZE - defaults +$defaultTZ = 'CST'; # time zone (hack!) +$defaultFMT = '%a %h %e %T %z%Y'; # format (ala date(1)) +X +# CUSTOMIZE - `local' formats +$locTF = '%T'; # time (as HH:MM:SS) +$locDF = '%D'; # date (as mm/dd/yy) +$locDTF = '%a %b %d %T %Y'; # date/time (as dow mon dd HH:MM:SS yyyy) +$locLDTF = '%i:%M:%S %p %A %B %E %Y'; # long date/time (as HH:MM:SS a/p day month dom yyyy) +X +# Time zone info +$TZ; # wkno needs this info too +X +# define the known format tags as associative keys with their associated +# replacement strings as values. Each replacement string should be +# an eval-able expresion assigning a value to $rep. These expressions are +# eval-ed, then the value of $rep is substituted into the supplied +# format (if any). +%Tags = ( '%a', q|($rep = $DoW[$wday])=~ s/^(...).*/\1/|, # abbr. weekday name - Sun to Sat +X '%A', q|$rep = $DoW[$wday]|, # full weekday name - Sunday to Saturday +X '%b', q|($rep = $MoY[$mon]) =~ s/^(...).*/\1/|, # abbr. month name - Jan to Dec +X '%B', q|$rep = $MoY[$mon]|, # full month name - January to December +X '%c', q|$rep = $locDTF; 1|, # date/time in local format +X '%C', q|$rep = $locLDTF; 1|, # date/time in local long format +X '%d', q|$rep = &date'pad($mday, 2, "0")|, # day of month - 01 to 31 +X '%D', q|$rep = '%m/%d/%y'|, # date as mm/dd/yy +X '%e', q|$rep = &date'pad($mday, 2, " ")|, # day of month (space padded) ` 1' to `31' +X '%E', q|$rep = &date'dsuf($mday)|, # day of month (w/suffix) `1st' to `31st' +X '%f', q|$rep = &date'pad($mon+1, 2, " ")|, # month of year (space padded) ` 1' to `12' +X '%h', q|$rep = '%b'|, # abbr. month name (same as %b) +X '%H', q|$rep = &date'pad($hour, 2, "0")|, # hour - 00 to 23 +X '%i', q|$rep = &date'ampmH($hour, " ")|, # hour (space padded ` 1' to `12' +X '%I', q|$rep = &date'ampmH($hour, "0")|, # hour - 01 to 12 +X '%j', q|$rep = &date'pad($yday+1, 3, "0")|, # Julian date 001 - 366 +X '%k', q|$rep = &date'pad($hour, 2, " ")|, # hour (space padded) ` 0' to `23' +X '%l', q|$rep = '%b %d ' . &date'ls($year)|, # ls(1) style date +X '%m', q|$rep = &date'pad($mon+1, 2, "0")|, # month of year - 01 to 12 +X '%M', q|$rep = &date'pad($min, 2, "0")|, # minute - 00 to 59 +X '%n', q|$rep = "\n"|, # insert a newline +X '%p', q|$rep = &date'ampmD($hour)|, # insert `AM' or `PM' +X '%r', q|$rep = '%I:%M:%S %p'|, # time in AM/PM notation +X '%R', q|$rep = '%H:%M'|, # time as HH:MM +X '%S', q|$rep = &date'pad($sec, 2, "0")|, # second - 00 to 59 +X '%t', q|$rep = "\t"|, # insert a tab +X '%T', q|$rep = '%H:%M:%S'|, # time as HH:MM:SS +X '%u', q|$rep = '%y%m%d%H%M.%S'|, # daaate/time in date(1) required format +X '%U', q|$rep = &date'wkno($year, $yday, 0)|, # week number (weeks start on Sun) - 00 to 53 +X '%V', q|$rep = '%m%d%H%M%y'|, # SysV touch(1) date-time format (mmddHHMMyy) +X '%w', q|$rep = $wday; 1|, # day of week - Sunday = 0 +X '%W', q|$rep = &date'wkno($year, $yday, 1)|, # week number (weeks start on Mon) - 00 to 53 +X '%x', q|$rep = $locDF; 1|, # date in local format +X '%X', q|$rep = $locTF; 1|, # time in local format +X '%y', q|($rep = $year) =~ s/..(..)/\1/|, # last 2 digits of year - 00 to 99 +X '%Y', q|$rep = "$year"; 1|, # full year ~ 1700 to 2000 odd +X '%z', q|$rep = $TZ eq "" ? "" : "$TZ "|, # time zone from TZ env var (w/trail. space) +X '%Z', q|$rep = $TZ; 1|, # time zone from TZ env. var. +X '%%', q|$rep = '%'; $adv=1|, # insert a `%' +X '%+', q|$rep = '+'| # insert a `+' +); +X +sub main'date { +X local($time, $format) = @_; +X local($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst); +X local($pos, $tag, $rep, $adv) = (0, "", "", 0); +X +X # default to date/ctime format or strip leading `+'... +X if ($format eq "") { +X $format = $defaultFMT; +X } elsif ($format =~ /^\+/) { +X $format = $'; +X } +X +X # Use local time if can't find a TZ in the environment +X $TZ = defined($ENV{'TZ'}) ? $ENV{'TZ'} : $defaultTZ; +X ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = +X &gettime ($TZ, $time); +X +X # Hack to deal with 'PST8PDT' format of TZ +X # Note that this can't deal with all the esoteric forms, but it +X # does recognize the most common: [:]STDoff[DST[off][,rule]] +X if ($TZ =~ /^([^:\d+\-,]{3,})([+-]?\d{1,2}(:\d{1,2}){0,2})([^\d+\-,]{3,})?/) { +X $TZ = $isdst ? $4 : $1; +X } +X +X # watch out in 2070... +X $year += ($year < 70) ? 2000 : 1900; +X +X # now loop throught the supplied format looking for tags... +X while (($pos = index ($format, '%')) != -1) { +X +X # grab the format tag +X $tag = substr($format, $pos, 2); +X $adv = 0; # for `%%' processing +X +X # do we have a replacement string? +X if (defined $Tags{$tag}) { +X +X # trap dead evals... +X if (! eval $Tags{$tag}) { +X print STDERR "date.pl: internal error: eval for $tag failed: $@\n"; +X return ""; +X } +X } else { +X $rep = ""; +X } +X +X # do the substitution +X substr ($format, $pos, 2) =~ s/$tag/$rep/; +X $pos++ if ($adv); +X } +X +X $format; +} +X +# dsuf - add `st', `nd', `rd', `th' to a date (ie 1st, 22nd, 29th) +sub dsuf { +X local ($mday) = @_; +X +X return $mday . 'st' if ($mday =~ m/.*1$/); +X return $mday . 'nd' if ($mday =~ m/.*2$/); +X return $mday . 'rd' if ($mday =~ m/.*3$/); +X return $mday . 'th'; +} +X +# weekno - figure out week number +sub wkno { +X local ($year, $yday, $firstweekday) = @_; +X local ($jan1, @jan1, $wks); +X +X # figure out the `time' value for January 1 of the given year +X $jan1 = &maketime ($TZ, 0, 0, 0, 1, 0, $year-1900); +X +X # figure out what day of the week January 1 was +X @jan1= &gettime ($TZ, $jan1); +X +X # and calculate the week number +X $wks = (($yday + ($jan1[6] - $firstweekday)) + 1)/ 7; +X $wks += (($wks - int($wks) > 0.0) ? 1 : 0); +X +X # supply zero padding +X &pad (int($wks), 2, "0"); +} +X +# ampmH - figure out am/pm (1 - 12) mode hour value, padded with $p (0 or ' ') +sub ampmH { local ($h, $p) = @_; &pad($h>12 ? $h-12 : ($h ? $h : 12), 2, $p); } +X +# ampmD - figure out am/pm designator +sub ampmD { shift @_ >= 12 ? "PM" : "AM"; } +X +# gettime - get the time via {local,gmt}time +sub gettime { ((shift @_) eq 'GMT') ? gmtime(shift @_) : localtime(shift @_); } +X +# maketime - make a time via time{local,gmt} +sub maketime { ((shift @_) eq 'GMT') ? &main'timegm(@_) : &main'timelocal(@_); } +X +# ls - generate the time/year portion of an ls(1) style date +sub ls { +X return ((&gettime ($TZ, time))[5] == @_[0]) ? "%R" : " %Y"; +} +X +# pad - pad $in with leading $pad until lenght $len +sub pad { +X local ($in, $len, $pad) = @_; +X local ($out) = "$in"; +X +X $out = $pad . $out until (length ($out) == $len); +X return $out; +} +X +1; +SHAR_EOF +chmod 0444 libs/date.pl || +echo 'restore of libs/date.pl failed' +Wc_c="`wc -c < 'libs/date.pl'`" +test 12339 -eq "$Wc_c" || + echo 'libs/date.pl: original size 12339, current size' "$Wc_c" +fi +# ============= libs/elapsed.pl ============== +if test -f 'libs/elapsed.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/elapsed.pl (File already exists)' +else +echo 'x - extracting libs/elapsed.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/elapsed.pl' && +;# NAME +;# elapsed.pl - convert seconds to elapsed time format +;# +;# AUTHOR +;# Michael S. Muegel <mmuegel@mot.com> +;# +;# RCS INFORMATION +;# mmuegel +;# /usr/local/ustart/src/mail-tools/dist/foo/libs/elapsed.pl,v +;# 1.1 of 1993/07/28 08:07:19 +X +package elapsed; +X +# Time field types +$DAYS = 1; +$HOURS = 2; +$MINUTES = 3; +$SECONDS = 4; +X +# The array contains four records each with four fields. The fields are, +# in order: +# +# Type Specifies what kind of time field this is. Once of +# $DAYS, $HOURS, $MINUTES, or $SECONDS. +# +# Multiplier Specifies what time field this is via the minimum +# number of seconds this time field may specify. For +# example, the minutes field would be non-zero +# when there are 60 or more seconds. +# +# Separator How to separate this time field from the next +# *greater* field. +# +# Format sprintf() format specifier on how to print this +# time field. +@MULT_AND_SEPS = ($DAYS, 60 * 60 * 24, "+", "%d", +X $HOURS, 60 * 60, ":", "%d", +X $MINUTES, 60, ":", "%02d", +X $SECONDS, 1, "", "%02d" +X ); +X +;############################################################################### +;# Seconds_To_Elapsed +;# +;# Coverts a seconds count to form [d+]h:mm:ss. If $Collapse +;# is true then the result is compacted somewhat. The string returned +;# will be of the form [d+][[h:]mm]:ss. +;# +;# Arguments: +;# $Seconds, $Collapse +;# +;# Examples: +;# &Seconds_To_Elapsed (0, 0) -> 0:00:00 +;# &Seconds_To_Elapsed (0, 1) -> :00 +;# +;# &Seconds_To_Elapsed (119, 0) -> 0:01:59 +;# &Seconds_To_Elapsed (119, 1) -> 01:59 +;# +;# &Seconds_To_Elapsed (3601, 0) -> 1:00:01 +;# &Seconds_To_Elapsed (3601, 1) -> 1:00:01 +;# +;# &Seconds_To_Elapsed (86401, 0) -> 1+0:00:01 +;# &Seconds_To_Elapsed (86401, 1) -> 1+:01 +;# +;# Returns: +;# $Elapsed +;############################################################################### +sub main'Seconds_To_Elapsed +{ +X local ($Seconds, $Collapse) = @_; +X local ($Type, $Multiplier, @Multipliers, $Separator, $DHMS_Used, +X $Elapsed, @Mult_And_Seps, $Print_Field); +X +X $Multiplier = 1; +X @Mult_And_Seps = @MULT_AND_SEPS; +X +X # Keep subtracting the number of seconds corresponding to a time field +X # from the number of seconds passed to the function. +X while (1) +X { +X ($Type, $Multiplier, $Separator, $Format) = splice (@Mult_And_Seps, 0, 4); +X last if (! $Multiplier); +X $Seconds -= $DHMS_Used * $Multiplier +X if ($DHMS_Used = int ($Seconds / $Multiplier)); +X +X # Figure out if we should print this field +X if ($Type == $DAYS) +X { +X $Print_Field = $DHMS_Used; +X } +X +X elsif ($Collapse) +X { +X if ($Type == $HOURS) +X { +X $Print_Field = $DHMS_Used; +X } +X elsif ($Type == $MINUTES) +X { +X $Print_Field = $DHMS_Used || $Printed_Field {$HOURS}; +X } +X else +X { +X $Format = ":%02d" +X if (! $Printed_Field {$MINUTES}); +X $Print_Field = 1; +X }; +X } +X +X else +X { +X $Print_Field = 1; +X }; +X +X $Printed_Field {$Type} = $Print_Field; +X $Elapsed .= sprintf ("$Format%s", $DHMS_Used, $Separator) +X if ($Print_Field); +X }; +X +X return ($Elapsed); +}; +X +1; +SHAR_EOF +chmod 0444 libs/elapsed.pl || +echo 'restore of libs/elapsed.pl failed' +Wc_c="`wc -c < 'libs/elapsed.pl'`" +test 3198 -eq "$Wc_c" || + echo 'libs/elapsed.pl: original size 3198, current size' "$Wc_c" +fi +# ============= libs/mail.pl ============== +if test -f 'libs/mail.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/mail.pl (File already exists)' +else +echo 'x - extracting libs/mail.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/mail.pl' && +;# NAME +;# mail.pl - perl function(s) to handle mail processing +;# +;# AUTHOR +;# Michael S. Muegel (mmuegel@mot.com) +;# +;# RCS INFORMATION +;# mmuegel +;# /usr/local/ustart/src/mail-tools/dist/foo/libs/mail.pl,v 1.1 1993/07/28 08:07:19 mmuegel Exp +X +package mail; +X +# Mailer statement to eval. $Users, $Subject, and $Verbose are substituted +# via eval +$BIN_MAILER = "/usr/ucb/mail \$Verbose -s '\$Subject' \$Users"; +X +# Sendmail command to use when $Use_Sendmail is true. +$SENDMAIL = '/usr/lib/sendmail $Verbose $Users'; +X +;############################################################################### +;# Send_Mail +;# +;# Sends $Message to $Users with a subject of $Subject. If $Message_Is_File +;# is true then $Message is assumed to be a filename pointing to the mail +;# message. This is a new option and thus the backwards-compatible hack. +;# $Users should be a space separated list of mail-ids. +;# +;# If everything went OK $Status will be 1 and $Error_Msg can be ignored; +;# otherwise, $Status will be 0 and $Error_Msg will contain an error message. +;# +;# If $Use_Sendmail is 1 then sendmail is used to send the message. Normally +;# a mailer such as Mail is used. By specifiying this you can include +;# headers in addition to text in either $Message or $Message_Is_File. +;# If either $Message or $Message_Is_File contain a Subject: header then +;# $Subject is ignored; otherwise, a Subject: header is automatically created. +;# Similar to the Subject: header, if a To: header does not exist one +;# is automatically created from the $Users argument. The mail is still +;# sent, however, to the recipients listed in $Users. This is keeping with +;# normal sendmail usage (header vs. envelope). +;# +;# In both bin mailer and sendmail modes $Verbose will turn on verbose mode +;# (normally just sendmail verbose mode output). +;# +;# Arguments: +;# $Users, $Subject, $Message, $Message_Is_File, $Verbose, $Use_Sendmail +;# +;# Returns: +;# $Status, $Error_Msg +;############################################################################### +sub main'Send_Mail +{ +X local ($Users, $Subject, $Message, $Message_Is_File, $Verbose, +X $Use_Sendmail) = @_; +X local ($BIN_MAILER_HANDLE, $Mailer_Command, $Header_Found, %Header_Map, +X $Header_Extra, $Mailer); +X +X # If the message is contained in a file read it in so we can have one +X # consistent interface +X if ($Message_Is_File) +X { +X undef $/; +X $Message_Is_File = 0; +X open (Message) || return (0, "error reading $Message: $!"); +X $Message = <Message>; +X close (Message); +X }; +X +X # If sendmail mode see if we need to add some headers +X if ($Use_Sendmail) +X { +X # Determine if a header block is included in the message and what headers +X # are there +X foreach (split (/\n/, $Message)) +X { +X last if ($_ eq ""); +X $Header_Found = $Header_Map {$1} = 1 if (/^([A-Z]\S*): /); +X }; +X +X # Add some headers? +X if (! $Header_Map {"To"}) +X { +X $Header_Extra .= "To: " . join (", ", $Users) . "\n"; +X }; +X if (($Subject ne "") && (! $Header_Map {"Subject"})) +X { +X $Header_Extra .= "Subject: $Subject\n"; +X }; +X +X # Add the required blank line between header/body if there where no +X # headers to begin with +X if ($Header_Found) +X { +X $Message = "$Header_Extra$Message"; +X } +X else +X { +X $Message = "$Header_Extra\n$Message"; +X }; +X }; +X +X # Get a string that is the mail command +X $Verbose = ($Verbose) ? "-v" : ""; +X $Mailer = ($Use_Sendmail) ? $SENDMAIL : $BIN_MAILER; +X eval "\$Mailer = \"$Mailer\""; +X return (0, "error setting \$Mailer: $@") if ($@); +X +X # need to catch SIGPIPE in case the $Mailer call fails +X $SIG {'PIPE'} = "mail'Cleanup"; +X +X # Open mailer +X return (0, "can not open mail program: $Mailer") if (! open (MAILER, "| $Mailer")); +X +X # Send off the mail! +X print MAILER $Message; +X close (MAILER); +X return (0, "error running mail program: $Mailer") if ($?); +X +X # Everything must have went AOK +X return (1); +}; +X +;############################################################################### +;# Cleanup +;# +;# Simply here so we can catch SIGPIPE and not exit. +;# +;# Globals: +;# None +;# +;# Arguments: +;# None +;# +;# Returns: +;# Nothing exciting +;############################################################################### +sub Cleanup +{ +}; +X +1; +SHAR_EOF +chmod 0444 libs/mail.pl || +echo 'restore of libs/mail.pl failed' +Wc_c="`wc -c < 'libs/mail.pl'`" +test 4356 -eq "$Wc_c" || + echo 'libs/mail.pl: original size 4356, current size' "$Wc_c" +fi +# ============= libs/mqueue.pl ============== +if test -f 'libs/mqueue.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/mqueue.pl (File already exists)' +else +echo 'x - extracting libs/mqueue.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/mqueue.pl' && +;# NAME +;# mqueue.pl - functions to work with the sendmail queue +;# +;# DESCRIPTION +;# Both Get_Queue_IDs and Parse_Control_File are available to get +;# information about the sendmail queue. The cqueue program is a good +;# example of how these functions work. +;# +;# AUTHOR +;# Michael S. Muegel (mmuegel@mot.com) +;# +;# RCS INFORMATION +;# mmuegel +;# /usr/local/ustart/src/mail-tools/dist/foo/libs/mqueue.pl,v +;# 1.1 of 1993/07/28 08:07:19 +X +package mqueue; +X +;############################################################################### +;# Get_Queue_IDs +;# +;# Will figure out the queue IDs in $Queue that have both control and data +;# files. They are returned in @Valid_IDs. Those IDs that have a +;# control file and no data file are saved to the array globbed by +;# *Missing_Control_IDs. Likewise, those IDs that have a data file and no +;# control file are saved to the array globbed by *Missing_Data_IDs. +;# +;# If $Skip_Locked is true they a message that has a lock file is skipped +;# and will not show up in any of the arrays. +;# +;# If everything went AOK then $Status is 1; otherwise, $Status is 0 and +;# $Msg tells what went wrong. +;# +;# Globals: +;# None +;# +;# Arguments: +;# $Queue, $Skip_Locked, *Missing_Control_IDs, *Missing_Data_IDs +;# +;# Returns: +;# $Status, $Msg, @Valid_IDs +;############################################################################### +sub main'Get_Queue_IDs +{ +X local ($Queue, $Skip_Locked, *Missing_Control_IDs, +X *Missing_Data_IDs) = @_; +X local (*QUEUE, @Files, %Lock_IDs, %Data_IDs, %Control_IDs, $_); +X +X # Make sure that the * argument @arrays ar empty +X @Missing_Control_IDs = @Missing_Data_IDs = (); +X +X # Save each data, lock, and queue file in @Files +X opendir (QUEUE, $Queue) || return (0, "error getting directory listing of $Queue"); +X @Files = grep (/^(df|lf|qf)/, readdir (QUEUE)); +X closedir (QUEUE); +X +X # Create indexed list of data and control files. IF $Skip_Locked is true +X # then skip either if there is a lock file present. +X if ($Skip_Locked) +X { +X grep ((s/^lf//) && ($Lock_IDs {$_} = 1), @Files); +X grep ((s/^df//) && (! $Lock_IDs {$_}) && ($Data_IDs {$_} = 1), @Files); +X grep ((s/^qf//) && (! $Lock_IDs {$_}) && ($Control_IDs {$_} = 1), @Files); +X } +X else +X { +X grep ((s/^df//) && ($Data_IDs {$_} = 1), @Files); +X grep ((s/^qf//) && ($Control_IDs {$_} = 1), @Files); +X }; +X +X # Find missing control and data files and remove them from the lists of each +X @Missing_Control_IDs = sort (grep ((! $Control_IDs {$_}) && (delete $Data_IDs {$_}), keys (%Data_IDs))); +X @Missing_Data_IDs = sort (grep ((! $Data_IDs {$_} && (delete $Control_IDs {$_})), keys (%Control_IDs))); +X +X +X # Return the IDs in an appartently random order +X return (1, "", keys (%Control_IDs)); +}; +X +X +;############################################################################### +;# Parse_Control_File +;# +;# Will pase a sendmail queue control file for useful information. See the +;# Sendmail Installtion and Operation Guide (SMM:07) for a complete +;# explanation of each field. +;# +;# The following globbed variables are set (or cleared) by this function: +;# +;# $Sender The sender's address. +;# +;# @Recipients One or more addresses for the recipient of the mail. +;# +;# @Errors_To One or more addresses for addresses to which mail +;# delivery errors should be sent. +;# +;# $Creation_Time The job creation time in time(3) format. That is, +;# seconds since 00:00:00 GMT 1/1/70. +;# +;# $Priority An integer representing the current message priority. +;# This is used to order the queue. Higher numbers mean +;# lower priorities. +;# +;# $Status_Message The status of the mail message. It can contain any +;# text. +;# +;# @Headers Message headers unparsed but in their original order. +;# Headers that span multiple lines are not mucked with, +;# embedded \ns will be evident. +;# +;# In all e-mail addresses bounding <> pairs are stripped. +;# +;# If everything went AOK then $Status is 1. If the message with queue ID +;# $Queue_ID just does not exist anymore -1 is returned. This is very +;# possible and should be allowed for. Otherwise, $Status is 0 and $Msg +;# tells what went wrong. +;# +;# Globals: +;# None +;# +;# Arguments: +;# $Queue, $Queue_ID, *Sender, *Recipients, *Errors_To, *Creation_Time, +;# *Priority, *Status_Message, *Headers +;# +;# Returns: +;# $Status, $Msg +;############################################################################### +sub main'Parse_Control_File +{ +X local ($Queue, $Queue_ID, *Sender, *Recipients, *Errors_To, *Creation_Time, +X *Priority, *Status_Message, *Headers) = @_; +X local (*Control, $_, $Not_Empty); +X +X # Required variables and the associated control. If empty at the end of +X # parsing we return a bad status. +X @REQUIRED_INFO = ('$Creation_Time', 'T', '$Sender', 'S', '@Recipients', 'R', +X '$Priority', 'P'); +X +X # Open up the control file for read +X $Control = "$Queue/qf$Queue_ID"; +X if (! open (Control)) +X { +X return (-1) if ((-x $Queue) && (! -f "$Queue/qf$Queue_ID") && +X (! -f "$Queue/df$Queue_ID")); +X return (0, "error opening $Control for read: $!"); +X }; +X +X # Reset the globbed variables just in case +X $Sender = $Creation_Time = $Priority = $Status_Message = ""; +X @Recipients = @Errors_To = @Headers = (); +X +X # Look for a few things in the control file +X READ: while (<Control>) +X { +X $Not_Empty = 1; +X chop; +X +X PARSE: +X { +X if (/^T(\d+)$/) +X { +X $Creation_Time = $1; +X } +X elsif (/^S(<)?([^>]+)/) +X { +X $Sender = $2; +X } +X elsif (/^R(<)?([^>]+)/) +X { +X push (@Recipients, $2); +X } +X elsif (/^E(<)?([^>]+)/) +X { +X push (@Errors_To, $2); +X } +X elsif (/^M(.*)/) +X { +X $Status_Message = $1; +X } +X elsif (/^P(\d+)$/) +X { +X $Priority = $1; +X } +X elsif (/^H(.*)/) +X { +X $Header = $1; +X while (<Control>) +X { +X chop; +X last if (/^[A-Z]/); +X $Header .= "\n$_"; +X }; +X push (@Headers, $Header); +X redo PARSE if ($_); +X last if (eof); +X }; +X }; +X }; +X +X # If the file was empty scream bloody murder +X return (0, "empty control file") if (! $Not_Empty); +X +X # Yell if we could not find a required field +X while (($Var, $Control) = splice (@REQUIRED_INFO, 0, 2)) +X { +X eval "return (0, 'required control field $Control not found') +X if (! $Var)"; +X return (0, "error checking \$Var: $@") if ($@); +X }; +X +X # Everything went AOK +X return (1); +}; +X +1; +SHAR_EOF +chmod 0444 libs/mqueue.pl || +echo 'restore of libs/mqueue.pl failed' +Wc_c="`wc -c < 'libs/mqueue.pl'`" +test 6908 -eq "$Wc_c" || + echo 'libs/mqueue.pl: original size 6908, current size' "$Wc_c" +fi +# ============= libs/newgetopts.pl ============== +if test -f 'libs/newgetopts.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/newgetopts.pl (File already exists)' +else +echo 'x - extracting libs/newgetopts.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/newgetopts.pl' && +;# NAME +;# newgetopts.pl - a better newgetopt (which is a better getopts which is +;# a better getopt ;-) +;# +;# AUTHOR +;# Mike Muegel (mmuegel@mot.com) +;# +;# mmuegel +;# /usr/local/ustart/src/mail-tools/dist/foo/libs/newgetopts.pl,v 1.1 1993/07/28 08:07:19 mmuegel Exp +X +;############################################################################### +;# New_Getopts +;# +;# Does not care about order of switches, options, and arguments like +;# getopts.pl. Thus all non-switches/options will be kept in ARGV even if they +;# are not at the end. If $Pass_Invalid is set all unkown options will be +;# passed back to the caller by keeping them in @ARGV. This is useful when +;# parsing a command line for your script while ignoring options that you +;# may pass to another script. If this is set New_Getopts tries to maintain +;# the switch clustering on the unkown switches. +;# +;# Accepts the special argument -usage to print the Usage string. Also accepts +;# the special option -version which prints the contents of the string +;# $VERSION. $VERSION may or may not have an embeded \n in it. If -usage +;# or -version are specified a status of -1 is returned. Note that the usage +;# option is only accepted if the usage string is not null. +;# +;# $Switches is just like the formal arguemnt of getopts.pl. $Usage is a usage +;# string with or without a trailing \n. *Switch_To_Order is an optional +;# pointer to the name of an associative array which will contain a mapping of +;# switch names to the order in which (if at all) the argument was entered. +;# +;# For example, if @ARGV contains -v, -x, test: +;# +;# $Switch_To_Order {"v"} = 1; +;# $Switch_To_Order {"x"} = 2; +;# +;# Note that in the case of multiple occurances of an option $Switch_To_Order +;# will store each occurance of the argument via a string that emulates +;# an array. This is done by using join ($;, ...). You can retrieve the +;# array by using split (/$;/, ...). +;# +;# *Split_ARGV is an optional pointer to an array which will conatin the +;# original switches along with their values. For the example used above +;# Split_ARGV would contain: +;# +;# @Split_ARGV = ("v", "", "x", "test"); +;# +;# Another exciting ;-) feature that newgetopts has. Along with creating the +;# normal $opt_ scalars for the last value of an argument the list @opt_ is +;# created. It is an array which contains all the values of arguments to the +;# basename of the variable. They are stored in the order which they occured +;# on the command line starting with $[. Note that blank arguments are stored +;# as "". Along with providing support for multiple options on the command +;# line this also provides a method of counting the number of times an option +;# was specified via $#opt_. +;# +;# Automatically resets all $opt_, @opt_, %Switch_To_Order, and @Split_ARGV +;# variables so that New_Getopts may be called more than once from within +;# the same program. Thus, if $opt_v is set upon entry to New_Getopts and +;# -v is not in @ARGV $opt_v will not be set upon exit. +;# +;# Arguments: +;# $Switches, $Usage, $Pass_Invalid, *Switch_To_Order, *Split_ARGV +;# +;# Returns: +;# -1, 0, or 1 depending on status (printed Usage/Version, OK, not OK) +;############################################################################### +sub New_Getopts +{ +X local($taint_argumentative, $Usage, $Pass_Invalid, *Switch_To_Order, +X *Split_ARGV) = @_; +X local(@args,$_,$first,$rest,$errs, @leftovers, @current_leftovers, +X %Switch_Found); +X local($[, $*, $Script_Name, $argumentative); +X +X # Untaint the argument cluster so that we can use this with taintperl +X $taint_argumentative =~ /^(.*)$/; +X $argumentative = $1; +X +X # Clear anything that might still be set from a previous New_Getopts +X # call. +X @Split_ARGV = (); +X +X # Get the basename of the calling script +X ($Script_Name = $0) =~ s/.*\///; +X +X # Make Usage have a trailing \n +X $Usage .= "\n" if ($Usage !~ /\n$/); +X +X @args = split( / */, $argumentative ); +X +X # Clear anything that might still be set from a previous New_Getopts call. +X foreach $first (@args) +X { +X next if ($first eq ":"); +X delete $Switch_Found {$first}; +X delete $Switch_To_Order {$first}; +X eval "undef \@opt_$first; undef \$opt_$first;"; +X }; +X +X while (@ARGV) +X { +X # Let usage through +X if (($ARGV[0] eq "-usage") && ($Usage ne "\n")) +X { +X print $Usage; +X exit (-1); +X } +X +X elsif ($ARGV[0] eq "-version") +X { +X if ($VERSION) +X { +X print $VERSION; +X print "\n" if ($VERSION !~ /\n$/); +X } +X else +X { +X warn "${Script_Name}: no version information available, sorry\n"; +X } +X exit (-1); +X } +X +X elsif (($_ = $ARGV[0]) =~ /^-(.)(.*)/) +X { +X ($first,$rest) = ($1,$2); +X $pos = index($argumentative,$first); +X +X $Switch_To_Order {$first} = join ($;, split (/$;/, $Switch_To_Order {$first}), ++$Order); +X +X if($pos >= $[) +X { +X if($args[$pos+1] eq ':') +X { +X shift(@ARGV); +X if($rest eq '') +X { +X $rest = shift(@ARGV); +X } +X +X eval "\$opt_$first = \$rest;"; +X eval "push (\@opt_$first, \$rest);"; +X push (@Split_ARGV, $first, $rest); +X } +X else +X { +X eval "\$opt_$first = 1"; +X eval "push (\@opt_$first, '');"; +X push (@Split_ARGV, $first, ""); +X +X if($rest eq '') +X { +X shift(@ARGV); +X } +X else +X { +X $ARGV[0] = "-$rest"; +X } +X } +X } +X +X else +X { +X # Save any other switches if $Pass_Valid +X if ($Pass_Invalid) +X { +X push (@current_leftovers, $first); +X } +X else +X { +X warn "${Script_Name}: unknown option: $first\n"; +X ++$errs; +X }; +X if($rest ne '') +X { +X $ARGV[0] = "-$rest"; +X } +X else +X { +X shift(@ARGV); +X } +X } +X } +X +X else +X { +X push (@leftovers, shift (@ARGV)); +X }; +X +X # Save any other switches if $Pass_Valid +X if ((@current_leftovers) && ($rest eq '')) +X { +X push (@leftovers, "-" . join ("", @current_leftovers)); +X @current_leftovers = (); +X }; +X }; +X +X # Automatically print Usage if a warning was given +X @ARGV = @leftovers; +X if ($errs != 0) +X { +X warn $Usage; +X return (0); +X } +X else +X { +X return (1); +X } +X +} +X +1; +SHAR_EOF +chmod 0444 libs/newgetopts.pl || +echo 'restore of libs/newgetopts.pl failed' +Wc_c="`wc -c < 'libs/newgetopts.pl'`" +test 7024 -eq "$Wc_c" || + echo 'libs/newgetopts.pl: original size 7024, current size' "$Wc_c" +fi +# ============= libs/strings1.pl ============== +if test -f 'libs/strings1.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/strings1.pl (File already exists)' +else +echo 'x - extracting libs/strings1.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/strings1.pl' && +;# NAME +;# strings1.pl - FUN with strings #1 +;# +;# NOTES +;# I wrote Format_Text_Block when I just started programming Perl so +;# it is probably not very Perlish code. Center is more like it :-). +;# +;# AUTHOR +;# Michael S. Muegel (mmuegel@mot.com) +;# +;# RCS INFORMATION +;# mmuegel +;# /usr/local/ustart/src/mail-tools/dist/foo/libs/strings1.pl,v 1.1 1993/07/28 08:07:19 mmuegel Exp +X +package strings1; +X +;###############################################################################;# Center +;# +;# Center $Text assuming the output should be $Columns wide. $Text can span +;# multiple lines, of course :-). Lines within $Text that contain only +;# whitespace are not centered and are instead collapsed. This may save time +;# when printing them later. +;# +;# Arguments: +;# $Text, $Columns +;# +;# Returns: +;# $Centered_Text +;############################################################################### +sub main'Center +{ +X local ($_, $Columns) = @_; +X local ($*) = 1; +X +X s@^(.*)$@" " x (($Columns - length ($1)) / 2) . $1@eg; +X s/^[\t ]*$//g; +X return ($_); +}; +X +;############################################################################### +;# Format_Text_Block +;# +;# Formats a text string to be printed to the display or other similar device. +;# Text in $String will be fomratted such that the following hold: +;# +;# + $String contains the (possibly) multi-line text to print. It is +;# automatically word-wrapped to fit in $Columns. +;# +;# + \n'd are maintained and are not folded. +;# +;# + $Offset is pre-pended before each separate line of text. +;# +;# + If $Offset_Once is $TRUE $Offset will only appear on the first line. +;# All other lines will be indented to match the amount of whitespace of +;# $Offset. +;# +;# + If $Bullet_Indent is $TRUE $Offset will only be applied to the begining +;# of lines as they occured in the original $String. Lines that are created +;# by this routine will always be indented by blank spaces. +;# +;# + If $Columns is 0 no word-wrap is done. This might be useful to still +;# to offset each line in a buffer. +;# +;# + If $Split_Expr is supplied the string is split on it. If not supplied +;# the string is split on " \t\/\-\,\." by default. +;# +;# + If $Offset_Blank is $TRUE then empty lines will have $Offset pre-pended +;# to them. Otherwise, they will still empty. +;# +;# This is a realy workhorse routine that I use in many places because of its +;# veratility. +;# +;# Arguments: +;# $String, $Offset, $Offset_Once, $Bullet_Indent, $Columns, $Split_Expr, +;# $Offset_Blank +;# +;# Returns: +;# $Buffer +;############################################################################### +sub main'Format_Text_Block +{ +X local ($String, $Real_Offset, $Offset_Once, $Bullet_Indent, $Columns, +X $Split_Expr, $Offset_Blank) = @_; +X +X local ($New_Line, $Line, $Chars_Per_Line, $Space_Offset, $Buffer, +X $Next_New_Line, $Num_Lines, $Num_Offsets, $Offset); +X local ($*) = 0; +X local ($BLANK_TAG) = "__FORMAT_BLANK__"; +X local ($Blank_Offset) = $Real_Offset if ($Offset_Blank); +X +X # What should we split on? +X $Split_Expr = " \\t\\/\\-\\,\\." if (! $Split_Expr); +X +X # Pre-process the string - convert blank lines to __FORMAT_BLANK__ sequence +X $String =~ s/\n\n/\n$BLANK_TAG\n/g; +X $String =~ s/^\n/$BLANK_TAG\n/g; +X $String =~ s/\n$/\n$BLANK_TAG/g; +X +X # If bad $Columns/$Offset combo or no $Columns make a VERRRYYY wide $Column +X $Offset = $Real_Offset; +X $Chars_Per_Line = 16000 if (($Chars_Per_Line = $Columns - length ($Offset)) <= 0); +X $Space_Offset = " " x length ($Offset); +X +X # Get a buffer +X foreach $Line (split ("\n", $String)) +X { +X $Offset = $Real_Offset if ($Bullet_Indent); +X +X # Find where to split the line +X if ($Line ne $BLANK_TAG) +X { +X $New_Line = ""; +X while ($Line =~ /^([$Split_Expr]*)([^$Split_Expr]+)/) +X { +X if (length ("$New_Line$&") >= $Chars_Per_Line) +X { +X $Next_New_Line = $+; +X $New_Line = "$Offset$New_Line$1"; +X $Buffer .= "\n" if ($Num_Lines++); +X $Buffer .= $New_Line; +X $Offset = $Space_Offset if (($Offset) && ($Offset_Once)); +X $New_Line = $Next_New_Line; +X ++$Num_Lines; +X } +X else +X { +X $New_Line .= $&; +X }; +X $Line = $'; +X }; +X +X $Buffer .= "\n" if ($Num_Lines++); +X $Buffer .= "$Offset$New_Line$Line"; +X $Offset = $Space_Offset if (($Offset) && ($Offset_Once)); +X } +X +X else +X { +X $Buffer .= "\n$Blank_Offset"; +X }; +X }; +X +X return ($Buffer); +X +}; +X +1; +SHAR_EOF +chmod 0444 libs/strings1.pl || +echo 'restore of libs/strings1.pl failed' +Wc_c="`wc -c < 'libs/strings1.pl'`" +test 4687 -eq "$Wc_c" || + echo 'libs/strings1.pl: original size 4687, current size' "$Wc_c" +fi +# ============= libs/timespec.pl ============== +if test -f 'libs/timespec.pl' -a X"$1" != X"-c"; then + echo 'x - skipping libs/timespec.pl (File already exists)' +else +echo 'x - extracting libs/timespec.pl (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'libs/timespec.pl' && +;# NAME +;# timespec.pl - convert a pre-defined time specifyer to seconds +;# +;# AUTHOR +;# Michael S. Muegel (mmuegel@mot.com) +;# +;# RCS INFORMATION +;# mmuegel +;# /usr/local/ustart/src/mail-tools/dist/foo/libs/timespec.pl,v 1.1 1993/07/28 08:07:19 mmuegel Exp +X +package timespec; +X +%TIME_SPEC_TO_SECONDS = ("s", 1, +X "m", 60, +X "h", 60 * 60, +X "d", 60 * 60 * 24 +X ); +X +$VALID_TIME_SPEC_EXPR = "[" . join ("", keys (%TIME_SPEC_TO_SECONDS)) . "]"; +X +;############################################################################### +;# Time_Spec_To_Seconds +;# +;# Converts a string of the form: +;# +;# (<number>(s|m|h|d))+ +;# +;# to seconds. The second part of the time spec specifies seconds, minutes, +;# hours, or days, respectfully. The first part is the number of those untis. +;# There can be any number of such specifiers. As an example, 1h30m means 1 +;# hour and 30 minutes. +;# +;# If the parsing went OK then $Status is 1, $Msg is undefined, and $Seconds +;# is $Time_Spec converted to seconds. If something went wrong then $Status +;# is 0 and $Msg explains what went wrong. +;# +;# Arguments: +;# $Time_Spec +;# +;# Returns: +;# $Status, $Msg, $Seconds +;############################################################################### +sub main'Time_Spec_To_Seconds +{ +X $Time_Spec = $_[0]; +X +X $Seconds = 0; +X while ($Time_Spec =~ /^(\d+)($VALID_TIME_SPEC_EXPR)/) +X { +X $Seconds += $1 * $TIME_SPEC_TO_SECONDS {$2}; +X $Time_Spec = $'; +X }; +X +X return (0, "error parsing time spec: $Time_Spec") if ($Time_Spec ne ""); +X return (1, "", $Seconds); +X +}; +X +X +1; +SHAR_EOF +chmod 0444 libs/timespec.pl || +echo 'restore of libs/timespec.pl failed' +Wc_c="`wc -c < 'libs/timespec.pl'`" +test 1609 -eq "$Wc_c" || + echo 'libs/timespec.pl: original size 1609, current size' "$Wc_c" +fi +# ============= man/cqueue.1 ============== +if test ! -d 'man'; then + echo 'x - creating directory man' + mkdir 'man' +fi +if test -f 'man/cqueue.1' -a X"$1" != X"-c"; then + echo 'x - skipping man/cqueue.1 (File already exists)' +else +echo 'x - extracting man/cqueue.1 (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'man/cqueue.1' && +.TH CQUEUE 1L +\" +\" mmuegel +\" /usr/local/ustart/src/mail-tools/dist/foo/man/cqueue.1,v 1.1 1993/07/28 08:08:25 mmuegel Exp +\" +.ds mp \fBcqueue\fR +.de IB +.IP \(bu 2 +.. +.SH NAME +\*(mp - check sendmail queue for problems +.SH SYNOPSIS +.IP \*(mp 7 +[ \fB-abdms\fR ] [ \fB-q\fR \fIqueue-dir\fI ] [ \fB-t\fR \fItime\fR ] +[ \fB-u\fR \fIusers\fR ] [ \fB-w\fR \fIwidth\fR ] +.SH DESCRIPTION +Reports on problems in the sendmail queue. With no options this simply +means listing messages that have been in the queue longer than a default +period along with a summary of queue mail by host and status message. +.SH OPTIONS +.IP \fB-a\fR 14 +Report on all messages in the queue. This is equivalent to saying \fB-t\fR 0s. +You may like this command so much that you use it as a replacement for +\fBmqueue\fR. For example: +.sp 1 +.RS +.RS +\fBalias mqueue cqueue -a\fR +.RE +.RE +.IP \fB-b\fR 14 +Also report on bogus queue files. Those are files that +have data files and no control files or vice versa. +.IP \fB-d\fR +Print a detailed report of mail messages that have been queued longer than +the specified or default time. Information that is presented includes: +.RS +.RS +.IB +Sendmail queue identifier. +.IB +Date the message was first queued. +.IB +Sender of the message. +.IB +One or more recipients of the message. +.IB +An optional status of the message. This usually indicates why the message +has not been delivered. +.RE +.RE +.IP \fB-m\fR 14 +Mail off the results if any problems were found. +Normaly results are printed to stdout. If this option +is specified they are mailed to one or more users. Results +are not printed to stdout in this case. Results are \fBonly\fR +mailed if \*(mp found something wrong. +.IP "\fB-q\fR \fIqueue-dir\fI" +The sendmail mail queue directory. Default is \fB/usr/spool/mqueue\fR or +some other site configured value. +.IP "\fB-t\fR \fItime\fR" +List messages that have been in the queue longer than +\fItime\fR. Time should of the form: +.sp 1 +.RS +.RS +(<number>(s|m|h|d))+ +.sp 1 +.RE +.RE +.RS 14 +The second portion of the above definition +specifies seconds, minutes, hours, or +days, respectfully. The first portion is the number of +those units. There can be any number of such specifiers. +As an example, 1h30m means 1 hour and 30 minutes. +.sp 1 +The default is 2 hours. +.RE +.IP \fB-s\fR 14 +Print a summary of messages that have been queued longer than +the specified or default time. Two separate types of summaries are printed. +The first summarizes the queue messages by destination host. The host name +is gleaned from the recipient addresses for each message. +Thus the actual host names for this summary should be taken with a grain +of salt since ruleset 0 has not been applied to the address the host was +taken from nor were MX records consulted. It would be possible to add +this; however, the execution time of the script would increase +dramatically. The second summary is by status message. +.IP "\fB-u\fR \fIusers\fR" +Specify list of users to send a mail report to other than +the invoker. This option is only valid when \fB-m\fR has been +specified. Multiple recipients may be separated by spaces. +.IP "\fB-w\fR \fIwidth\fR" +Specify the page width to which the output should tailored. \fIwidth\fR +should be an integer representing some character position. The default is +80 or some other site configured value. Output is folded neatly to match +\fIwidth\fR. +.SH EXAMPLES +.nf +% \fBdate\fR +Tue Jan 19 12:07:20 CST 1993 +X +% \fBcqueue -t 21h45m -w 70\fR +X +Summary of messages in queue longer than 21:45:00 by destination +host: +X +X Number of +X Messages Destination Host +X --------- ---------------- +X 2 cigseg.rtsg.mot.com +X 1 mnesouth.corp.mot.com +X --------- +X 3 +X +Summary of messages in queue longer than 21:45:00 by status message: +X +X Number of +X Messages Status Message +X --------- -------------- +X 1 Deferred: Connection refused by mnesouth.corp.mot.com +X 2 Deferred: Host Name Lookup Failure +X --------- +X 3 +X +Detail of messages in queue longer than 21:45:00 sorted by creation +date: +X +X ID: AA20573 +X Date: 02:09:27 PM 01/18/93 +X Sender: melrose-place-owner@ferkel.ucsb.edu +X Recipient: pbaker@cigseg.rtsg.mot.com +X Status: Deferred: Host Name Lookup Failure +X +X ID: AA20757 +X Date: 02:11:30 PM 01/18/93 +X Sender: 90210-owner@ferkel.ucsb.edu +X Recipient: pbaker@cigseg.rtsg.mot.com +X Status: Deferred: Host Name Lookup Failure +X +X ID: AA21110 +X Date: 02:17:01 PM 01/18/93 +X Sender: rd_lap_wg@mdd.comm.mot.com +X Recipient: jim_mathis@mnesouth.corp.mot.com +X Status: Deferred: Connection refused by mnesouth.corp.mot.com +.fi +.SH AUTHOR +.nf +Michael S. Muegel (mmuegel@mot.com) +UNIX Applications Startup Group +Corporate Information Office, Schaumburg, IL +Motorola, Inc. +.fi +.SH COPYRIGHT NOTICE +Copyright 1993, Motorola, Inc. +.sp 1 +Permission to use, copy, modify and distribute without charge this +software, documentation, etc. is granted, provided that this +comment and the author's name is retained. The author nor Motorola assume any +responsibility for problems resulting from the use of this software. +.SH SEE ALSO +.nf +\fBsendmail(8)\fR +\fISendmail Installation and Operation Guide\fR. +.fi +SHAR_EOF +chmod 0444 man/cqueue.1 || +echo 'restore of man/cqueue.1 failed' +Wc_c="`wc -c < 'man/cqueue.1'`" +test 5212 -eq "$Wc_c" || + echo 'man/cqueue.1: original size 5212, current size' "$Wc_c" +fi +# ============= man/postclip.1 ============== +if test -f 'man/postclip.1' -a X"$1" != X"-c"; then + echo 'x - skipping man/postclip.1 (File already exists)' +else +echo 'x - extracting man/postclip.1 (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'man/postclip.1' && +.TH POSTCLIP 1L +\" +\" mmuegel +\" /usr/local/ustart/src/mail-tools/dist/foo/man/postclip.1,v 1.1 1993/07/28 08:08:25 mmuegel Exp +\" +.ds mp \fBpostclip\fR +.SH NAME +\*(mp - send only the headers to Postmaster +.SH SYNOPSIS +\*(mp [ \fB-v\fR ] [ \fIto\fR ... ] +.SH DESCRIPTION +\*(mp will forward non-delivery reports to a postmaster after deleting the body +of the message. This keeps bounced mail private and helps to avoid disk space problems. \*(mp tries its best to keep as much of the header trail as possible. +Hopefully only the original body of the message will be filtered. Only messages +that have a subject that begins with 'Returned mail:' are filtered. This +ensures that other mail is not accidently mucked with. Finally, note that +\fBsendmail\fR is used to deliver the message after it has been (possibly) +filtered. All of the original headers will remain intact. +.sp 1 +You can use this with any \fBsendmail\fR by modifying the Postmaster alias. +If you use IDA \fBsendmail\fR you could add the following to <machine>.m4: +.sp 1 +.RS +define(POSTMASTERBOUNCE, mailer-errors) +.RE +.sp 1 +In the aliases file, add a line similar to the following: +.sp 1 +.RS +mailer-errors: "|/usr/local/bin/postclip postmaster" +.RE +.SH OPTIONS +.IP \fB-v\fR +Be verbose about delivery. Probably only useful when debugging \*(mp. +.IP \fIto\fR +A list of one or more e-mail ids to send the modified +Postmaster messages to. If none are specified postmaster +is used. +.SH AUTHOR +.nf +Michael S. Muegel (mmuegel@mot.com) +UNIX Applications Startup Group +Corporate Information Office, Schaumburg, IL +Motorola, Inc. +.fi +.SH CREDITS +The original idea to filter Postmaster mail was taken from a script by +Christopher Davis <ckd@eff.org>. +.SH COPYRIGHT NOTICE +Copyright 1992, Motorola, Inc. +.sp 1 +Permission to use, copy, modify and distribute without charge this +software, documentation, etc. is granted, provided that this +comment and the author's name is retained. The author nor Motorola assume any +responsibility for problems resulting from the use of this software. +.SH SEE ALSO +.nf +\fBsendmail(8)\fR +.fi +SHAR_EOF +chmod 0444 man/postclip.1 || +echo 'restore of man/postclip.1 failed' +Wc_c="`wc -c < 'man/postclip.1'`" +test 2078 -eq "$Wc_c" || + echo 'man/postclip.1: original size 2078, current size' "$Wc_c" +fi +# ============= src/cqueue ============== +if test ! -d 'src'; then + echo 'x - creating directory src' + mkdir 'src' +fi +if test -f 'src/cqueue' -a X"$1" != X"-c"; then + echo 'x - skipping src/cqueue (File already exists)' +else +echo 'x - extracting src/cqueue (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'src/cqueue' && +#!/usr/local/ustart/bin/suidperl +X +# NAME +# cqueue - check sendmail queue for problems +# +# SYNOPSIS +# Type cqueue -usage +# +# AUTHOR +# Michael S. Muegel <mmuegel@mot.com> +# +# RCS INFORMATION +# mmuegel +# /usr/local/ustart/src/mail-tools/dist/foo/src/cqueue,v 1.1 1993/07/28 08:09:02 mmuegel Exp +X +# So that date.pl does not yell (Domain/OS version does a ``) +$ENV{'PATH'} = ""; +X +# A better getopts routine +require "newgetopts.pl"; +require "timespec.pl"; +require "mail.pl"; +require "date.pl"; +require "mqueue.pl"; +require "strings1.pl"; +require "elapsed.pl"; +X +($Script_Name = $0) =~ s/.*\///; +X +# Some defaults you may want to change +$DEF_TIME = "2h"; +$DEF_QUEUE = "/usr/spool/mqueue"; +$DEF_COLUMNS = 80; +$DATE_FORMAT = "%r %D"; +X +# Constants that probably should not be changed +$USAGE = "Usage: $Script_Name [ -abdms ] [ -q queue-dir ] [ -t time ] [ -u user ] [ -w width ]\n"; +$VERSION = "${Script_Name} by mmuegel; 1.1 of 1993/07/28 08:09:02"; +$SWITCHES = "abdmst:u:q:w:"; +$SPLIT_EXPR = '\s,\.@!%:'; +$ADDR_PART_EXPR = '[^!@%]+'; +X +# Let getopts parse for switches +$Status = &New_Getopts ($SWITCHES, $USAGE); +exit (0) if ($Status == -1); +exit (1) if (! $Status); +X +# Check args +die "${Script_Name}: -u only valid with -m\n" if (($opt_u) && (! $opt_m)); +die "${Script_Name}: -a not valid with -t option\n" if ($opt_a && $opt_t); +$opt_u = getlogin || (getpwuid ($<))[0] || $ENV{"USER"} || die "${Script_Name}: can not determine who you are!\n" if (! $opt_u); +X +# Set defaults +$opt_t = "0s" if ($opt_a); +$opt_t = $DEF_TIME if ($opt_t eq ""); +$opt_w = $DEF_COLUMNS if ($opt_w eq ""); +$opt_q = $DEF_QUEUE if ($opt_q eq ""); +$opt_s = $opt_d = 1 if (! ($opt_s || $opt_d)); +X +# Untaint the users to mail to +$opt_u =~ /^(.*)$/; +$Users = $1; +X +# Convert time option to seconds and seconds to elapsed form +die "${Script_Name}: $Msg\n" if (! (($Status, $Msg, $Seconds) = &Time_Spec_To_Seconds ($opt_t))[0]); +$Elapsed = &Seconds_To_Elapsed ($Seconds, 1); +$Time_Info = " longer than $Elapsed" if ($Seconds); +X +# Get the current time +$Current_Time = time; +$Current_Date = &date ($Current_Time, $DATE_FORMAT); +X +($Status, $Msg, @Queue_IDs) = &Get_Queue_IDs ($opt_q, 1, @Missing_Control_IDs, +X @Missing_Data_IDs); +die "$Script_Name: $Msg\n" if (! $Status); +X +# Yell about missing data/control files? +if ($opt_b) +{ +X +X $Report = "\nMessages missing control files:\n\n " . +X join ("\n ", @Missing_Control_IDs) . +X "\n" +X if (@Missing_Control_IDs); +X +X $Report .= "\nMessages missing data files:\n\n " . +X join ("\n ", @Missing_Data_IDs) . +X "\n" +X if (@Missing_Data_IDs); +}; +X +# See if any mail messages are older than $Seconds +foreach $Queue_ID (@Queue_IDs) +{ +X # Get lots of info about this sendmail message via the control file +X ($Status, $Msg) = &Parse_Control_File ($opt_q, $Queue_ID, *Sender, +X *Recipients, *Errors_To, *Creation_Time, *Priority, *Status_Message, +X *Headers); +X next if ($Status == -1); +X if (! $Status) +X { +X warn "$Script_Name: $Queue_ID: $Msg\n"; +X next; +X }; +X +X # Report on message if it is older than $Seconds +X if ($Current_Time - $Creation_Time >= $Seconds) +X { +X # Build summary by host information. Keep track of each host destination +X # encountered. +X if ($opt_s) +X { +X %Host_Map = (); +X foreach (@Recipients) +X { +X if ((/@($ADDR_PART_EXPR)$/) || (/($ADDR_PART_EXPR)!$ADDR_PART_EXPR$/)) +X { +X ($Host = $1) =~ tr/A-Z/a-z/; +X $Host_Map {$Host} = 1; +X } +X else +X { +X warn "$Script_Name: could not find host part from $_; contact author\n"; +X }; +X }; +X +X # For each unique target host add to its stats +X grep ($Host_Queued {$_}++, keys (%Host_Map)); +X +X # Build summary by message information. +X $Message_Queued {$Status_Message}++ if ($Status_Message); +X }; +X +X # Build long report information for this creation time (there may be +X # more than one message created at the same time) +X if ($opt_d) +X { +X $Creation_Date = &date ($Creation_Time, $DATE_FORMAT); +X $Recipient_Info = &Format_Text_Block (join (", ", @Recipients), +X " Recipient: ", 1, 0, $opt_w, $SPLIT_EXPR); +X $Time_To_Report {$Creation_Time} .= <<"EOS"; +X +X ID: $Queue_ID +X Date: $Creation_Date +X Sender: $Sender +$Recipient_Info +EOS +X +X # Add the status message if available to long report +X if ($Status_Message) +X { +X $Time_To_Report {$Creation_Time} .= &Format_Text_Block ($Status_Message, +X " Status: ", 1, 0, $opt_w, $SPLIT_EXPR) . "\n"; +X }; +X }; +X }; +X +}; +X +# Add the summary report by target host? +if ($opt_s) +{ +X foreach $Host (sort (keys (%Host_Queued))) +X { +X $Host_Report .= &Format_Text_Block ($Host, +X sprintf (" %-9d ", $Host_Queued{$Host}), 1, 0, $opt_w, +X $SPLIT_EXPR) . "\n"; +X $Num_Hosts += $Host_Queued{$Host}; +X }; +X if ($Host_Report) +X { +X chop ($Host_Report); +X $Report .= &Format_Text_Block("\nSummary of messages in queue$Time_Info by destination host:\n", "", 0, 0, $opt_w); +X +X $Report .= <<"EOS"; +X +X Number of +X Messages Destination Host +X --------- ---------------- +$Host_Report +X --------- +X $Num_Hosts +EOS +X }; +}; +X +# Add the summary by message report? +if ($opt_s) +{ +X foreach $Message (sort (keys (%Message_Queued))) +X { +X $Message_Report .= &Format_Text_Block ($Message, +X sprintf (" %-9d ", $Message_Queued{$Message}), 1, 0, $opt_w, +X $SPLIT_EXPR) . "\n"; +X $Num_Messages += $Message_Queued{$Message}; +X }; +X if ($Message_Report) +X { +X chop ($Message_Report); +X $Report .= &Format_Text_Block ("\nSummary of messages in queue$Time_Info by status message:\n", "", 0, 0, $opt_w); +X +X $Report .= <<"EOS"; +X +X Number of +X Messages Status Message +X --------- -------------- +$Message_Report +X --------- +X $Num_Messages +EOS +X }; +}; +X +# Add the detailed message reports? +if ($opt_d) +{ +X foreach $Time (sort { $a <=> $b} (keys (%Time_To_Report))) +X { +X $Report .= &Format_Text_Block ("\nDetail of messages in queue$Time_Info sorted by creation date:\n","", 0, 0, $opt_w) if (! $Detailed_Header++); +X $Report .= $Time_To_Report {$Time}; +X }; +}; +X +# Now mail or print the report +if ($Report) +{ +X $Report .= "\n"; +X if ($opt_m) +X { +X ($Status, $Msg) = &Send_Mail ($Users, "sendmail queue report for $Current_Date", $Report, 0); +X die "${Script_Name}: $Msg" if (! $Status); +X } +X +X else +X { +X print $Report; +X }; +X +}; +X +# I am outta here... +exit (0); +SHAR_EOF +chmod 0555 src/cqueue || +echo 'restore of src/cqueue failed' +Wc_c="`wc -c < 'src/cqueue'`" +test 6647 -eq "$Wc_c" || + echo 'src/cqueue: original size 6647, current size' "$Wc_c" +fi +# ============= src/postclip ============== +if test -f 'src/postclip' -a X"$1" != X"-c"; then + echo 'x - skipping src/postclip (File already exists)' +else +echo 'x - extracting src/postclip (Text)' +sed 's/^X//' << 'SHAR_EOF' > 'src/postclip' && +#!/usr/local/bin/perl +X +# NAME +# postclip - send only the headers to Postmaster +# +# SYNOPSIS +# postclip [ -v ] [ to ... ] +# +# AUTHOR +# Michael S. Muegel <mmuegel@mot.com> +# +# RCS INFORMATION +# /usr/local/ustart/src/mail-tools/dist/foo/src/postclip,v +# 1.1 of 1993/07/28 08:09:02 +X +# We use this to send off the mail +require "newgetopts.pl"; +require "mail.pl"; +X +# Get the basename of the script +($Script_Name = $0) =~ s/.*\///; +X +# Some famous constants +$USAGE = "Usage: $Script_Name [ -v ] [ to ... ]\n"; +$VERSION = "${Script_Name} by mmuegel; 1.1 of 1993/07/28 08:09:02"; +$SWITCHES = "v"; +X +# Let getopts parse for switches +$Status = &New_Getopts ($SWITCHES, $USAGE); +exit (0) if ($Status == -1); +exit (1) if (! $Status); +X +# Who should we send the modified mail to? +@ARGV = ("postmaster") if (! @ARGV); +$Users = join (" ", @ARGV); +@ARGV = (); +X +# Suck in the original header and save a few interesting lines +while (<>) +{ +X $Buffer .= $_ if (! /^From /); +X $Subject = $1 if (/^Subject:\s+(.*)$/); +X $From = $1 if (/^From:\s+(.*)$/); +X last if (/^$/); +}; +X +# Do not filter the message unless it has a subject and the subject indicates +# it is an NDN +if ($Subject && ($Subject =~ /^returned mail/i)) +{ +X # Slurp input by paragraph. Keep track of the last time we saw what +X # appeared to be NDN text. We keep this. +X $/ = "\n\n"; +X $* = 1; +X while (<>) +X { +X push (@Paragraphs, $_); +X $Last_Error_Para = $#Paragraphs +X if (/unsent message follows/i || /was not delivered because/); +X }; +X +X # Now save the NDN text into $Buffer +X $Buffer .= join ("", @Paragraphs [0..$Last_Error_Para]); +} +X +else +{ +X undef $/; +X $Buffer .= <>; +}; +X +# Send off the (possibly) modified mail +($Status, $Msg) = &Send_Mail ($Users, "", $Buffer, 0, $opt_v, 1); +die "$Script_Name: $Msg\n" if (! $Status); +SHAR_EOF +chmod 0555 src/postclip || +echo 'restore of src/postclip failed' +Wc_c="`wc -c < 'src/postclip'`" +test 1836 -eq "$Wc_c" || + echo 'src/postclip: original size 1836, current size' "$Wc_c" +fi +exit 0 + +-- ++----------------------------------------------------------------------------+ +| Michael S. Muegel | Internet E-Mail: mmuegel@mot.com | +| UNIX Applications Startup Group | Moto Dist E-Mail: X10090 | +| Corporate Information Office | Voice: (708) 576-0507 | +| Motorola | Fax: (708) 576-4153 | ++----------------------------------------------------------------------------+ + + "I'm disturbed, I'm depressed, I'm inadequate -- I've got it all!" + -- George from _Seinfeld_ diff --git a/usr.sbin/sendmail/contrib/oldbind.compat.c b/usr.sbin/sendmail/contrib/oldbind.compat.c new file mode 100644 index 0000000..1621a7b --- /dev/null +++ b/usr.sbin/sendmail/contrib/oldbind.compat.c @@ -0,0 +1,79 @@ +/* +** OLDBIND.COMPAT.C +** +** Very old systems do not have res_query(), res_querydomain() or +** res_search(), so emulate them here. +** +** You really ought to be upgrading to a newer version of BIND +** (4.8.2 or later) rather than be using this. +** +** J.R. Oldroyd <jr@inset.com> +*/ + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> + +typedef union +{ + HEADER qb1; + char qb2[PACKETSZ]; +} querybuf; + +res_query(dname, class, type, data, datalen) + char * dname; + int class; + int type; + char * data; + int datalen; +{ + int n; + querybuf buf; + + n = res_mkquery(QUERY, dname, class, type, (char *) NULL, 0, + NULL, (char *) &buf, sizeof buf); + n = res_send((char *)&buf, n, data, datalen); + + return n; +} + +res_querydomain(host, dname, class, type, data, datalen) + char * host; + char * dname; + int class; + int type; + char * data; + int datalen; +{ + int n; + querybuf buf; + char dbuf[256]; + + strcpy(dbuf, host); + if (dbuf[strlen(dbuf)-1] != '.') + strcat(dbuf, "."); + strcat(dbuf, dname); + n = res_mkquery(QUERY, dbuf, class, type, (char *) NULL, 0, + NULL, (char *)&buf, sizeof buf); + n = res_send((char *) &buf, n, data, datalen); + + return n; +} + +res_search(dname, class, type, data, datalen) + char * dname; + int class; + int type; + char * data; + int datalen; +{ + int n; + querybuf buf; + + n = res_mkquery(QUERY, dname, class, type, (char *)NULL, 0, + NULL, (char *) &buf, sizeof buf); + n = res_send((char *) &buf, n, data, datalen); + + return n; +} diff --git a/usr.sbin/sendmail/contrib/rcpt-streaming b/usr.sbin/sendmail/contrib/rcpt-streaming new file mode 100644 index 0000000..a43af6d --- /dev/null +++ b/usr.sbin/sendmail/contrib/rcpt-streaming @@ -0,0 +1,305 @@ +Message-ID: <wgKo1lW00WBw46OU8k@andrew.cmu.edu> +Date: Sun, 1 Aug 1993 00:02:57 -0400 (EDT) +From: John Gardiner Myers <jgm+@CMU.EDU> +To: sendmail@cs.berkeley.edu +Subject: contrib/rcpt-streaming +Beak: Is + +This patch implements "RCPT streaming" in sendmail version 8. 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/deliver.c /afs/andrew.cmu.edu/system/src/local/../host/oldsmail/016/src/deliver.c +*** ./src/deliver.c Thu Jul 22 14:28:19 1993 +--- /afs/andrew.cmu.edu/system/src/local/../host/oldsmail/016/src/deliver.c Fri Jul 30 21:11:16 1993 +*************** +*** 1334,1339 **** +--- 1334,1354 ---- + 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/usersmtp.c /afs/andrew.cmu.edu/system/src/local/../host/oldsmail/016/src/usersmtp.c +*** ./src/usersmtp.c Mon Jul 19 23:50:43 1993 +--- /afs/andrew.cmu.edu/system/src/local/../host/oldsmail/016/src/usersmtp.c Fri Jul 30 21:12:00 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,434 ---- + { + register int r; + ++ #ifdef RCPTSTREAM ++ sprintf(SmtpMsgBuffer, "RCPT To:<%s>", to->q_user); ++ #else + smtpmessage("RCPT To:<%s>", m, mci, to->q_user); ++ #endif + + SmtpPhase = mci->mci_phase = "client RCPT"; + setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); +*************** +*** 667,672 **** +--- 694,703 ---- + bool firstline = TRUE; + char junkbuf[MAXLINE]; + ++ #ifdef RCPTSTREAM ++ extern char MsgBuf[]; /* err.c */ ++ #endif ++ + if (mci->mci_out != NULL) + (void) fflush(mci->mci_out); + +*************** +*** 682,687 **** +--- 713,755 ---- + 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 */ +*************** +*** 792,797 **** +--- 860,937 ---- + + 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 (TrafficLogFile != NULL) ++ fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), 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 /afs/andrew.cmu.edu/system/src/local/../host/oldsmail/016/src: usersmtp.c~ +diff -cr ./src/util.c /afs/andrew.cmu.edu/system/src/local/../host/oldsmail/016/src/util.c +*** ./src/util.c Mon Jul 19 23:50:45 1993 +--- /afs/andrew.cmu.edu/system/src/local/../host/oldsmail/016/src/util.c Mon Jul 26 17:17:10 1993 +*************** +*** 1034,1039 **** +--- 1034,1091 ---- + 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) { ++ #ifdef FNONBIO ++ flags |= FNONBIO; ++ #else ++ flags |= O_NDELAY; ++ #endif ++ } ++ else { ++ #ifdef FNONBIO ++ flags &= ~FNONBIO; ++ #else ++ flags &= ~O_NDELAY; ++ #endif ++ } ++ fcntl(fd, F_SETFL, flags); ++ } ++ #endif + /* + ** STRCONTAINEDIN -- tell if one string is contained in another + ** diff --git a/usr.sbin/sendmail/contrib/xla/README b/usr.sbin/sendmail/contrib/xla/README new file mode 100644 index 0000000..a72fd03 --- /dev/null +++ b/usr.sbin/sendmail/contrib/xla/README @@ -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 diff --git a/usr.sbin/sendmail/contrib/xla/xla.c b/usr.sbin/sendmail/contrib/xla/xla.c new file mode 100644 index 0000000..627d383 --- /dev/null +++ b/usr.sbin/sendmail/contrib/xla/xla.c @@ -0,0 +1,532 @@ +/* + * (C) Copyright 1993-94, 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. + * + * Version: 940417, applied a patch from Paul Graham <pjg@acsu.buffalo.edu> + * (lockfile syntax in xla.c did not reflect anymore the + * new one used by sendmail, now fine). + * + */ + + +#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((char *) &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, NULL, 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, NULL, 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, NULL, 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, NULL, 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, NULL, 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, NULL, 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 */ |