X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/c2d0b04b06972188d897e6758ea49042b554cae7..fd88f5c5678c80ff5e338adc372d28a52ad20530:/usr/src/usr.sbin/sendmail/src/parseaddr.c diff --git a/usr/src/usr.sbin/sendmail/src/parseaddr.c b/usr/src/usr.sbin/sendmail/src/parseaddr.c index c4624c7f91..405486ea44 100644 --- a/usr/src/usr.sbin/sendmail/src/parseaddr.c +++ b/usr/src/usr.sbin/sendmail/src/parseaddr.c @@ -1,28 +1,42 @@ /* - * Copyright (c) 1983 Eric P. Allman + * Copyright (c) 1983, 1995 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * - * %sccs.include.redist.c% + * 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 University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR CONTRIBUTORS 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. */ #ifndef lint -static char sccsid[] = "@(#)parseaddr.c 8.19 (Berkeley) %G%"; +static char sccsid[] = "@(#)parseaddr.c 8.73 (Berkeley) 6/19/95"; #endif /* not lint */ -#include "sendmail.h" - -#ifdef CC_WONT_PROMOTE -static int toktype __P((char)); -#else /* !CC_WONT_PROMOTE */ -static int toktype __P((int)); /* char -> int */ -#endif /* CC_WONT_PROMOTE */ -static void _rewrite __P((char **, int)); -static void callsubr __P((char **)); -static ADDRESS * buildaddr __P((char **, ADDRESS *)); -static void uurelativize __P((const char *, const char *, char **)); - -char *DelimChar; /* set to point to the delimiter */ +# include "sendmail.h" /* ** PARSEADDR -- Parse an address @@ -75,6 +89,9 @@ parseaddr(addr, a, flags, delim, delimptr, e) auto char *delimptrbuf; bool queueup; char pvpbuf[PSBUFSIZE]; + extern ADDRESS *buildaddr(); + extern bool invalidaddr(); + extern void allocaddr __P((ADDRESS *, int, char *)); /* ** Initialize and prescan address. @@ -84,34 +101,10 @@ parseaddr(addr, a, flags, delim, delimptr, e) if (tTd(20, 1)) printf("\n--parseaddr(%s)\n", addr); - { - extern char *DelimChar; /* parseaddr.c */ - char savec; - bool invalid; - extern char *finddelim(); - extern bool invalidaddr(); - - DelimChar = finddelim(addr, delim); - savec = *DelimChar; - *DelimChar = '\0'; - invalid = invalidaddr(addr); - *DelimChar = savec; - if (invalid) - return (NULL); - } - if (delimptr == NULL) delimptr = &delimptrbuf; - if (strlen(addr) >= MAXNAME) - { - usrerr("Name too long, %d characters max", MAXNAME - 1); - if (tTd(20, 1)) - printf("parseaddr-->NULL\n"); - return NULL; - } - - pvp = prescan(addr, delim, pvpbuf, delimptr); + pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL); if (pvp == NULL) { if (tTd(20, 1)) @@ -140,7 +133,7 @@ parseaddr(addr, a, flags, delim, delimptr, e) if (savec != '\0') **delimptr = '\0'; - addr = newstr(addr); + e->e_to = addr = newstr(addr); if (savec != '\0') **delimptr = savec; } @@ -151,9 +144,9 @@ parseaddr(addr, a, flags, delim, delimptr, e) */ queueup = FALSE; - if (rewrite(pvp, 3, e) == EX_TEMPFAIL) + if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) queueup = TRUE; - if (rewrite(pvp, 0, e) == EX_TEMPFAIL) + if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL) queueup = TRUE; @@ -186,6 +179,7 @@ parseaddr(addr, a, flags, delim, delimptr, e) if (e->e_message == NULL) e->e_message = newstr(msg); a->q_flags |= QQUEUEUP; + a->q_status = "4.4.3"; } /* @@ -216,7 +210,7 @@ invalidaddr(addr, delimptr) register char *addr; char *delimptr; { - char savedelim; + char savedelim = '\0'; if (delimptr != NULL) { @@ -239,14 +233,14 @@ invalidaddr(addr, delimptr) } if (*addr == '\0') { - if (savedelim != '\0' && delimptr != NULL) + if (delimptr != NULL && savedelim != '\0') *delimptr = savedelim; return FALSE; } setstat(EX_USAGE); usrerr("553 Address contained invalid control characters"); addrfailure: - if (savedelim != '\0' && delimptr != NULL) + if (delimptr != NULL && savedelim != '\0') *delimptr = savedelim; return TRUE; } @@ -268,13 +262,14 @@ invalidaddr(addr, delimptr) ** Copies portions of a into local buffers as requested. */ +void allocaddr(a, flags, paddr) register ADDRESS *a; int flags; char *paddr; { if (tTd(24, 4)) - printf("allocaddr(flags=%o, paddr=%s)\n", flags, paddr); + printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr); a->q_paddr = paddr; @@ -294,45 +289,6 @@ allocaddr(a, flags, paddr) a->q_paddr = a->q_user; } /* -** INVALIDADDR -- check an address string for invalid control characters. -** -** Parameters: -** addr -- address string to be checked. -** -** Returns: -** TRUE if address string could cause problems, FALSE o/w. -** -** Side Effects: -** ExitStat may be changed and an error message generated. -*/ - -bool -invalidaddr(addr) - const char *addr; -{ - register const char *cp; - - /* make sure error messages don't have garbage on them */ - errno = 0; - - /* - ** Sendmail reserves characters 020 - 036 for rewriting rules - ** which can cause havoc (e.g. infinite rewriting loops) if - ** one shows up at the wrong time. If any of these characters - ** appear in an address, the address is deemed "invalid" and - ** an error message is generated. - */ - - for (cp = addr; *cp; cp++) - if ((*cp >= MATCHZANY && *cp <= HOSTEND) || *cp == '\001') - { - setstat(EX_USAGE); - usrerr("address contained invalid control char(s)"); - return (TRUE); - } - return (FALSE); -} - /* ** PRESCAN -- Prescan name and make it canonical ** ** Scans a name and turns it into a set of tokens. This process @@ -355,8 +311,11 @@ invalidaddr(addr) ** If '\t' then we are reading the .cf file. ** pvpbuf -- place to put the saved text -- note that ** the pointers are static. +** pvpbsize -- size of pvpbuf. ** delimptr -- if non-NULL, set to the location of the ** terminating delimiter. +** toktab -- if set, a token table to use for parsing. +** If NULL, use the default table. ** ** Returns: ** A pointer to a vector of tokens. @@ -369,8 +328,9 @@ invalidaddr(addr) # define QST 2 /* in quoted string */ # define SPC 3 /* chewing up spaces */ # define ONE 4 /* pick up one character */ +# define ILL 5 /* illegal character */ -# define NSTATES 5 /* number of states */ +# define NSTATES 6 /* number of states */ # define TYPE 017 /* mask to select state type */ /* meta bits for table */ @@ -380,22 +340,101 @@ invalidaddr(addr) static short StateTab[NSTATES][NSTATES] = { - /* oldst chtype> OPR ATM QST SPC ONE */ - /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, - /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B, - /*QST*/ QST, QST, OPR, QST, QST, - /*SPC*/ OPR, ATM, QST, SPC|M, ONE, - /*ONE*/ OPR, OPR, OPR, OPR, OPR, + /* oldst chtype> OPR ATM QST SPC ONE ILL */ + /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB, + /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB, + /*QST*/ QST, QST, OPR, QST, QST, QST, + /*SPC*/ OPR, ATM, QST, SPC|M, ONE, ILL|MB, + /*ONE*/ OPR, OPR, OPR, OPR, OPR, ILL|MB, + /*ILL*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M, +}; + +/* token type table -- it gets modified with $o characters */ +static u_char TokTypeTab[256] = +{ + /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, + /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* sp ! " # $ % & ' ( ) * + , - . / */ + SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM, + /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* @ A B C D E F G H I J K L M N O */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* P Q R S T U V W X Y Z [ \ ] ^ _ */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* ` a b c d e f g h i j k l m n o */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* p q r s t u v w x y z { | } ~ del */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + + /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ + OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, + /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ + OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, + /* sp ! " # $ % & ' ( ) * + , - . / */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* @ A B C D E F G H I J K L M N O */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* P Q R S T U V W X Y Z [ \ ] ^ _ */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* ` a b c d e f g h i j k l m n o */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* p q r s t u v w x y z { | } ~ del */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, }; +/* token type table for MIME parsing */ +u_char MimeTokenTab[256] = +{ + /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ + ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL, + /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ + ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, + /* sp ! " # $ % & ' ( ) * + , - . / */ + SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,OPR,ATM,ATM,OPR, + /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR, + /* @ A B C D E F G H I J K L M N O */ + OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* P Q R S T U V W X Y Z [ \ ] ^ _ */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM, + /* ` a b c d e f g h i j k l m n o */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + /* p q r s t u v w x y z { | } ~ del */ + ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, + + /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ + ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, + /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ + ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, + /* sp ! " # $ % & ' ( ) * + , - . / */ + ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, + /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ + ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, + /* @ A B C D E F G H I J K L M N O */ + ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, + /* P Q R S T U V W X Y Z [ \ ] ^ _ */ + ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, + /* ` a b c d e f g h i j k l m n o */ + ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, + /* p q r s t u v w x y z { | } ~ del */ + ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, +}; + + # define NOCHAR -1 /* signal nothing in lookahead token */ char ** -prescan(addr, delim, pvpbuf, delimptr) +prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) char *addr; - char delim; + int delim; char pvpbuf[]; char **delimptr; + u_char *toktab; { register char *p; register char *q; @@ -409,6 +448,25 @@ prescan(addr, delim, pvpbuf, delimptr) int newstate; char *saveto = CurEnv->e_to; static char *av[MAXATOM+1]; + static char firsttime = TRUE; + extern int errno; + + if (firsttime) + { + /* initialize the token type table */ + char obuf[50]; + + firsttime = FALSE; + expand("\201o", obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv); + strcat(obuf, DELIMCHARS); + for (p = obuf; *p != '\0'; p++) + { + if (TokTypeTab[*p & 0xff] == ATM) + TokTypeTab[*p & 0xff] = OPR; + } + } + if (toktab == NULL) + toktab = TokTypeTab; /* make sure error messages don't have garbage on them */ errno = 0; @@ -439,9 +497,12 @@ prescan(addr, delim, pvpbuf, delimptr) if (c != NOCHAR && !bslashmode) { /* see if there is room */ - if (q >= &pvpbuf[PSBUFSIZE - 5]) + if (q >= &pvpbuf[pvpbsize - 5]) { usrerr("553 Address too long"); + if (strlen(addr) > MAXNAME) + addr[MAXNAME] = '\0'; + returnnull: if (delimptr != NULL) *delimptr = p; CurEnv->e_to = saveto; @@ -507,7 +568,7 @@ prescan(addr, delim, pvpbuf, delimptr) { bslashmode = TRUE; } - if (state == QST) + else if (state == QST) { /* do nothing, just avoid next clauses */ } @@ -542,20 +603,6 @@ prescan(addr, delim, pvpbuf, delimptr) } else if (delim == ' ' && isascii(c) && isspace(c)) c = ' '; - else if (c == ':' && !CurEnv->e_oldstyle) - { - /* consume characters until a semicolon */ - while (*p != '\0' && *p != ';') - p++; - if (*p == '\0') - usrerr("Unbalanced ':...;' group spec"); - else - p++; - c = ' '; - } - - else if (c == ';') /* semicolons are not tokens */ - c = NOCHAR; if (c == NOCHAR) continue; @@ -564,10 +611,17 @@ prescan(addr, delim, pvpbuf, delimptr) if (c == delim && anglecnt <= 0 && state != QST) break; - newstate = StateTab[state][toktype(c)]; + newstate = StateTab[state][toktab[c & 0xff]]; if (tTd(22, 101)) printf("ns=%02o\n", newstate); state = newstate & TYPE; + if (state == ILL) + { + if (isascii(c) && isprint(c)) + usrerr("653 Illegal character %c", c); + else + usrerr("653 Illegal character 0x%02x", c); + } if (bitset(M, newstate)) c = NOCHAR; if (bitset(B, newstate)) @@ -587,10 +641,12 @@ prescan(addr, delim, pvpbuf, delimptr) if (avp >= &av[MAXATOM]) { syserr("553 prescan: too many tokens"); - if (delimptr != NULL) - *delimptr = p; - CurEnv->e_to = saveto; - return (NULL); + goto returnnull; + } + if (q - tok > MAXNAME) + { + syserr("553 prescan: token too long"); + goto returnnull; } *avp++ = tok; } @@ -614,53 +670,6 @@ prescan(addr, delim, pvpbuf, delimptr) return (av); } /* -** TOKTYPE -- return token type -** -** Parameters: -** c -- the character in question. -** -** Returns: -** Its type. -** -** Side Effects: -** none. -*/ - -static int -toktype(c) - register int c; -{ - static char buf[50]; - static bool firstime = TRUE; - - if (firstime) - { - firstime = FALSE; - expand("\201o", buf, &buf[sizeof buf - 1], CurEnv); - (void) strcat(buf, DELIMCHARS); - } - c &= 0377; - if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS) - return (ONE); - if (c == MACRODEXPAND) - return (ONE); -#ifdef MACVALUE - if (c == MACVALUE) - return (ONE); -#endif /* MACVALUE */ - if (c == '"') - return (QST); - if ((c & 0340) == 0200) - return (OPR); - if (!isascii(c)) - return (ATM); - if (isspace(c) || c == ')') - return (SPC); - if (strchr(buf, c) != NULL) - return (OPR); - return (ATM); -} - /* ** REWRITE -- apply rewrite rules to token vector. ** ** This routine is an ordered production system. Each rewrite @@ -685,6 +694,7 @@ toktype(c) ** Parameters: ** pvp -- pointer to token vector. ** ruleset -- the ruleset to use for rewriting. +** reclevel -- recursion level (to catch loops). ** e -- the current envelope. ** ** Returns: @@ -695,53 +705,26 @@ toktype(c) ** pvp is modified. */ -# define OP_NONZLEN 00001 -# define OP_VARLEN 00002 -# define OP_CLASS 00004 -# define OP_EXACT 00010 - struct match { char **first; /* first token matched */ char **last; /* last token matched */ char **pattern; /* pointer to pattern */ - char **source; /* left hand source operand */ - char flags; /* attributes of this operator */ }; # define MAXMATCH 9 /* max params per rewrite */ -# define MAX_CONTROL ' ' - -static char control_opts[MAX_CONTROL]; -int -static char control_init_data[] = { - MATCHZANY, OP_VARLEN, - MATCHONE, OP_NONZLEN, - MATCHANY, OP_VARLEN|OP_NONZLEN, -#ifdef MACVALUE - MACVALUE, OP_EXACT, -#endif /* MACVALUE */ - MATCHNCLASS, OP_NONZLEN, - MATCHCLASS, OP_NONZLEN|OP_VARLEN|OP_CLASS, -}; +# ifndef MAXRULERECURSION +# define MAXRULERECURSION 50 /* max recursion depth */ +# endif -static int nrw; -void -rewrite(pvp, ruleset, e) +int +rewrite(pvp, ruleset, reclevel, e) char **pvp; int ruleset; + int reclevel; register ENVELOPE *e; -{ - nrw = 0; - _rewrite(pvp, ruleset); -} - -static void -_rewrite(pvp, ruleset) - char **pvp; - int ruleset; { register char *ap; /* address pointer */ register char *rp; /* rewrite pointer */ @@ -752,45 +735,30 @@ _rewrite(pvp, ruleset) int ruleno; /* current rule number */ int rstat = EX_OK; /* return status */ int loopcount; - int subr; /* subroutine number if >= 0 */ - bool dolookup; /* do host aliasing */ + struct match mlist[MAXMATCH]; /* stores match on LHS */ char *npvp[MAXATOM+1]; /* temporary space for rebuild */ - char tokbuf[MAXNAME+1]; /* for concatenated class tokens */ - int nloops, nmatches = 0; /* for looping rule checks */ - struct rewrite *prev_rwr; /* pointer to previous rewrite rule */ - struct match mlist[MAXMATCH+1]; /* stores match on LHS */ - struct match *old_mlp; /* to save our place */ - bool extend_match; /* extend existing match during backup */ - - if (OpMode == MD_TEST || tTd(21, 2)) + + if (OpMode == MD_TEST || tTd(21, 1)) { printf("rewrite: ruleset %2d input:", ruleset); - printcav(pvp); + printav(pvp); } if (ruleset < 0 || ruleset >= MAXRWSETS) { syserr("554 rewrite: illegal ruleset number %d", ruleset); return EX_CONFIG; } - if (pvp == NULL) - return EX_USAGE; - - if (++nrw > 100) + if (reclevel++ > MAXRULERECURSION) { - char buf[MAXLINE]; - - buf[0] = buf[MAXLINE-1] = 0; - while (*pvp) - (void) strncat(buf, *pvp++, sizeof buf); - syserr("address causes rewrite loop: <%s>", buf); - return; + syserr("rewrite: infinite recursion, ruleset %d", ruleset); + return EX_CONFIG; } - - /* Be sure to recognize first rule as new */ - prev_rwr = NULL; + if (pvp == NULL) + return EX_USAGE; /* - ** Run through the list of rewrite rules, applying any that match. + ** Run through the list of rewrite rules, applying + ** any that match. */ ruleno = 1; @@ -800,57 +768,15 @@ _rewrite(pvp, ruleset) if (tTd(21, 12)) { printf("-----trying rule:"); - printcav(rwr->r_lhs); - } - - /* - ** Set up the match list. This is done once for each - ** rule. If a rule is used repeatedly, the list need not - ** be set up the next time. - */ - - if (rwr != prev_rwr) - { - prev_rwr = rwr; - for (rvp = rwr->r_lhs, mlp = mlist; - *rvp && (mlp < &mlist[MAXMATCH]); rvp++) - { - mlp->flags = ((unsigned char) **rvp >= MAX_CONTROL) ? - 0 : control_opts[**rvp] ; - if (mlp->flags) - { - mlp->source = rvp; - mlp++; - } - } - if (*rvp) - { - syserr("Too many variables on LHS in ruleset %d", ruleset); - return; - } - mlp->source = rvp; - - /* Make sure end marker is initialized */ - mlp->flags = 0; + printav(rwr->r_lhs); } /* try to match on this rule */ mlp = mlist; - rvp = rwr->r_lhs; avp = pvp; - nloops = 0; - extend_match = FALSE; - if (++loopcount > 100) { - if (nloops++ > 400) - { - syserr("Looping on ruleset %d, rule %d", - ruleset, rwr-RewriteRules[ruleset]); - mlp = mlist - 1; /* force rule failure */ - break; - } syserr("554 Infinite loop in ruleset %d, rule %d", ruleset, ruleno); if (tTd(21, 1)) @@ -864,7 +790,6 @@ _rewrite(pvp, ruleset) while ((ap = *avp) != NULL || *rvp != NULL) { rp = *rvp; - if (tTd(21, 35)) { printf("ADVANCE rp="); @@ -873,90 +798,76 @@ _rewrite(pvp, ruleset) xputs(ap); printf("\n"); } - - if (extend_match) - extend_match = FALSE; - else - { - mlp->first = avp; - mlp->last = mlp->flags == 0 || (mlp->flags & OP_NONZLEN) ? - avp + 1 : avp; - } - if (rp == NULL) + { /* end-of-pattern before end-of-address */ goto backup; - - /* Premature end of address */ - if (ap == NULL && avp != mlp->last) - goto backup; - - /* - ** Simplest case - exact token comparison between - ** pattern and address. Such a match is not saved - ** in mlp. - */ - - if (rvp < mlp->source) + } + if (ap == NULL && (*rp & 0377) != MATCHZANY && + (*rp & 0377) != MATCHZERO) { - if (ap == NULL || strcasecmp(ap, rp)) - goto backup; - rvp++; - avp++; - continue; + /* end-of-input with patterns left */ + goto backup; } -#ifdef MACVALUE - /* - ** This is special case handled. The match is exact, - ** but might span zero or more address tokens. The - ** result is saved in mlp. - */ - - if (*rp == MACVALUE) + switch (*rp & 0377) { - int len; - rp = macvalue(rp[1], CurEnv); + char buf[MAXLINE]; - if (rp) - while (*rp) + case MATCHCLASS: + /* match any phrase in a class */ + mlp->pattern = rvp; + mlp->first = avp; + extendclass: + ap = *avp; + if (ap == NULL) + goto backup; + mlp->last = avp++; + cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0'); + if (!wordinclass(buf, rp[1])) + { + if (tTd(21, 36)) { - if (*avp == NULL || strncasecmp(rp,*avp,len = strlen(*avp))) - goto backup; - rp += len; - avp++; + printf("EXTEND rp="); + xputs(rp); + printf(", ap="); + xputs(ap); + printf("\n"); } - mlp->last = avp; - rvp++; + goto extendclass; + } + if (tTd(21, 36)) + printf("CLMATCH\n"); mlp++; - continue; - } -#endif /* MACVALUE */ + break; - /* - ** All other matches are saved in mlp. Initially - ** assume they match at the shortest possible length - ** for this pattern. Variable patterns will be - ** extended later as needed. - */ + case MATCHNCLASS: + /* match any token not in a class */ + if (wordinclass(ap, rp[1])) + goto backup; - /* Fixed length first */ - if (!(mlp->flags & OP_VARLEN)) - { - switch (*rp) - { - break; - } + /* fall through */ + + case MATCHONE: + case MATCHANY: + /* match exactly one token */ + mlp->pattern = rvp; + mlp->first = avp; + mlp->last = avp++; + mlp++; + break; - avp = mlp->last; - rvp++; + case MATCHZANY: + /* match zero or more tokens */ + mlp->pattern = rvp; + mlp->first = avp; + mlp->last = avp - 1; mlp++; break; case MATCHZERO: /* match zero tokens */ - continue; - } + break; case MACRODEXPAND: /* @@ -991,210 +902,76 @@ _rewrite(pvp, ruleset) /* match */ break; - /* - ** We now have a variable length item. It could - ** be $+ or $* in which case no special checking - ** is needed. But a class match such as $=x must - ** be verified. - ** - ** As a speedup, if a variable length item is - ** followed by a plain character token, we initially - ** extend the match to the first such token we find. - ** If the required character token cannot be found, - ** we fail the match at this point. - */ - - avp = mlp->last; - - /* If next token is char token */ - if (&rvp[1] < mlp[1].source) - { - while (*avp && strcasecmp(*avp, rvp[1])) - avp++; - - /* - ** If we can't find the proper ending token, - ** leave avp point to NULL. This indicates - ** we have run out of address tokens. It is - ** pointless to advance the beginning of this - ** match and retry. - */ - - if (*avp == NULL) + default: + /* must have exact match */ + if (strcasecmp(rp, ap)) goto backup; - mlp->last = avp; - } - else if (rvp[1] == NULL) - /* next token is end of address */ - { - while (*avp) - avp++; - mlp->last = avp; - } - - if (mlp->flags & OP_CLASS) - { - register char *cp = tokbuf; - - avp = mlp->first; - strcpy(cp, *avp); avp++; - for (;;) - { - while (avp < mlp->last) - { - while (*cp) - cp++; - strcpy(cp, *avp); - avp++; - } - switch (*rp) - { - register STAB *s; - - case MATCHCLASS: - s = stab(tokbuf, ST_CLASS, ST_FIND); - if (s != NULL && bitnset(rp[1], s->s_class)) - goto have_match; - break; - } - - /* - ** Class match initially failed. - ** Extend the tentative match. - ** Again, if followed by a character - ** token, extend all the way to that - ** token before checking. - */ - - if (*avp) - { - (mlp->last)++; - if (&rvp[1] < mlp[1].source) - { - while (*(mlp->last) && strcasecmp(*(mlp->last), rvp[1])) - (mlp->last)++; - if (*(mlp->last) == NULL) - avp = mlp->last; - } - } - if (*avp == NULL) - { - /* - ** We could not find the - ** ending token. But we had - ** found ending tokens before. - ** A match is still plausible - ** if the start of the - ** tentative match is advanced. - ** Hence we must not leave avp - ** pointing to NULL. - */ - avp = mlp->first; - goto backup; - } - } + break; } - have_match: + /* successful match on this token */ rvp++; - mlp++; continue; -backup: - /* We failed to match. mlp marks point of failure */ - - /* - ** There is a special case when we have exhausted - ** the address, but have not exhausted the pattern. - ** Under normal circumstances we could consider the - ** failure permanent, since extending the number of - ** address tokens matched by a '$+' or a '$*' will - ** only worsen the situation. - ** - ** There is an exception, however. It is possible - ** that we have matched a class token, say '$=x', - ** with three or more tokens. Extending a '$+' say, - ** which precedes the '$=x' will move the beginning - ** of the '$=x' match to the right, but it might match - ** a smaller number of tokens then, possibly - ** correcting the mismatch. - ** - ** Thus in this case we initially back up to the - ** $=x which matches three or more tokens. - */ - - if (*avp == NULL) + backup: + /* match failed -- back up */ + while (--mlp >= mlist) { rvp = mlp->pattern; - while (--mlp > mlist) + rp = *rvp; + avp = mlp->last + 1; + ap = *avp; + + if (tTd(21, 36)) { - if ((mlp->flags & OP_CLASS) && - mlp->last > 2 + mlp->first) - break; + printf("BACKUP rp="); + xputs(rp); + printf(", ap="); + xputs(ap); + printf("\n"); } - } - /* - ** Now backup till we find a match with a pattern - ** whose length is extendable, and extend that. - */ - - mlp--; - while (mlp >= mlist && !(mlp->flags & OP_VARLEN)) - mlp--; + if (ap == NULL) + { + /* run off the end -- back up again */ + continue; + } + if ((*rp & 0377) == MATCHANY || + (*rp & 0377) == MATCHZANY) + { + /* extend binding and continue */ + mlp->last = avp++; + rvp++; + mlp++; + break; + } + if ((*rp & 0377) == MATCHCLASS) + { + /* extend binding and try again */ + mlp->last = avp; + goto extendclass; + } + } - /* Total failure to match */ if (mlp < mlist) - break; - - avp = ++(mlp->last); - rvp = mlp->source; - - /* - ** We have found a backup point. Normally we would - ** increase the matched amount by one token, and - ** continue from the next item in the pattern. But - ** there are two special cases. If this is a - ** class-type match (OP_CLASS), we must test the - ** validity of the extended match. If this pattern - ** item is directly followed by a character token, it - ** is worth going back and locating the next such - ** character token before we continue on. - */ - if ((mlp->flags & OP_CLASS) || (&rvp[1] < mlp[1].source)) - { - avp = mlp->first; - extend_match = TRUE; - } - else { - mlp++; - rvp++; + /* total failure to match */ + break; } } /* - ** See if we successfully matched. + ** See if we successfully matched */ - if (mlp < mlist) + if (mlp < mlist || *rvp != NULL) { if (tTd(21, 10)) printf("----- rule fails\n"); rwr = rwr->r_next; ruleno++; loopcount = 0; - nmatches = 0; - continue; - } - - if (nmatches++ > 200) - { - syserr("Loop in ruleset %d, rule %d (too many matches)", - ruleset, rwr - RewriteRules[ruleset]); - rwr = rwr->r_next; - nmatches = 0; continue; } @@ -1202,7 +979,7 @@ backup: if (tTd(21, 12)) { printf("-----rule matches:"); - printcav(rvp); + printav(rvp); } rp = *rvp; @@ -1212,7 +989,6 @@ backup: rwr = rwr->r_next; ruleno++; loopcount = 0; - nmatches = 0; } else if ((*rp & 0377) == CANONHOST) { @@ -1223,27 +999,19 @@ backup: rwr = NULL; /* substitute */ - dolookup = FALSE; for (avp = npvp; *rvp != NULL; rvp++) { register struct match *m; register char **pp; rp = *rvp; - - /* check to see if we should do a lookup */ - if (*rp == MATCHLOOKUP) - dolookup = TRUE; - - /* see if there is substitution here */ - if (*rp == MATCHREPL && rp[1] >= '1' && rp[1] <= '9') + if ((*rp & 0377) == MATCHREPL) { /* substitute from LHS */ m = &mlist[rp[1] - '1']; if (m < mlist || m >= mlp) { - toolong: - syserr("rewrite: ruleset %d: replacement #%c out of bounds", + syserr("554 rewrite: ruleset %d: replacement $%c out of bounds", ruleset, rp[1]); return EX_CONFIG; } @@ -1251,7 +1019,7 @@ backup: { printf("$%c:", rp[1]); pp = m->first; - while (pp < m->last) + while (pp <= m->last) { printf(" %x=\"", *pp); (void) fflush(stdout); @@ -1260,10 +1028,13 @@ backup: printf("\n"); } pp = m->first; - while (pp < m->last) + while (pp <= m->last) { if (avp >= &npvp[MAXATOM]) - goto toolong; + { + syserr("554 rewrite: expansion too long"); + return EX_DATAERR; + } *avp++ = *pp++; } } @@ -1271,21 +1042,23 @@ backup: { /* vanilla replacement */ if (avp >= &npvp[MAXATOM]) - goto toolong; -#ifdef MACVALUE - if (*rp == MACVALUE) { - char *p = macvalue(rp[1], CurEnv); - - if (tTd(21, 2)) - printf("expanding runtime macro '%c' to \"%s\"\n", - rp[1], p ? p : "(null)"); - if (p) - *avp++ = p; + toolong: + syserr("554 rewrite: expansion too long"); + return EX_DATAERR; } - else -#endif /* MACVALUE */ + if ((*rp & 0377) != MACRODEXPAND) *avp++ = rp; + else + { + *avp = macvalue(rp[1], e); + if (tTd(21, 2)) + printf("rewrite: RHS $&%c => \"%s\"\n", + rp[1], + *avp == NULL ? "(NULL)" : *avp); + if (*avp != NULL) + avp++; + } } } *avp++ = NULL; @@ -1296,7 +1069,7 @@ backup: for (rvp = npvp; *rvp != NULL; rvp++) { - char **hbrvp, **ubrvp; + char **hbrvp; char **xpvp; int trsize; char *replac; @@ -1306,14 +1079,11 @@ backup: char **key_rvp; char **arg_rvp; char **default_rvp; - char hbuf[MAXNAME + 1], ubuf[MAXNAME + 1]; + char buf[MAXNAME + 1]; char *pvpb1[MAXATOM + 1]; char *argvect[10]; char pvpbuf[PSBUFSIZE]; char *nullpvp[1]; - bool match, defaultpart; - char begintype; - char db = '\0'; if ((**rvp & 0377) != HOSTBEGIN && (**rvp & 0377) != LOOKUPBEGIN) @@ -1325,9 +1095,7 @@ backup: ** This could be optimized fairly easily. */ - begintype = **rvp; hbrvp = rvp; - ubrvp = NULL; if ((**rvp & 0377) == HOSTBEGIN) { endtoken = HOSTEND; @@ -1395,139 +1163,146 @@ backup: trsize = (int) (avp - rvp + 1) * sizeof *rvp; bcopy((char *) rvp, (char *) pvpb1, trsize); - /* append it to the token list */ - for (avp = hbrvp; *xpvp != NULL; xpvp++) + /* look it up */ + cataddr(key_rvp, NULL, buf, sizeof buf, '\0'); + argvect[0] = buf; + if (map != NULL && bitset(MF_OPEN, map->s_map.map_mflags)) + { + auto int stat = EX_OK; + + if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags)) + stripquotes(buf); + + /* XXX should try to auto-open the map here */ + + if (tTd(60, 1)) + printf("map_lookup(%s, %s) => ", + mapname, buf); + replac = (*map->s_map.map_class->map_lookup)(&map->s_map, + buf, argvect, &stat); + if (tTd(60, 1)) + printf("%s (%d)\n", + replac ? replac : "NOT FOUND", + stat); + + /* should recover if stat == EX_TEMPFAIL */ + if (stat == EX_TEMPFAIL) { - *avp++ = newstr(*xpvp); - if (avp >= &npvp[MAXATOM]) - goto toolong; + rstat = EX_TEMPFAIL; + if (tTd(60, 1)) + printf("map_lookup(%s, %s) failed (stat = %d)\n", + mapname, buf, stat); + if (e->e_message == NULL) + { + char mbuf[300]; + + sprintf(mbuf, "map %s: lookup (%s) failed", + mapname, buf); + e->e_message = newstr(mbuf); + } } } else - avp = hbrvp; + replac = NULL; - /* restore the old trailing information */ - rvp = avp - 1; - for (xpvp = pvpb1; *xpvp != NULL; xpvp++) + /* if no replacement, use default */ + if (replac == NULL && default_rvp != NULL) + { + /* create the default */ + cataddr(default_rvp, NULL, buf, sizeof buf, '\0'); + replac = buf; + } + + if (replac == NULL) + { + xpvp = key_rvp; + } + else if (*replac == '\0') { - if (defaultpart && **xpvp == HOSTEND) + /* null replacement */ + nullpvp[0] = NULL; + xpvp = nullpvp; + } + else + { + /* scan the new replacement */ + xpvp = prescan(replac, '\0', pvpbuf, + sizeof pvpbuf, NULL, NULL); + if (xpvp == NULL) { - defaultpart = FALSE; - rvp = avp - 1; + /* prescan already printed error */ + return EX_DATAERR; } - else if (!defaultpart || !match) - *avp++ = *xpvp; + } + + /* append it to the token list */ + for (avp = hbrvp; *xpvp != NULL; xpvp++) + { + *avp++ = newstr(*xpvp); if (avp >= &npvp[MAXATOM]) goto toolong; } - *avp++ = NULL; - /*break;*/ + /* restore the old trailing information */ + for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) + if (avp >= &npvp[MAXATOM]) + goto toolong; + + break; } /* ** Check for subroutine calls. - ** Then copy vector back into original space. */ - callsubr(npvp); - - for (avp = npvp; *avp++ != NULL;); - subr = atoi(*++rvp); - rvp++; + if (*npvp != NULL && (**npvp & 0377) == CALLSUBR) + { + int stat; + if (npvp[1] == NULL) + { + syserr("parseaddr: NULL subroutine call in ruleset %d, rule %d", + ruleset, ruleno); + *pvp = NULL; + } + else + { + int ruleset; + STAB *s; + + bcopy((char *) &npvp[2], (char *) pvp, + (int) (avp - npvp - 2) * sizeof *avp); + if (tTd(21, 3)) + printf("-----callsubr %s\n", npvp[1]); + ruleset = strtorwset(npvp[1], NULL, ST_FIND); + stat = rewrite(pvp, ruleset, reclevel, e); + if (rstat == EX_OK || stat == EX_TEMPFAIL) + rstat = stat; + if (*pvp != NULL && (**pvp & 0377) == CANONNET) + rwr = NULL; + } + } else - subr = -1; - - /* - ** Copy result back to original string. - */ - - for (avp = pvp; *rvp != NULL; rvp++) - *avp++ = *rvp; - *avp = NULL; - - /* - ** If this specified a subroutine, call it. - */ - - if (subr >= 0) { -# ifdef DEBUG - if (tTd(21, 3)) - printf("-----callsubr %s\n", subr); -# endif DEBUG - rewrite(pvp, subr); + bcopy((char *) npvp, (char *) pvp, + (int) (avp - npvp) * sizeof *avp); } - - /* - ** Done with rewriting this pass. - */ - if (tTd(21, 4)) { printf("rewritten as:"); - printcav(pvp); + printav(pvp); } } - if (OpMode == MD_TEST || tTd(21, 2)) + if (OpMode == MD_TEST || tTd(21, 1)) { printf("rewrite: ruleset %2d returns:", ruleset); - printcav(pvp); + printav(pvp); } return rstat; } /* -** CALLSUBR -- call subroutines in rewrite vector -** -** Parameters: -** pvp -- pointer to token vector. -** -** Returns: -** none. -** -** Side Effects: -** pvp is modified. -*/ - -static void -callsubr(pvp) - char **pvp; -{ - char **rvp; - int subr; - - for (; *pvp != NULL; pvp++) - if (**pvp == CALLSUBR && pvp[1] != NULL && isdigit(pvp[1][0])) - { - subr = atoi(pvp[1]); - - if (tTd(21, 3)) - printf("-----callsubr %d\n", subr); - - /* - ** Take care of possible inner calls. - */ - callsubr(pvp+2); - - /* - ** Move vector up over calling opcode. - */ - for (rvp = pvp+2; *rvp != NULL; rvp++) - rvp[-2] = rvp[0]; - rvp[-2] = NULL; - - /* - ** Call inferior ruleset. - */ - _rewrite(pvp, subr); - - break; - } -} - /* ** BUILDADDR -- build address from token vector. ** ** Parameters: @@ -1565,7 +1340,7 @@ struct errcodes NULL, EX_UNAVAILABLE, }; -static ADDRESS * +ADDRESS * buildaddr(tv, a, flags, e) register char **tv; register ADDRESS *a; @@ -1574,24 +1349,29 @@ buildaddr(tv, a, flags, e) { struct mailer **mp; register struct mailer *m; - char *bp; - int spaceleft; + register char *p; + char *mname; + char **hostp; + char hbuf[MAXNAME + 1]; static MAILER errormailer; static char *errorargv[] = { "ERROR", NULL }; - static char buf[MAXNAME]; + static char ubuf[MAXNAME + 1]; if (tTd(24, 5)) { - printf("buildaddr, flags=%o, tv=", flags); + printf("buildaddr, flags=%x, tv=", flags); printav(tv); } if (a == NULL) a = (ADDRESS *) xalloc(sizeof *a); - clear((char *) a, sizeof *a); + bzero((char *) a, sizeof *a); + + /* set up default error return flags */ + a->q_flags |= QPINGONFAILURE|QPINGONDELAY; /* figure out what net/mailer to use */ - if (*tv == NULL || **tv != CANONNET) + if (*tv == NULL || (**tv & 0377) != CANONNET) { syserr("554 buildaddr: no net"); badaddr: @@ -1606,138 +1386,149 @@ badaddr: } return a; } - tv++; - if (strcasecmp(*tv, "error") == 0) + mname = *++tv; + + /* extract host and user portions */ + if ((**++tv & 0377) == CANONHOST) + hostp = ++tv; + else + hostp = NULL; + while (*tv != NULL && (**tv & 0377) != CANONUSER) + tv++; + if (*tv == NULL) + { + syserr("554 buildaddr: no user"); + goto badaddr; + } + if (hostp != NULL) + cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0'); + cataddr(++tv, NULL, ubuf, sizeof ubuf, '\0'); + + /* save away the host name */ + if (strcasecmp(mname, "error") == 0) { - if ((**++tv & 0377) == CANONHOST) + if (hostp != NULL) { register struct errcodes *ep; - if (isascii(**++tv) && isdigit(**tv)) + if (strchr(hbuf, '.') != NULL) + { + a->q_status = newstr(hbuf); + setstat(dsntoexitstat(hbuf)); + } + else if (isascii(hbuf[0]) && isdigit(hbuf[0])) { - setstat(atoi(*tv)); + setstat(atoi(hbuf)); } else { for (ep = ErrorCodes; ep->ec_name != NULL; ep++) - if (strcasecmp(ep->ec_name, *tv) == 0) + if (strcasecmp(ep->ec_name, hbuf) == 0) break; setstat(ep->ec_code); } - tv++; } else setstat(EX_UNAVAILABLE); - buf[0] = '\0'; - for (; (*tv != NULL) && (**tv != CANONUSER); tv++) - { - if (buf[0] != '\0') - (void) strcat(buf, " "); - (void) strcat(buf, *tv); - } - if ((**tv & 0377) != CANONUSER) - syserr("554 buildaddr: error: no user"); - cataddr(++tv, NULL, buf, sizeof buf, ' '); - stripquotes(buf); -#ifdef LOG - if (LogLevel > 8) - syslog (LOG_DEBUG, "%s: Trace: $#ERROR $: %s", - CurEnv->e_id, buf); -#endif /* LOG */ - if (isascii(buf[0]) && isdigit(buf[0]) && - isascii(buf[1]) && isdigit(buf[1]) && - isascii(buf[2]) && isdigit(buf[2]) && - buf[3] == ' ') + stripquotes(ubuf); + if (isascii(ubuf[0]) && isdigit(ubuf[0]) && + isascii(ubuf[1]) && isdigit(ubuf[1]) && + isascii(ubuf[2]) && isdigit(ubuf[2]) && + ubuf[3] == ' ') { char fmt[10]; - strncpy(fmt, buf, 3); + strncpy(fmt, ubuf, 3); strcpy(&fmt[3], " %s"); - usrerr(fmt, buf + 4); + usrerr(fmt, ubuf + 4); + + /* + ** If this is a 4xx code and we aren't running + ** SMTP on our input, bounce this message; + ** otherwise it disappears without a trace. + */ + + if (fmt[0] == '4' && OpMode != MD_SMTP && + OpMode != MD_DAEMON) + { + e->e_flags |= EF_FATALERRS; + } } else { - usrerr("%s", buf); + usrerr("553 %s", ubuf); } goto badaddr; } for (mp = Mailer; (m = *mp++) != NULL; ) { - if (strcasecmp(m->m_name, *tv) == 0) + if (strcasecmp(m->m_name, mname) == 0) break; } if (m == NULL) { - syserr("554 buildaddr: unknown mailer %s", *tv); + syserr("554 buildaddr: unknown mailer %s", mname); goto badaddr; } a->q_mailer = m; /* figure out what host (if any) */ - if (**++tv != CANONHOST) + if (hostp == NULL) { + if (!bitnset(M_LOCALMAILER, m->m_flags)) + { + syserr("554 buildaddr: no host"); + goto badaddr; + } a->q_host = NULL; } else - { - else - a->q_host = NULL; - } - else - { - while (*++tv != NULL && **tv != CANONUSER) - (void) strcat(buf, *tv); - a->q_host = newstr(buf); - } + a->q_host = newstr(hbuf); /* figure out the user */ - if (*tv == NULL || (**tv & 0377) != CANONUSER) + p = ubuf; + if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@') { - syserr("554 buildaddr: no user"); - goto badaddr; + p++; + tv++; + a->q_flags |= QNOTREMOTE; } - tv++; /* do special mapping for local mailer */ - if (m == LocalMailer && *tv != NULL) + if (*p == '"') + p++; + if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags)) + a->q_mailer = m = ProgMailer; + else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags)) + a->q_mailer = m = FileMailer; + else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags)) { - register char *p = *tv; - - if (*p == '"') - p++; - if (*p == '|') - a->q_mailer = m = ProgMailer; - else if (*p == '/') - a->q_mailer = m = FileMailer; - else if (*p == ':') + /* may be :include: */ + stripquotes(ubuf); + if (strncasecmp(ubuf, ":include:", 9) == 0) { - /* may be :include: */ - cataddr(tv, NULL, buf, sizeof buf, '\0'); - stripquotes(buf); - if (strncasecmp(buf, ":include:", 9) == 0) - { - /* if :include:, don't need further rewriting */ - a->q_mailer = m = InclMailer; - a->q_user = &buf[9]; - return (a); - } + /* if :include:, don't need further rewriting */ + a->q_mailer = m = InclMailer; + a->q_user = newstr(&ubuf[9]); + return a; } } - if (m == LocalMailer && *tv != NULL && strcmp(*tv, "@") == 0) + /* rewrite according recipient mailer rewriting rules */ + define('h', a->q_host, e); + if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags)) { - tv++; - a->q_flags |= QNOTREMOTE; + /* sender addresses done later */ + (void) rewrite(tv, 2, 0, e); + if (m->m_re_rwset > 0) + (void) rewrite(tv, m->m_re_rwset, 0, e); } - - if (m->m_r_rwset > 0) - rewrite(tv, m->m_r_rwset); - (void) rewrite(tv, 4, e); + (void) rewrite(tv, 4, 0, e); /* save the result for the command line/RCPT argument */ - cataddr(tv, NULL, buf, sizeof buf, '\0'); - a->q_user = buf; + cataddr(tv, NULL, ubuf, sizeof ubuf, '\0'); + a->q_user = ubuf; /* ** Do mapping to lower case as requested by mailer @@ -1748,7 +1539,7 @@ badaddr: if (!bitnset(M_USR_UPPER, m->m_flags)) makelower(a->q_user); - return (a); + return a; } /* ** CATADDR -- concatenate pieces of addresses (putting in subs) @@ -1775,10 +1566,10 @@ cataddr(pvp, evp, buf, sz, spacesub) char **evp; char *buf; register int sz; - char spacesub; + int spacesub; { bool oatomtok = FALSE; - bool natomtok; + bool natomtok = FALSE; register int i; register char *p; @@ -1794,7 +1585,7 @@ cataddr(pvp, evp, buf, sz, spacesub) sz -= 2; while (*pvp != NULL && (i = strlen(*pvp)) < sz) { - natomtok = (toktype(**pvp) == ATM); + natomtok = (TokTypeTab[**pvp & 0xff] == ATM); if (oatomtok && natomtok) *p++ = spacesub; (void) strcpy(p, *pvp); @@ -1828,17 +1619,26 @@ sameaddr(a, b) register ADDRESS *a; register ADDRESS *b; { + register ADDRESS *ca, *cb; + /* if they don't have the same mailer, forget it */ if (a->q_mailer != b->q_mailer) return (FALSE); /* if the user isn't the same, we can drop out */ - if (strcasecmp(a->q_user, b->q_user)) + if (strcmp(a->q_user, b->q_user) != 0) return (FALSE); - /* if we have good uids for both but the differ, these are different */ - if (bitset(QGOODUID, a->q_flags & b->q_flags) && a->q_uid != b->q_uid) - return (FALSE); + /* if we have good uids for both but they differ, these are different */ + if (a->q_mailer == ProgMailer) + { + ca = getctladdr(a); + cb = getctladdr(b); + if (ca != NULL && cb != NULL && + bitset(QGOODUID, ca->q_flags & cb->q_flags) && + ca->q_uid != cb->q_uid) + return (FALSE); + } /* otherwise compare hosts (but be careful for NULL ptrs) */ if (a->q_host == b->q_host) @@ -1851,7 +1651,7 @@ sameaddr(a, b) /* only one is a null pointer */ return (FALSE); } - if (strcasecmp(a->q_host, b->q_host)) + if (strcmp(a->q_host, b->q_host) != 0) return (FALSE); return (TRUE); @@ -1870,23 +1670,55 @@ sameaddr(a, b) ** none. */ +struct qflags +{ + char *qf_name; + u_long qf_bit; +}; + +struct qflags AddressFlags[] = +{ + "QDONTSEND", QDONTSEND, + "QBADADDR", QBADADDR, + "QGOODUID", QGOODUID, + "QPRIMARY", QPRIMARY, + "QQUEUEUP", QQUEUEUP, + "QSENT", QSENT, + "QNOTREMOTE", QNOTREMOTE, + "QSELFREF", QSELFREF, + "QVERIFIED", QVERIFIED, + "QBOGUSSHELL", QBOGUSSHELL, + "QUNSAFEADDR", QUNSAFEADDR, + "QPINGONSUCCESS", QPINGONSUCCESS, + "QPINGONFAILURE", QPINGONFAILURE, + "QPINGONDELAY", QPINGONDELAY, + "QHASNOTIFY", QHASNOTIFY, + "QRELAYED", QRELAYED, + "QEXPANDED", QEXPANDED, + "QDELIVERED", QDELIVERED, + "QDELAYED", QDELAYED, + "QTHISPASS", QTHISPASS, + NULL +}; + void printaddr(a, follow) register ADDRESS *a; bool follow; { - bool first = TRUE; register MAILER *m; MAILER pseudomailer; + register struct qflags *qfp; + bool firstone; - static int indent; - register int i; + if (a == NULL) + { + printf("[NULL]\n"); + return; + } while (a != NULL) { - first = FALSE; - for (i = indent; i > 0; i--) - printf("\t"); printf("%x=", a); (void) fflush(stdout); @@ -1899,25 +1731,58 @@ printaddr(a, follow) m->m_name = "NULL"; } - for (i = indent; i > 0; i--) - printf("\t"); - printf("\tnext=%x, flags=%o, rmailer %d, alias=%x, sibling=%x, child=%x\n", - a->q_next, a->q_flags, a->q_rmailer, a->q_alias, - a->q_sibling, a->q_child); - - /* follow the chain if appropriate */ + printf("%s:\n\tmailer %d (%s), host `%s'\n", + a->q_paddr, m->m_mno, m->m_name, + a->q_host == NULL ? "" : a->q_host); + printf("\tuser `%s', ruser `%s'\n", + a->q_user, + a->q_ruser == NULL ? "" : a->q_ruser); + printf("\tnext=%x, alias %x, uid %d, gid %d\n", + a->q_next, a->q_alias, a->q_uid, a->q_gid); + printf("\tflags=%lx<", a->q_flags); + firstone = TRUE; + for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++) + { + if (!bitset(qfp->qf_bit, a->q_flags)) + continue; + if (!firstone) + printf(","); + firstone = FALSE; + printf("%s", qfp->qf_name); + } + printf(">\n"); + printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n", + a->q_owner == NULL ? "(none)" : a->q_owner, + a->q_home == NULL ? "(none)" : a->q_home, + a->q_fullname == NULL ? "(none)" : a->q_fullname); + printf("\torcpt=\"%s\", statmta=%s, rstatus=%s\n", + a->q_orcpt == NULL ? "(none)" : a->q_orcpt, + a->q_statmta == NULL ? "(none)" : a->q_statmta, + a->q_rstatus == NULL ? "(none)" : a->q_rstatus); + if (!follow) return; - - indent++; - printaddr(a->q_child, TRUE); - indent--; - a = a->q_sibling; + a = a->q_next; } - if (first) - printf("[NULL]\n"); } + /* +** EMPTYADDR -- return TRUE if this address is empty (``<>'') +** +** Parameters: +** a -- pointer to the address +** +** Returns: +** TRUE -- if this address is "empty" (i.e., no one should +** ever generate replies to it. +** FALSE -- if it is a "regular" (read: replyable) address. +*/ +bool +emptyaddr(a) + register ADDRESS *a; +{ + return strcmp(a->q_paddr, "<>") == 0 || strcmp(a->q_user, "<>") == 0; +} /* ** REMOTENAME -- return the name relative to the current mailer ** @@ -1944,7 +1809,7 @@ printaddr(a, follow) char * remotename(name, m, flags, pstat, e) char *name; - MAILER *m; + struct mailer *m; int flags; int *pstat; register ENVELOPE *e; @@ -1953,15 +1818,22 @@ remotename(name, m, flags, pstat, e) char *fancy; char *oldg = macvalue('g', e); int rwset; - static char buf[MAXNAME]; - char lbuf[MAXNAME]; + static char buf[MAXNAME + 1]; + char lbuf[MAXNAME + 1]; char pvpbuf[PSBUFSIZE]; + extern char *crackaddr(); if (tTd(12, 1)) printf("remotename(%s)\n", name); /* don't do anything if we are tagging it as special */ - if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0) + if (bitset(RF_SENDERADDR, flags)) + rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset + : m->m_se_rwset; + else + rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset + : m->m_re_rwset; + if (rwset < 0) return (name); /* @@ -1982,10 +1854,10 @@ remotename(name, m, flags, pstat, e) ** domain will be appended. */ - pvp = prescan(name, '\0', pvpbuf, NULL); + pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL); if (pvp == NULL) return (name); - if (rewrite(pvp, 3, e) == EX_TEMPFAIL) + if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL) { @@ -2002,24 +1874,31 @@ remotename(name, m, flags, pstat, e) while ((*pxp++ = *qxq++) != NULL) continue; - if (rewrite(pvp, 3, e) == EX_TEMPFAIL) + if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; } } /* ** Do more specific rewriting. - ** Rewrite using ruleset 1 or 2 for envelope addresses and - ** 5 or 6 for header addresses depending on whether this - ** is a sender address or not. + ** Rewrite using ruleset 1 or 2 depending on whether this is + ** a sender address or not. ** Then run it through any receiving-mailer-specific rulesets. */ + if (bitset(RF_SENDERADDR, flags)) + { + if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; + } else { + if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL) + *pstat = EX_TEMPFAIL; + } if (rwset > 0) { - if (rewrite(pvp, rwset, e) == EX_TEMPFAIL) + if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; } @@ -2030,7 +1909,7 @@ remotename(name, m, flags, pstat, e) ** may be used as a default to the above rules. */ - if (rewrite(pvp, 4, e) == EX_TEMPFAIL) + if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; /* @@ -2039,7 +1918,13 @@ remotename(name, m, flags, pstat, e) cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0'); define('g', lbuf, e); - expand(fancy, buf, &buf[sizeof buf - 1], e); + + /* need to make sure route-addrs have */ + if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@') + expand("<\201g>", buf, sizeof buf, e); + else + expand(fancy, buf, sizeof buf, e); + define('g', oldg, e); if (tTd(12, 1)) @@ -2047,90 +1932,24 @@ remotename(name, m, flags, pstat, e) return (buf); } /* -** UURELATIVIZE -- Make an address !-relative to recipient/sender nodes -** -** Parameters: -** from -- the sending node (usually "$k" or "$w") -** to -- the receiving node (usually "$h") -** pvp -- address vector -** -** Returns: -** none. -** -** Side Effects: -** The pvp is rewritten to be relative the "to" node -** wrt the "from" node. In other words, if the pvp -** is headed by "to!" that part is stripped; otherwise -** "from!" is prepended. Exception: "to!user" addresses -** with no '!'s in the user part are sent as is. -** -** Bugs: -** The pvp may overflow, but we don't catch it. -*/ - -static void -uurelativize(from, to, pvp) - const char *from, *to; - char **pvp; -{ - register char **pxp = pvp; - char expfrom[MAXNAME], expto[MAXNAME]; - - expand(from, expfrom, &expfrom[sizeof expfrom - 1], CurEnv); - expand(to, expto, &expto[sizeof expto - 1], CurEnv); - - /* - * supposing that we've got something, should - * we add "from!" or remove "to!"? - */ - if (pvp[0] != NULL) - if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 || - /*strcasecmp?*/ strcmp(pvp[0], expto) != 0) - { - /* either local name, no UUCP address, */ - /* or not to "to!" ==> prepend address with "from!" */ - - /* already there? */ - if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 || - /*strcasecmp?*/ strcmp(pvp[0], expfrom) != 0) - { - - /* no, put it there */ - while (*pxp != NULL) - pxp++; - do - pxp[2] = *pxp; - while (pxp-- != pvp); - pvp[0] = newstr(expfrom); - pvp[1] = "!"; - } - } - else - { - /* address is to "to!" -- remove if not "to!user" */ - for (pxp = &pvp[2]; - *pxp != NULL && strcmp(*pxp, "!") != 0; pxp++) - ; - if (*pxp != NULL) - for (pxp = pvp; *pxp != NULL; pxp++) - *pxp = pxp[2]; - } -} - /* ** MAPLOCALUSER -- run local username through ruleset 5 for final redirection ** ** Parameters: ** a -- the address to map (but just the user name part). ** sendq -- the sendq in which to install any replacement ** addresses. +** aliaslevel -- the alias nesting depth. +** e -- the envelope. ** ** Returns: ** none. */ -maplocaluser(a, sendq, e) +void +maplocaluser(a, sendq, aliaslevel, e) register ADDRESS *a; ADDRESS **sendq; + int aliaslevel; ENVELOPE *e; { register char **pvp; @@ -2143,11 +1962,11 @@ maplocaluser(a, sendq, e) printf("maplocaluser: "); printaddr(a, FALSE); } - pvp = prescan(a->q_user, '\0', pvpbuf, &delimptr); + pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL); if (pvp == NULL) return; - (void) rewrite(pvp, 5, e); + (void) rewrite(pvp, 5, 0, e); if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) return; @@ -2165,7 +1984,7 @@ maplocaluser(a, sendq, e) } a1->q_alias = a; allocaddr(a1, RF_COPYALL, NULL); - (void) recipient(a1, sendq, e); + (void) recipient(a1, sendq, aliaslevel, e); } /* ** DEQUOTE_INIT -- initialize dequote map @@ -2187,6 +2006,7 @@ dequote_init(map, args) { register char *p = args; + map->map_mflags |= MF_KEEPQUOTES; for (;;) { while (isascii(*p) && isspace(*p)) @@ -2198,6 +2018,10 @@ dequote_init(map, args) case 'a': map->map_app = ++p; break; + + case 's': + map->map_coldelim = *++p; + break; } while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; @@ -2234,19 +2058,13 @@ dequote_map(map, name, av, statp) register char *p; register char *q; register char c; - int anglecnt; - int cmntcnt; - int quotecnt; - int spacecnt; - bool quotemode; - bool bslashmode; - - anglecnt = 0; - cmntcnt = 0; - quotecnt = 0; - spacecnt = 0; - quotemode = FALSE; - bslashmode = FALSE; + int anglecnt = 0; + int cmntcnt = 0; + int quotecnt = 0; + int spacecnt = 0; + bool quotemode = FALSE; + bool bslashmode = FALSE; + char spacesub = map->map_coldelim; for (p = q = name; (c = *p++) != '\0'; ) { @@ -2257,6 +2075,9 @@ dequote_map(map, name, av, statp) continue; } + if (c == ' ' && spacesub != '\0') + c = spacesub; + switch (c) { case '\\':