122976Smiriam /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333730Sbostic * Copyright (c) 1988 Regents of the University of California. 433730Sbostic * All rights reserved. 533730Sbostic * 642828Sbostic * %sccs.include.redist.c% 733730Sbostic */ 822976Smiriam 922976Smiriam #ifndef lint 10*56326Seric static char sccsid[] = "@(#)parseaddr.c 5.13.1.1 (Berkeley) 09/22/92"; 1133730Sbostic #endif /* not lint */ 1222976Smiriam 13*56326Seric #include "sendmail.h" 14297Seric 15*56326Seric #ifdef CC_WONT_PROMOTE 16*56326Seric static int toktype __P((char)); 17*56326Seric #else /* !CC_WONT_PROMOTE */ 18*56326Seric static int toktype __P((int)); /* char -> int */ 19*56326Seric #endif /* CC_WONT_PROMOTE */ 20*56326Seric static void _rewrite __P((char **, int)); 21*56326Seric static void callsubr __P((char **)); 22*56326Seric static ADDRESS * buildaddr __P((char **, ADDRESS *)); 23*56326Seric static void uurelativize __P((const char *, const char *, char **)); 24*56326Seric 25*56326Seric char *DelimChar; /* set to point to the delimiter */ 26*56326Seric 27297Seric /* 289888Seric ** PARSEADDR -- Parse an address 29297Seric ** 30297Seric ** Parses an address and breaks it up into three parts: a 31297Seric ** net to transmit the message on, the host to transmit it 32297Seric ** to, and a user on that host. These are loaded into an 332973Seric ** ADDRESS header with the values squirreled away if necessary. 34297Seric ** The "user" part may not be a real user; the process may 35297Seric ** just reoccur on that machine. For example, on a machine 36297Seric ** with an arpanet connection, the address 37297Seric ** csvax.bill@berkeley 38297Seric ** will break up to a "user" of 'csvax.bill' and a host 39297Seric ** of 'berkeley' -- to be transmitted over the arpanet. 40297Seric ** 41297Seric ** Parameters: 42297Seric ** addr -- the address to parse. 43297Seric ** a -- a pointer to the address descriptor buffer. 44297Seric ** If NULL, a header will be created. 45297Seric ** copyf -- determines what shall be copied: 46297Seric ** -1 -- don't copy anything. The printname 47297Seric ** (q_paddr) is just addr, and the 48297Seric ** user & host are allocated internally 49297Seric ** to parse. 50297Seric ** 0 -- copy out the parsed user & host, but 51297Seric ** don't copy the printname. 52297Seric ** +1 -- copy everything. 5311445Seric ** delim -- the character to terminate the address, passed 5411445Seric ** to prescan. 55297Seric ** 56297Seric ** Returns: 57297Seric ** A pointer to the address descriptor header (`a' if 58297Seric ** `a' is non-NULL). 59297Seric ** NULL on error. 60297Seric ** 61297Seric ** Side Effects: 62297Seric ** none 63297Seric */ 64297Seric 659374Seric /* following delimiters are inherent to the internal algorithms */ 6616155Seric # define DELIMCHARS "\001()<>,;\\\"\r\n" /* word delimiters */ 672091Seric 682973Seric ADDRESS * 69*56326Seric parseaddr(addr, a, copyf, delim) 70297Seric char *addr; 712973Seric register ADDRESS *a; 72297Seric int copyf; 7311445Seric char delim; 74297Seric { 753149Seric register char **pvp; 76*56326Seric register struct mailer *m; 7716914Seric char pvpbuf[PSBUFSIZE]; 78297Seric 79297Seric /* 80297Seric ** Initialize and prescan address. 81297Seric */ 82297Seric 83*56326Seric CurEnv->e_to = (char *)addr; 847675Seric if (tTd(20, 1)) 859888Seric printf("\n--parseaddr(%s)\n", addr); 863188Seric 87*56326Seric { 88*56326Seric extern char *DelimChar; /* parseaddr.c */ 89*56326Seric char savec; 90*56326Seric bool invalid; 91*56326Seric 92*56326Seric DelimChar = finddelim(addr, delim); 93*56326Seric savec = *DelimChar; 94*56326Seric *DelimChar = '\0'; 95*56326Seric invalid = invalidaddr(addr); 96*56326Seric *DelimChar = savec; 97*56326Seric if (invalid) 98*56326Seric return (NULL); 99*56326Seric } 100*56326Seric 10116914Seric pvp = prescan(addr, delim, pvpbuf); 1023149Seric if (pvp == NULL) 103297Seric return (NULL); 104297Seric 105297Seric /* 1063149Seric ** Apply rewriting rules. 1077889Seric ** Ruleset 0 does basic parsing. It must resolve. 108297Seric */ 109297Seric 1108181Seric rewrite(pvp, 3); 1114070Seric rewrite(pvp, 0); 112297Seric 1133149Seric /* 1143149Seric ** See if we resolved to a real mailer. 1153149Seric */ 116297Seric 1173149Seric if (pvp[0][0] != CANONNET) 1183149Seric { 1193149Seric setstat(EX_USAGE); 1203149Seric usrerr("cannot resolve name"); 1213149Seric return (NULL); 122297Seric } 123297Seric 124297Seric /* 1253149Seric ** Build canonical address from pvp. 126297Seric */ 127297Seric 1283149Seric a = buildaddr(pvp, a); 1294279Seric if (a == NULL) 1304279Seric return (NULL); 131*56326Seric m = a->q_mailer; 132297Seric 133297Seric /* 1343149Seric ** Make local copies of the host & user and then 1353149Seric ** transport them out. 136297Seric */ 137297Seric 138*56326Seric if (copyf > 0) 1398078Seric { 1408078Seric char savec = *DelimChar; 1418078Seric 1428078Seric *DelimChar = '\0'; 143*56326Seric a->q_paddr = newstr(addr); 1448078Seric *DelimChar = savec; 1458078Seric } 146297Seric else 147*56326Seric a->q_paddr = addr; 14824944Seric 14924944Seric if (a->q_user == NULL) 15024944Seric a->q_user = ""; 15124944Seric if (a->q_host == NULL) 15224944Seric a->q_host = ""; 15324944Seric 1543149Seric if (copyf >= 0) 155297Seric { 15624944Seric a->q_host = newstr(a->q_host); 1573149Seric if (a->q_user != a->q_paddr) 1583149Seric a->q_user = newstr(a->q_user); 159297Seric } 160297Seric 161297Seric /* 16216202Seric ** Convert host name to lower case if requested. 16316202Seric ** User name will be done later. 16416202Seric */ 16516202Seric 16616202Seric if (!bitnset(M_HST_UPPER, m->m_flags)) 16716202Seric makelower(a->q_host); 168*56326Seric 169*56326Seric /* 170*56326Seric ** Compute return value. 171*56326Seric */ 172*56326Seric 173*56326Seric if (tTd(20, 1)) 174*56326Seric { 175*56326Seric printf("parseaddr-->"); 176*56326Seric printaddr(a, FALSE); 177*56326Seric } 178*56326Seric 179*56326Seric return (a); 180297Seric } 181297Seric /* 18216162Seric ** LOWERADDR -- map UPPER->lower case on addresses as requested. 18316162Seric ** 18416162Seric ** Parameters: 18516162Seric ** a -- address to be mapped. 18616162Seric ** 18716162Seric ** Returns: 18816162Seric ** none. 18916162Seric ** 19016162Seric ** Side Effects: 19116162Seric ** none. 19216162Seric */ 19316162Seric 194*56326Seric void 19516162Seric loweraddr(a) 19616162Seric register ADDRESS *a; 19716162Seric { 19816162Seric register MAILER *m = a->q_mailer; 19916162Seric 20016162Seric if (!bitnset(M_USR_UPPER, m->m_flags)) 20116162Seric makelower(a->q_user); 20216162Seric } 20316162Seric /* 204*56326Seric ** INVALIDADDR -- check an address string for invalid control characters. 205*56326Seric ** 206*56326Seric ** Parameters: 207*56326Seric ** addr -- address string to be checked. 208*56326Seric ** 209*56326Seric ** Returns: 210*56326Seric ** TRUE if address string could cause problems, FALSE o/w. 211*56326Seric ** 212*56326Seric ** Side Effects: 213*56326Seric ** ExitStat may be changed and an error message generated. 214*56326Seric */ 215*56326Seric 216*56326Seric bool 217*56326Seric invalidaddr(addr) 218*56326Seric const char *addr; 219*56326Seric { 220*56326Seric register const char *cp; 221*56326Seric 222*56326Seric /* make sure error messages don't have garbage on them */ 223*56326Seric errno = 0; 224*56326Seric 225*56326Seric /* 226*56326Seric ** Sendmail reserves characters 020 - 036 for rewriting rules 227*56326Seric ** which can cause havoc (e.g. infinite rewriting loops) if 228*56326Seric ** one shows up at the wrong time. If any of these characters 229*56326Seric ** appear in an address, the address is deemed "invalid" and 230*56326Seric ** an error message is generated. 231*56326Seric */ 232*56326Seric 233*56326Seric for (cp = addr; *cp; cp++) 234*56326Seric if ((*cp >= MATCHZANY && *cp <= HOSTEND) || *cp == '\001') 235*56326Seric { 236*56326Seric setstat(EX_USAGE); 237*56326Seric usrerr("address contained invalid control char(s)"); 238*56326Seric return (TRUE); 239*56326Seric } 240*56326Seric return (FALSE); 241*56326Seric } 242*56326Seric /* 243297Seric ** PRESCAN -- Prescan name and make it canonical 244297Seric ** 2459374Seric ** Scans a name and turns it into a set of tokens. This process 2469374Seric ** deletes blanks and comments (in parentheses). 247297Seric ** 248297Seric ** This routine knows about quoted strings and angle brackets. 249297Seric ** 250297Seric ** There are certain subtleties to this routine. The one that 251297Seric ** comes to mind now is that backslashes on the ends of names 252297Seric ** are silently stripped off; this is intentional. The problem 253297Seric ** is that some versions of sndmsg (like at LBL) set the kill 254297Seric ** character to something other than @ when reading addresses; 255297Seric ** so people type "csvax.eric\@berkeley" -- which screws up the 256297Seric ** berknet mailer. 257297Seric ** 258297Seric ** Parameters: 259297Seric ** addr -- the name to chomp. 260297Seric ** delim -- the delimiter for the address, normally 261297Seric ** '\0' or ','; \0 is accepted in any case. 26215284Seric ** If '\t' then we are reading the .cf file. 26316914Seric ** pvpbuf -- place to put the saved text -- note that 26416914Seric ** the pointers are static. 265297Seric ** 266297Seric ** Returns: 2673149Seric ** A pointer to a vector of tokens. 268297Seric ** NULL on error. 269297Seric ** 270297Seric ** Side Effects: 27125279Seric ** sets DelimChar to point to the character matching 'delim'. 272297Seric */ 273297Seric 2748078Seric /* states and character types */ 2758078Seric # define OPR 0 /* operator */ 2768078Seric # define ATM 1 /* atom */ 2778078Seric # define QST 2 /* in quoted string */ 2788078Seric # define SPC 3 /* chewing up spaces */ 2798078Seric # define ONE 4 /* pick up one character */ 2803149Seric 2818078Seric # define NSTATES 5 /* number of states */ 2828078Seric # define TYPE 017 /* mask to select state type */ 2838078Seric 2848078Seric /* meta bits for table */ 2858078Seric # define M 020 /* meta character; don't pass through */ 2868078Seric # define B 040 /* cause a break */ 2878078Seric # define MB M|B /* meta-break */ 2888078Seric 2898078Seric static short StateTab[NSTATES][NSTATES] = 2908078Seric { 2918087Seric /* oldst chtype> OPR ATM QST SPC ONE */ 2929051Seric /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, 2939051Seric /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B, 2949051Seric /*QST*/ QST, QST, OPR, QST, QST, 2958078Seric /*SPC*/ OPR, ATM, QST, SPC|M, ONE, 2968078Seric /*ONE*/ OPR, OPR, OPR, OPR, OPR, 2978078Seric }; 2988078Seric 2998078Seric # define NOCHAR -1 /* signal nothing in lookahead token */ 3008078Seric 3013149Seric char ** 30216914Seric prescan(addr, delim, pvpbuf) 303297Seric char *addr; 304297Seric char delim; 30516914Seric char pvpbuf[]; 306297Seric { 307297Seric register char *p; 3088078Seric register char *q; 3099346Seric register int c; 3103149Seric char **avp; 311297Seric bool bslashmode; 312297Seric int cmntcnt; 3138423Seric int anglecnt; 3143149Seric char *tok; 3158078Seric int state; 3168078Seric int newstate; 3178078Seric static char *av[MAXATOM+1]; 318297Seric 31915253Seric /* make sure error messages don't have garbage on them */ 32015253Seric errno = 0; 32115253Seric 32216914Seric q = pvpbuf; 3233149Seric bslashmode = FALSE; 3247800Seric cmntcnt = 0; 3258423Seric anglecnt = 0; 3263149Seric avp = av; 327*56326Seric state = OPR; 3288078Seric c = NOCHAR; 3298078Seric p = addr; 3308078Seric if (tTd(22, 45)) 331297Seric { 3328078Seric printf("prescan: "); 3338078Seric xputs(p); 33423109Seric (void) putchar('\n'); 3358078Seric } 3368078Seric 3378078Seric do 3388078Seric { 3393149Seric /* read a token */ 3403149Seric tok = q; 3418078Seric for (;;) 342297Seric { 3438078Seric /* store away any old lookahead character */ 3448078Seric if (c != NOCHAR) 3458078Seric { 34615284Seric /* see if there is room */ 34716914Seric if (q >= &pvpbuf[PSBUFSIZE - 5]) 3488078Seric { 3498078Seric usrerr("Address too long"); 3508078Seric DelimChar = p; 3518078Seric return (NULL); 3528078Seric } 35315284Seric 35415284Seric /* squirrel it away */ 3558078Seric *q++ = c; 3568078Seric } 3578078Seric 3588078Seric /* read a new input character */ 3598078Seric c = *p++; 3608078Seric if (c == '\0') 3618078Seric break; 362*56326Seric c &= 0177; 36315284Seric 3648078Seric if (tTd(22, 101)) 3658078Seric printf("c=%c, s=%d; ", c, state); 3668078Seric 3673149Seric /* chew up special characters */ 3683149Seric *q = '\0'; 3693149Seric if (bslashmode) 3703149Seric { 37124944Seric /* kludge \! for naive users */ 37224944Seric if (c != '!') 373*56326Seric c |= 0200; 3743149Seric bslashmode = FALSE; 375*56326Seric if (cmntcnt > 0) 376*56326Seric c = NOCHAR; 3773149Seric } 378*56326Seric else if (c == '\\') 3793149Seric { 3803149Seric bslashmode = TRUE; 3818078Seric c = NOCHAR; 3823149Seric } 383*56326Seric if (state == QST) 3848514Seric { 3858514Seric /* do nothing, just avoid next clauses */ 3868514Seric } 3878078Seric else if (c == '(') 3884100Seric { 3898078Seric cmntcnt++; 3908078Seric c = NOCHAR; 3914100Seric } 3928078Seric else if (c == ')') 3933149Seric { 3948078Seric if (cmntcnt <= 0) 3953149Seric { 3968078Seric usrerr("Unbalanced ')'"); 3978078Seric DelimChar = p; 3988078Seric return (NULL); 3993149Seric } 4008078Seric else 4018078Seric cmntcnt--; 4028078Seric } 4038078Seric else if (cmntcnt > 0) 4048078Seric c = NOCHAR; 4058423Seric else if (c == '<') 4068423Seric anglecnt++; 4078423Seric else if (c == '>') 4088423Seric { 4098423Seric if (anglecnt <= 0) 4108423Seric { 4118423Seric usrerr("Unbalanced '>'"); 4128423Seric DelimChar = p; 4138423Seric return (NULL); 4148423Seric } 4158423Seric anglecnt--; 4168423Seric } 41711423Seric else if (delim == ' ' && isspace(c)) 41811423Seric c = ' '; 4193149Seric 420*56326Seric else if (c == ';') /* semicolons are not tokens */ 421*56326Seric c = NOCHAR; 422*56326Seric 4238078Seric if (c == NOCHAR) 4248078Seric continue; 4253149Seric 4268078Seric /* see if this is end of input */ 42711405Seric if (c == delim && anglecnt <= 0 && state != QST) 4283149Seric break; 4293149Seric 4308078Seric newstate = StateTab[state][toktype(c)]; 4318078Seric if (tTd(22, 101)) 4328078Seric printf("ns=%02o\n", newstate); 4338078Seric state = newstate & TYPE; 4348078Seric if (bitset(M, newstate)) 4358078Seric c = NOCHAR; 4368078Seric if (bitset(B, newstate)) 4374228Seric break; 438297Seric } 4393149Seric 4403149Seric /* new token */ 4418078Seric if (tok != q) 4421378Seric { 4438078Seric *q++ = '\0'; 4448078Seric if (tTd(22, 36)) 445297Seric { 4468078Seric printf("tok="); 4478078Seric xputs(tok); 44823109Seric (void) putchar('\n'); 449297Seric } 4508078Seric if (avp >= &av[MAXATOM]) 451297Seric { 4528078Seric syserr("prescan: too many tokens"); 4538078Seric DelimChar = p; 4548078Seric return (NULL); 455297Seric } 4568078Seric *avp++ = tok; 457297Seric } 4588423Seric } while (c != '\0' && (c != delim || anglecnt > 0)); 4593149Seric *avp = NULL; 4608078Seric DelimChar = --p; 4613149Seric if (cmntcnt > 0) 4623149Seric usrerr("Unbalanced '('"); 4638423Seric else if (anglecnt > 0) 4648423Seric usrerr("Unbalanced '<'"); 4658078Seric else if (state == QST) 4663149Seric usrerr("Unbalanced '\"'"); 4673149Seric else if (av[0] != NULL) 4683149Seric return (av); 4693149Seric return (NULL); 4703149Seric } 4713149Seric /* 4723149Seric ** TOKTYPE -- return token type 4733149Seric ** 4743149Seric ** Parameters: 4753149Seric ** c -- the character in question. 4763149Seric ** 4773149Seric ** Returns: 4783149Seric ** Its type. 4793149Seric ** 4803149Seric ** Side Effects: 4813149Seric ** none. 4823149Seric */ 483297Seric 484*56326Seric static int 4853149Seric toktype(c) 4863149Seric register char c; 4873149Seric { 4883380Seric static char buf[50]; 4893382Seric static bool firstime = TRUE; 4903380Seric 4913382Seric if (firstime) 4923380Seric { 4933382Seric firstime = FALSE; 49416155Seric expand("\001o", buf, &buf[sizeof buf - 1], CurEnv); 4957005Seric (void) strcat(buf, DELIMCHARS); 4963380Seric } 497*56326Seric if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS || 498*56326Seric c == MATCHMAP || c == MATCHNMAP) 4998078Seric return (ONE); 500*56326Seric #ifdef MACVALUE 501*56326Seric if (c == MACVALUE) 502*56326Seric return (ONE); 503*56326Seric #endif /* MACVALUE */ 5048078Seric if (c == '"') 5058078Seric return (QST); 5064100Seric if (!isascii(c)) 5078078Seric return (ATM); 5088078Seric if (isspace(c) || c == ')') 5098078Seric return (SPC); 5103380Seric if (iscntrl(c) || index(buf, c) != NULL) 5118078Seric return (OPR); 5128078Seric return (ATM); 5133149Seric } 5143149Seric /* 5153149Seric ** REWRITE -- apply rewrite rules to token vector. 5163149Seric ** 5174476Seric ** This routine is an ordered production system. Each rewrite 5184476Seric ** rule has a LHS (called the pattern) and a RHS (called the 5194476Seric ** rewrite); 'rwr' points the the current rewrite rule. 5204476Seric ** 5214476Seric ** For each rewrite rule, 'avp' points the address vector we 5224476Seric ** are trying to match against, and 'pvp' points to the pattern. 5238058Seric ** If pvp points to a special match value (MATCHZANY, MATCHANY, 5249585Seric ** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp 5259585Seric ** matched is saved away in the match vector (pointed to by 'mvp'). 5264476Seric ** 5274476Seric ** When a match between avp & pvp does not match, we try to 5289585Seric ** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS 5294476Seric ** we must also back out the match in mvp. If we reach a 5308058Seric ** MATCHANY or MATCHZANY we just extend the match and start 5318058Seric ** over again. 5324476Seric ** 5334476Seric ** When we finally match, we rewrite the address vector 5344476Seric ** and try over again. 5354476Seric ** 5363149Seric ** Parameters: 5373149Seric ** pvp -- pointer to token vector. 5383149Seric ** 5393149Seric ** Returns: 5403149Seric ** none. 5413149Seric ** 5423149Seric ** Side Effects: 5433149Seric ** pvp is modified. 5443149Seric */ 5452091Seric 546*56326Seric # define OP_NONZLEN 00001 547*56326Seric # define OP_VARLEN 00002 548*56326Seric # define OP_CLASS 00004 549*56326Seric # define OP_EXACT 00010 550*56326Seric 5513149Seric struct match 5523149Seric { 5534468Seric char **first; /* first token matched */ 5544468Seric char **last; /* last token matched */ 555*56326Seric char **source; /* left hand source operand */ 556*56326Seric char flags; /* attributes of this operator */ 5573149Seric }; 5583149Seric 5594468Seric # define MAXMATCH 9 /* max params per rewrite */ 560*56326Seric # define MAX_CONTROL ' ' 5613149Seric 562*56326Seric static char control_opts[MAX_CONTROL]; 5633149Seric 564*56326Seric static char control_init_data[] = { 565*56326Seric MATCHZANY, OP_VARLEN, 566*56326Seric MATCHONE, OP_NONZLEN, 567*56326Seric MATCHANY, OP_VARLEN|OP_NONZLEN, 568*56326Seric #ifdef MACVALUE 569*56326Seric MACVALUE, OP_EXACT, 570*56326Seric #endif /* MACVALUE */ 571*56326Seric MATCHNCLASS, OP_NONZLEN, 572*56326Seric MATCHCLASS, OP_NONZLEN|OP_VARLEN|OP_CLASS, 573*56326Seric MATCHNMAP, OP_NONZLEN, 574*56326Seric MATCHMAP, OP_NONZLEN|OP_VARLEN|OP_CLASS 575*56326Seric }; 576*56326Seric 577*56326Seric static int nrw; 578*56326Seric 579*56326Seric void 5804070Seric rewrite(pvp, ruleset) 5813149Seric char **pvp; 5824070Seric int ruleset; 5833149Seric { 584*56326Seric nrw = 0; 585*56326Seric _rewrite(pvp, ruleset); 586*56326Seric } 587*56326Seric 588*56326Seric static void 589*56326Seric _rewrite(pvp, ruleset) 590*56326Seric char **pvp; 591*56326Seric int ruleset; 592*56326Seric { 5933149Seric register char *ap; /* address pointer */ 5943149Seric register char *rp; /* rewrite pointer */ 5953149Seric register char **avp; /* address vector pointer */ 5963149Seric register char **rvp; /* rewrite vector pointer */ 5978058Seric register struct match *mlp; /* cur ptr into mlist */ 5988058Seric register struct rewrite *rwr; /* pointer to current rewrite rule */ 5993149Seric char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 600*56326Seric char tokbuf[MAXNAME+1]; /* for concatenated class tokens */ 601*56326Seric int nloops, nmatches = 0; /* for looping rule checks */ 602*56326Seric struct rewrite *prev_rwr; /* pointer to previous rewrite rule */ 603*56326Seric struct match mlist[MAXMATCH+1]; /* stores match on LHS */ 604*56326Seric struct match *old_mlp; /* to save our place */ 605*56326Seric bool extend_match; /* extend existing match during backup */ 6063149Seric 607*56326Seric /* test entry which should be nonzero */ 608*56326Seric if (control_opts[MATCHMAP] == 0) 609*56326Seric { 610*56326Seric /* First time through, initialize table */ 611*56326Seric int c, i; 612*56326Seric 613*56326Seric for (i = 0; i < sizeof control_init_data; ) 614*56326Seric { 615*56326Seric c = control_init_data[i++]; 616*56326Seric control_opts[c] = control_init_data[i++]; 617*56326Seric } 618*56326Seric } 619*56326Seric 6209279Seric if (OpMode == MD_TEST || tTd(21, 2)) 6213149Seric { 6228959Seric printf("rewrite: ruleset %2d input:", ruleset); 623*56326Seric printcav(pvp); 6243149Seric } 6258423Seric if (pvp == NULL) 6268423Seric return; 6273149Seric 628*56326Seric if (++nrw > 100) 629*56326Seric { 630*56326Seric char buf[MAXLINE]; 631*56326Seric 632*56326Seric buf[0] = buf[MAXLINE-1] = 0; 633*56326Seric while (*pvp) 634*56326Seric (void) strncat(buf, *pvp++, sizeof buf); 635*56326Seric syserr("address causes rewrite loop: <%s>", buf); 636*56326Seric return; 637*56326Seric } 638*56326Seric 639*56326Seric /* Be sure to recognize first rule as new */ 640*56326Seric prev_rwr = NULL; 641*56326Seric 6423149Seric /* 643*56326Seric ** Run through the list of rewrite rules, applying any that match. 6443149Seric */ 6453149Seric 6464070Seric for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 6473149Seric { 6487675Seric if (tTd(21, 12)) 649297Seric { 6508069Seric printf("-----trying rule:"); 651*56326Seric printcav(rwr->r_lhs); 6523149Seric } 6533149Seric 654*56326Seric /* 655*56326Seric ** Set up the match list. This is done once for each 656*56326Seric ** rule. If a rule is used repeatedly, the list need not 657*56326Seric ** be set up the next time. 658*56326Seric */ 659*56326Seric 660*56326Seric if (rwr != prev_rwr) 661*56326Seric { 662*56326Seric prev_rwr = rwr; 663*56326Seric for (rvp = rwr->r_lhs, mlp = mlist; 664*56326Seric *rvp && (mlp < &mlist[MAXMATCH]); rvp++) 665*56326Seric { 666*56326Seric mlp->flags = ((unsigned char) **rvp >= MAX_CONTROL) ? 667*56326Seric 0 : control_opts[**rvp] ; 668*56326Seric if (mlp->flags) 669*56326Seric { 670*56326Seric mlp->source = rvp; 671*56326Seric mlp++; 672*56326Seric } 673*56326Seric } 674*56326Seric if (*rvp) 675*56326Seric { 676*56326Seric syserr("Too many variables on LHS in ruleset %d", ruleset); 677*56326Seric return; 678*56326Seric } 679*56326Seric mlp->source = rvp; 680*56326Seric 681*56326Seric /* Make sure end marker is initialized */ 682*56326Seric mlp->flags = 0; 683*56326Seric } 684*56326Seric 6853149Seric /* try to match on this rule */ 6864468Seric mlp = mlist; 687*56326Seric 6888058Seric rvp = rwr->r_lhs; 6898058Seric avp = pvp; 690*56326Seric nloops = 0; 691*56326Seric extend_match = FALSE; 692*56326Seric 6938058Seric while ((ap = *avp) != NULL || *rvp != NULL) 6943149Seric { 695*56326Seric if (nloops++ > 400) 69652637Seric { 697*56326Seric syserr("Looping on ruleset %d, rule %d", 698*56326Seric ruleset, rwr-RewriteRules[ruleset]); 699*56326Seric mlp = mlist - 1; /* force rule failure */ 70052637Seric break; 70152637Seric } 7023149Seric rp = *rvp; 703*56326Seric 7048058Seric if (tTd(21, 35)) 7058058Seric { 706*56326Seric printf("ap="); 7078058Seric xputs(ap); 708*56326Seric printf(", rp="); 7098058Seric xputs(rp); 7108069Seric printf("\n"); 7118058Seric } 712*56326Seric 713*56326Seric if (extend_match) 714*56326Seric extend_match = FALSE; 715*56326Seric else 716*56326Seric { 717*56326Seric mlp->first = avp; 718*56326Seric mlp->last = mlp->flags == 0 || (mlp->flags & OP_NONZLEN) ? 719*56326Seric avp + 1 : avp; 720*56326Seric } 721*56326Seric 7223149Seric if (rp == NULL) 7233149Seric /* end-of-pattern before end-of-address */ 7248058Seric goto backup; 725*56326Seric 726*56326Seric /* Premature end of address */ 727*56326Seric if (ap == NULL && avp != mlp->last) 728*56326Seric goto backup; 729*56326Seric 730*56326Seric /* 731*56326Seric ** Simplest case - exact token comparison between 732*56326Seric ** pattern and address. Such a match is not saved 733*56326Seric ** in mlp. 734*56326Seric */ 735*56326Seric 736*56326Seric if (rvp < mlp->source) 737*56326Seric { 738*56326Seric if (ap == NULL || strcasecmp(ap, rp)) 739*56326Seric goto backup; 740*56326Seric rvp++; 741*56326Seric avp++; 742*56326Seric continue; 743297Seric } 744*56326Seric 745*56326Seric #ifdef MACVALUE 746*56326Seric /* 747*56326Seric ** This is special case handled. The match is exact, 748*56326Seric ** but might span zero or more address tokens. The 749*56326Seric ** result is saved in mlp. 750*56326Seric */ 751*56326Seric 752*56326Seric if (*rp == MACVALUE) 7538058Seric { 754*56326Seric int len; 755*56326Seric rp = macvalue(rp[1], CurEnv); 756*56326Seric 757*56326Seric if (rp) 758*56326Seric while (*rp) 759*56326Seric { 760*56326Seric if (*avp == NULL || strncasecmp(rp,*avp,len = strlen(*avp))) 761*56326Seric goto backup; 762*56326Seric rp += len; 763*56326Seric avp++; 764*56326Seric } 765*56326Seric mlp->last = avp; 766*56326Seric rvp++; 767*56326Seric mlp++; 768*56326Seric continue; 7698058Seric } 770*56326Seric #endif /* MACVALUE */ 7713149Seric 772*56326Seric /* 773*56326Seric ** All other matches are saved in mlp. Initially 774*56326Seric ** assume they match at the shortest possible length 775*56326Seric ** for this pattern. Variable patterns will be 776*56326Seric ** extended later as needed. 777*56326Seric */ 778*56326Seric 779*56326Seric /* Fixed length first */ 780*56326Seric if (!(mlp->flags & OP_VARLEN)) 7813149Seric { 782*56326Seric switch (*rp) 783*56326Seric { 784*56326Seric register STAB *s; 7854060Seric 786*56326Seric case MATCHNCLASS: 787*56326Seric /* match any single token not in a class */ 788*56326Seric s = stab(ap, ST_CLASS, ST_FIND); 789*56326Seric if (s != NULL && bitnset(rp[1], s->s_class)) 7909585Seric goto backup; 791*56326Seric break; 7924468Seric 793*56326Seric case MATCHNMAP: 794*56326Seric /* match any token not in a DBM map */ 795*56326Seric if ((rp = mapkey(rp[1], ap, 0, NULL)) != NULL) 796*56326Seric { 797*56326Seric free(rp); 798*56326Seric goto backup; 799*56326Seric } 800*56326Seric break; 8014476Seric 802*56326Seric } 8034060Seric 804*56326Seric avp = mlp->last; 805*56326Seric rvp++; 8068058Seric mlp++; 807*56326Seric continue; 808*56326Seric } 8098058Seric 810*56326Seric /* 811*56326Seric ** We now have a variable length item. It could 812*56326Seric ** be $+ or $* in which case no special checking 813*56326Seric ** is needed. But a class match such as $=x must 814*56326Seric ** be verified. 815*56326Seric ** 816*56326Seric ** As a speedup, if a variable length item is 817*56326Seric ** followed by a plain character token, we initially 818*56326Seric ** extend the match to the first such token we find. 819*56326Seric ** If the required character token cannot be found, 820*56326Seric ** we fail the match at this point. 821*56326Seric */ 822*56326Seric 823*56326Seric avp = mlp->last; 824*56326Seric 825*56326Seric /* If next token is char token */ 826*56326Seric if (&rvp[1] < mlp[1].source) 827*56326Seric { 828*56326Seric while (*avp && strcasecmp(*avp, rvp[1])) 829*56326Seric avp++; 830*56326Seric 831*56326Seric /* 832*56326Seric ** If we can't find the proper ending token, 833*56326Seric ** leave avp point to NULL. This indicates 834*56326Seric ** we have run out of address tokens. It is 835*56326Seric ** pointless to advance the beginning of this 836*56326Seric ** match and retry. 837*56326Seric */ 838*56326Seric 839*56326Seric if (*avp == NULL) 8408058Seric goto backup; 841*56326Seric mlp->last = avp; 842*56326Seric } 843*56326Seric else if (rvp[1] == NULL) 844*56326Seric /* next token is end of address */ 845*56326Seric { 846*56326Seric while (*avp) 847*56326Seric avp++; 848*56326Seric mlp->last = avp; 849*56326Seric } 850*56326Seric 851*56326Seric if (mlp->flags & OP_CLASS) 852*56326Seric { 853*56326Seric register char *cp = tokbuf; 854*56326Seric 855*56326Seric avp = mlp->first; 856*56326Seric strcpy(cp, *avp); 8574468Seric avp++; 858*56326Seric for (;;) 859*56326Seric { 860*56326Seric while (avp < mlp->last) 861*56326Seric { 862*56326Seric while (*cp) 863*56326Seric cp++; 864*56326Seric strcpy(cp, *avp); 865*56326Seric avp++; 866*56326Seric } 867*56326Seric switch (*rp) 868*56326Seric { 869*56326Seric register STAB *s; 870*56326Seric 871*56326Seric case MATCHCLASS: 872*56326Seric s = stab(tokbuf, ST_CLASS, ST_FIND); 873*56326Seric if (s != NULL && bitnset(rp[1], s->s_class)) 874*56326Seric goto have_match; 875*56326Seric break; 876*56326Seric 877*56326Seric case MATCHMAP: 878*56326Seric if (mapkey(rp[1], tokbuf, sizeof(tokbuf)-1, NULL)) 879*56326Seric goto have_match; 880*56326Seric break; 881*56326Seric } 882*56326Seric 883*56326Seric /* 884*56326Seric ** Class match initially failed. 885*56326Seric ** Extend the tentative match. 886*56326Seric ** Again, if followed by a character 887*56326Seric ** token, extend all the way to that 888*56326Seric ** token before checking. 889*56326Seric */ 890*56326Seric 891*56326Seric if (*avp) 892*56326Seric { 893*56326Seric (mlp->last)++; 894*56326Seric if (&rvp[1] < mlp[1].source) 895*56326Seric { 896*56326Seric while (*(mlp->last) && strcasecmp(*(mlp->last), rvp[1])) 897*56326Seric (mlp->last)++; 898*56326Seric if (*(mlp->last) == NULL) 899*56326Seric avp = mlp->last; 900*56326Seric } 901*56326Seric } 902*56326Seric if (*avp == NULL) 903*56326Seric { 904*56326Seric /* 905*56326Seric ** We could not find the 906*56326Seric ** ending token. But we had 907*56326Seric ** found ending tokens before. 908*56326Seric ** A match is still plausible 909*56326Seric ** if the start of the 910*56326Seric ** tentative match is advanced. 911*56326Seric ** Hence we must not leave avp 912*56326Seric ** pointing to NULL. 913*56326Seric */ 914*56326Seric avp = mlp->first; 915*56326Seric goto backup; 916*56326Seric } 917*56326Seric } 9183149Seric } 9193149Seric 920*56326Seric have_match: 9213149Seric rvp++; 922*56326Seric mlp++; 9233149Seric continue; 9243149Seric 925*56326Seric backup: 926*56326Seric /* We failed to match. mlp marks point of failure */ 927*56326Seric 928*56326Seric /* 929*56326Seric ** There is a special case when we have exhausted 930*56326Seric ** the address, but have not exhausted the pattern. 931*56326Seric ** Under normal circumstances we could consider the 932*56326Seric ** failure permanent, since extending the number of 933*56326Seric ** address tokens matched by a '$+' or a '$*' will 934*56326Seric ** only worsen the situation. 935*56326Seric ** 936*56326Seric ** There is an exception, however. It is possible 937*56326Seric ** that we have matched a class token, say '$=x', 938*56326Seric ** with three or more tokens. Extending a '$+' say, 939*56326Seric ** which precedes the '$=x' will move the beginning 940*56326Seric ** of the '$=x' match to the right, but it might match 941*56326Seric ** a smaller number of tokens then, possibly 942*56326Seric ** correcting the mismatch. 943*56326Seric ** 944*56326Seric ** Thus in this case we initially back up to the 945*56326Seric ** $=x which matches three or more tokens. 946*56326Seric */ 947*56326Seric 948*56326Seric if (*avp == NULL) 9493149Seric { 950*56326Seric while (--mlp > mlist) 9514468Seric { 952*56326Seric if ((mlp->flags & OP_CLASS) && 953*56326Seric mlp->last > 2 + mlp->first) 954*56326Seric break; 9554468Seric } 9563149Seric } 9573149Seric 958*56326Seric /* 959*56326Seric ** Now backup till we find a match with a pattern 960*56326Seric ** whose length is extendable, and extend that. 961*56326Seric */ 962*56326Seric 963*56326Seric mlp--; 964*56326Seric while (mlp >= mlist && !(mlp->flags & OP_VARLEN)) 965*56326Seric mlp--; 966*56326Seric 967*56326Seric /* Total failure to match */ 968*56326Seric if (mlp < mlist) 969*56326Seric break; 970*56326Seric 971*56326Seric avp = ++(mlp->last); 972*56326Seric rvp = mlp->source; 973*56326Seric 974*56326Seric /* 975*56326Seric ** We have found a backup point. Normally we would 976*56326Seric ** increase the matched amount by one token, and 977*56326Seric ** continue from the next item in the pattern. But 978*56326Seric ** there are two special cases. If this is a 979*56326Seric ** class-type match (OP_CLASS), we must test the 980*56326Seric ** validity of the extended match. If this pattern 981*56326Seric ** item is directly followed by a character token, it 982*56326Seric ** is worth going back and locating the next such 983*56326Seric ** character token before we continue on. 984*56326Seric */ 985*56326Seric if ((mlp->flags & OP_CLASS) || (&rvp[1] < mlp[1].source)) 9863149Seric { 987*56326Seric avp = mlp->first; 988*56326Seric extend_match = TRUE; 9893149Seric } 990*56326Seric else 991*56326Seric { 992*56326Seric mlp++; 993*56326Seric rvp++; 994*56326Seric } 995297Seric } 9963149Seric 9973149Seric /* 998*56326Seric ** See if we successfully matched. 9993149Seric */ 10003149Seric 1001*56326Seric if (mlp < mlist) 10023149Seric { 10039374Seric if (tTd(21, 10)) 10049374Seric printf("----- rule fails\n"); 10059374Seric rwr = rwr->r_next; 1006*56326Seric nmatches = 0; 10079374Seric continue; 10089374Seric } 10093149Seric 1010*56326Seric if (nmatches++ > 200) 1011*56326Seric { 1012*56326Seric syserr("Loop in ruleset %d, rule %d (too many matches)", 1013*56326Seric ruleset, rwr - RewriteRules[ruleset]); 1014*56326Seric rwr = rwr->r_next; 1015*56326Seric nmatches = 0; 1016*56326Seric continue; 1017*56326Seric } 1018*56326Seric 10199374Seric rvp = rwr->r_rhs; 10209374Seric if (tTd(21, 12)) 10219374Seric { 10229374Seric printf("-----rule matches:"); 1023*56326Seric printcav(rvp); 10249374Seric } 10259374Seric 10269374Seric rp = *rvp; 10279374Seric if (*rp == CANONUSER) 10289374Seric { 10299374Seric rvp++; 10309374Seric rwr = rwr->r_next; 1031*56326Seric nmatches = 0; 10329374Seric } 10339374Seric else if (*rp == CANONHOST) 10349374Seric { 10359374Seric rvp++; 10369374Seric rwr = NULL; 10379374Seric } 10389374Seric else if (*rp == CANONNET) 10399374Seric rwr = NULL; 10409374Seric 10419374Seric /* substitute */ 10429374Seric for (avp = npvp; *rvp != NULL; rvp++) 10439374Seric { 10449374Seric register struct match *m; 10459374Seric register char **pp; 10469374Seric 10478058Seric rp = *rvp; 1048*56326Seric if (*rp == MATCHREPL && rp[1] >= '1' && rp[1] <= '9') 10498058Seric { 105016914Seric /* substitute from LHS */ 105116914Seric m = &mlist[rp[1] - '1']; 105216914Seric if (m >= mlp) 10539374Seric { 1054*56326Seric syserr("rewrite: ruleset %d: replacement #%c out of bounds", 1055*56326Seric ruleset, rp[1]); 10569374Seric return; 10579374Seric } 105816914Seric if (tTd(21, 15)) 105916914Seric { 106016914Seric printf("$%c:", rp[1]); 106116914Seric pp = m->first; 1062*56326Seric while (pp < m->last) 106316914Seric { 106416914Seric printf(" %x=\"", *pp); 106516914Seric (void) fflush(stdout); 106616914Seric printf("%s\"", *pp++); 106716914Seric } 106816914Seric printf("\n"); 106916914Seric } 10709374Seric pp = m->first; 1071*56326Seric while (pp < m->last) 10723149Seric { 107316914Seric if (avp >= &npvp[MAXATOM]) 1074*56326Seric goto toolong; 107516914Seric *avp++ = *pp++; 10763149Seric } 10773149Seric } 107816914Seric else 10798226Seric { 108016914Seric /* vanilla replacement */ 10819374Seric if (avp >= &npvp[MAXATOM]) 108216889Seric { 1083*56326Seric toolong: 108416889Seric syserr("rewrite: expansion too long"); 108516889Seric return; 108616889Seric } 1087*56326Seric #ifdef MACVALUE 1088*56326Seric if (*rp == MACVALUE) 1089*56326Seric { 1090*56326Seric char *p = macvalue(rp[1], CurEnv); 1091*56326Seric 1092*56326Seric if (tTd(21, 2)) 1093*56326Seric printf("expanding runtime macro '%c' to \"%s\"\n", 1094*56326Seric rp[1], p ? p : "(null)"); 1095*56326Seric if (p) 1096*56326Seric *avp++ = p; 1097*56326Seric } 1098*56326Seric else 1099*56326Seric #endif /* MACVALUE */ 1100*56326Seric *avp++ = rp; 11018226Seric } 11029374Seric } 11039374Seric *avp++ = NULL; 110416914Seric 110516914Seric /* 1106*56326Seric ** Check for any hostname lookups. 110716914Seric */ 110816914Seric 110916914Seric for (rvp = npvp; *rvp != NULL; rvp++) 111016914Seric { 1111*56326Seric char **hbrvp, **ubrvp; 111216914Seric char **xpvp; 111316914Seric int trsize; 111417473Seric char *olddelimchar; 1115*56326Seric char hbuf[MAXNAME + 1], ubuf[MAXNAME + 1]; 111616914Seric char *pvpb1[MAXATOM + 1]; 111717174Seric char pvpbuf[PSBUFSIZE]; 1118*56326Seric bool match, defaultpart; 1119*56326Seric char begintype; 1120*56326Seric char db = '\0'; 112116914Seric 1122*56326Seric if (**rvp != HOSTBEGIN && **rvp != KEYBEGIN) 112316914Seric continue; 112416914Seric 112516914Seric /* 1126*56326Seric ** Got a hostname or database lookup. 112716914Seric ** 112816914Seric ** This could be optimized fairly easily. 112916914Seric */ 113016914Seric 1131*56326Seric begintype = **rvp; 113216914Seric hbrvp = rvp; 1133*56326Seric ubrvp = NULL; 1134*56326Seric 1135*56326Seric /* read database name if that's what we're up for */ 1136*56326Seric if (begintype == KEYBEGIN) 113753654Seric { 1138*56326Seric if (*++rvp != NULL) 1139*56326Seric db = **rvp; 114053654Seric } 114116914Seric 114216914Seric /* extract the match part */ 1143*56326Seric if (begintype == HOSTBEGIN) 1144*56326Seric while (*++rvp != NULL && **rvp != HOSTEND && 1145*56326Seric **rvp != CANONUSER) 1146*56326Seric continue; 1147*56326Seric else 1148*56326Seric while (*++rvp != NULL && **rvp != KEYEND && 1149*56326Seric **rvp != CANONHOST && **rvp != CANONUSER) 1150*56326Seric continue; 1151*56326Seric /* got a sprintf argument? */ 1152*56326Seric if (**rvp == CANONHOST) 115353654Seric { 1154*56326Seric *rvp = NULL; 1155*56326Seric ubrvp = rvp+1; 1156*56326Seric while (*++rvp != NULL && **rvp != KEYEND && 1157*56326Seric **rvp != CANONUSER) 1158*56326Seric continue; 115953654Seric } 1160*56326Seric defaultpart = **rvp == CANONUSER; 116116914Seric if (*rvp != NULL) 116216914Seric *rvp++ = NULL; 116316914Seric 116416914Seric /* save the remainder of the input string */ 116516914Seric trsize = (int) (avp - rvp + 1) * sizeof *rvp; 116616914Seric bcopy((char *) rvp, (char *) pvpb1, trsize); 116716914Seric 1168*56326Seric /* Look it up (lowercase version) */ 1169*56326Seric cataddr(hbrvp + (begintype == HOSTBEGIN ? 1 : 2), 1170*56326Seric hbuf, sizeof hbuf); 1171*56326Seric if (begintype == HOSTBEGIN) 1172*56326Seric #ifdef VMUNIX 1173*56326Seric match = maphostname(hbuf, sizeof hbuf); 1174*56326Seric #else /* !VMUNIX */ 1175*56326Seric match = FALSE; 1176*56326Seric #endif /* VMUNIX */ 117753654Seric else 117851317Seric { 1179*56326Seric if (ubrvp == NULL) 1180*56326Seric { 1181*56326Seric /* no sprintf argument part */ 1182*56326Seric match = (mapkey(db, hbuf, sizeof hbuf, NULL) != NULL); 1183*56326Seric } 118453654Seric else 1185*56326Seric { 1186*56326Seric cataddr(ubrvp, ubuf, sizeof ubuf); 1187*56326Seric match = (mapkey(db, hbuf, sizeof hbuf, ubuf) != NULL); 1188*56326Seric } 118953654Seric } 1190*56326Seric if (match || !defaultpart) 119153654Seric { 1192*56326Seric /* scan the new route/host name */ 1193*56326Seric olddelimchar = DelimChar; 1194*56326Seric xpvp = prescan(hbuf, '\0', pvpbuf); 1195*56326Seric DelimChar = olddelimchar; 119653654Seric if (xpvp == NULL) 119751317Seric { 1198*56326Seric syserr("rewrite: cannot prescan %s: %s", 1199*56326Seric begintype == HOSTBEGIN ? 1200*56326Seric "new hostname" : 1201*56326Seric "dbm lookup result", 1202*56326Seric hbuf); 1203*56326Seric return; 120451317Seric } 120551317Seric 120616914Seric /* append it to the token list */ 1207*56326Seric for (avp = hbrvp; *xpvp != NULL; xpvp++) 1208*56326Seric { 120917174Seric *avp++ = newstr(*xpvp); 121016920Seric if (avp >= &npvp[MAXATOM]) 121116914Seric goto toolong; 1212*56326Seric } 121317174Seric } 1214*56326Seric else 1215*56326Seric avp = hbrvp; 121616914Seric 121716914Seric /* restore the old trailing information */ 1218*56326Seric rvp = avp - 1; 1219*56326Seric for (xpvp = pvpb1; *xpvp != NULL; xpvp++) 1220*56326Seric { 1221*56326Seric if (defaultpart && (begintype == HOSTBEGIN ? 1222*56326Seric **xpvp == HOSTEND : 1223*56326Seric **xpvp == KEYEND)) 1224*56326Seric { 1225*56326Seric defaultpart = FALSE; 1226*56326Seric rvp = avp - 1; 1227*56326Seric } 1228*56326Seric else if (!defaultpart || !match) 1229*56326Seric *avp++ = *xpvp; 123016920Seric if (avp >= &npvp[MAXATOM]) 123116914Seric goto toolong; 1232*56326Seric } 1233*56326Seric *avp++ = NULL; 123417174Seric 1235*56326Seric /*break;*/ 123616914Seric } 123716914Seric 123816914Seric /* 123916914Seric ** Check for subroutine calls. 1240*56326Seric ** Then copy vector back into original space. 124116914Seric */ 124216914Seric 1243*56326Seric callsubr(npvp); 1244*56326Seric 1245*56326Seric for (avp = npvp; *avp++ != NULL;); 124617348Seric bcopy((char *) npvp, (char *) pvp, 124716900Seric (int) (avp - npvp) * sizeof *avp); 1248*56326Seric 12499374Seric if (tTd(21, 4)) 12509374Seric { 12519374Seric printf("rewritten as:"); 1252*56326Seric printcav(pvp); 12539374Seric } 1254297Seric } 12558069Seric 12569279Seric if (OpMode == MD_TEST || tTd(21, 2)) 12578069Seric { 12588959Seric printf("rewrite: ruleset %2d returns:", ruleset); 1259*56326Seric printcav(pvp); 12608069Seric } 12613149Seric } 12623149Seric /* 1263*56326Seric ** CALLSUBR -- call subroutines in rewrite vector 1264*56326Seric ** 1265*56326Seric ** Parameters: 1266*56326Seric ** pvp -- pointer to token vector. 1267*56326Seric ** 1268*56326Seric ** Returns: 1269*56326Seric ** none. 1270*56326Seric ** 1271*56326Seric ** Side Effects: 1272*56326Seric ** pvp is modified. 1273*56326Seric */ 1274*56326Seric 1275*56326Seric static void 1276*56326Seric callsubr(pvp) 1277*56326Seric char **pvp; 1278*56326Seric { 1279*56326Seric char **rvp; 1280*56326Seric int subr; 1281*56326Seric 1282*56326Seric for (; *pvp != NULL; pvp++) 1283*56326Seric if (**pvp == CALLSUBR && pvp[1] != NULL && isdigit(pvp[1][0])) 1284*56326Seric { 1285*56326Seric subr = atoi(pvp[1]); 1286*56326Seric 1287*56326Seric if (tTd(21, 3)) 1288*56326Seric printf("-----callsubr %d\n", subr); 1289*56326Seric 1290*56326Seric /* 1291*56326Seric ** Take care of possible inner calls. 1292*56326Seric */ 1293*56326Seric callsubr(pvp+2); 1294*56326Seric 1295*56326Seric /* 1296*56326Seric ** Move vector up over calling opcode. 1297*56326Seric */ 1298*56326Seric for (rvp = pvp+2; *rvp != NULL; rvp++) 1299*56326Seric rvp[-2] = rvp[0]; 1300*56326Seric rvp[-2] = NULL; 1301*56326Seric 1302*56326Seric /* 1303*56326Seric ** Call inferior ruleset. 1304*56326Seric */ 1305*56326Seric _rewrite(pvp, subr); 1306*56326Seric 1307*56326Seric break; 1308*56326Seric } 1309*56326Seric } 1310*56326Seric /* 13113149Seric ** BUILDADDR -- build address from token vector. 13123149Seric ** 13133149Seric ** Parameters: 13143149Seric ** tv -- token vector. 13153149Seric ** a -- pointer to address descriptor to fill. 13163149Seric ** If NULL, one will be allocated. 13173149Seric ** 13183149Seric ** Returns: 13194279Seric ** NULL if there was an error. 13204279Seric ** 'a' otherwise. 13213149Seric ** 13223149Seric ** Side Effects: 13233149Seric ** fills in 'a' 13243149Seric */ 13253149Seric 1326*56326Seric static ADDRESS * 13273149Seric buildaddr(tv, a) 13283149Seric register char **tv; 13293149Seric register ADDRESS *a; 13303149Seric { 13313149Seric static char buf[MAXNAME]; 13323149Seric struct mailer **mp; 13333149Seric register struct mailer *m; 13343149Seric 13353149Seric if (a == NULL) 13363149Seric a = (ADDRESS *) xalloc(sizeof *a); 133716889Seric bzero((char *) a, sizeof *a); 13383149Seric 13393149Seric /* figure out what net/mailer to use */ 1340*56326Seric if (*tv == NULL || **tv != CANONNET) 13414279Seric { 13423149Seric syserr("buildaddr: no net"); 13434279Seric return (NULL); 13444279Seric } 13453149Seric tv++; 134633725Sbostic if (!strcasecmp(*tv, "error")) 13474279Seric { 134810183Seric if (**++tv == CANONHOST) 134910183Seric { 135010183Seric setstat(atoi(*++tv)); 135110183Seric tv++; 135210183Seric } 1353*56326Seric buf[0] = '\0'; 1354*56326Seric for (; (*tv != NULL) && (**tv != CANONUSER); tv++) 1355*56326Seric { 1356*56326Seric if (buf[0] != '\0') 1357*56326Seric (void) strcat(buf, " "); 1358*56326Seric (void) strcat(buf, *tv); 1359*56326Seric } 136010183Seric if (**tv != CANONUSER) 13614279Seric syserr("buildaddr: error: no user"); 13624279Seric while (*++tv != NULL) 13634279Seric { 13644279Seric if (buf[0] != '\0') 13657005Seric (void) strcat(buf, " "); 13667005Seric (void) strcat(buf, *tv); 13674279Seric } 1368*56326Seric #ifdef LOG 1369*56326Seric if (LogLevel > 8) 1370*56326Seric syslog (LOG_DEBUG, "%s: Trace: $#ERROR $: %s", 1371*56326Seric CurEnv->e_id, buf); 1372*56326Seric #endif /* LOG */ 13734279Seric usrerr(buf); 13744279Seric return (NULL); 13754279Seric } 13764598Seric for (mp = Mailer; (m = *mp++) != NULL; ) 13773149Seric { 137833725Sbostic if (!strcasecmp(m->m_name, *tv)) 13793149Seric break; 13803149Seric } 13813149Seric if (m == NULL) 13824279Seric { 138324944Seric syserr("buildaddr: unknown mailer %s", *tv); 13844279Seric return (NULL); 13854279Seric } 13864598Seric a->q_mailer = m; 13873149Seric 13883149Seric /* figure out what host (if any) */ 1389*56326Seric if (**++tv != CANONHOST) 13903149Seric { 1391*56326Seric if (!bitnset(M_LOCAL, m->m_flags)) 13924279Seric { 13933149Seric syserr("buildaddr: no host"); 13944279Seric return (NULL); 13954279Seric } 1396*56326Seric else 1397*56326Seric a->q_host = NULL; 1398*56326Seric } 1399*56326Seric else 1400*56326Seric { 14015704Seric buf[0] = '\0'; 1402*56326Seric while (*++tv != NULL && **tv != CANONUSER) 1403*56326Seric (void) strcat(buf, *tv); 14045704Seric a->q_host = newstr(buf); 14053149Seric } 14063149Seric 14073149Seric /* figure out the user */ 140836615Sbostic if (*tv == NULL || **tv != CANONUSER) 14094279Seric { 14103149Seric syserr("buildaddr: no user"); 14114279Seric return (NULL); 14124279Seric } 141319040Seric 1414*56326Seric /* define tohost before running mailer rulesets */ 1415*56326Seric define('h', a->q_host, CurEnv); 141651317Seric 141719040Seric /* rewrite according recipient mailer rewriting rules */ 141819040Seric rewrite(++tv, 2); 1419*56326Seric if (m->m_re_rwset > 0) 1420*56326Seric rewrite(tv, m->m_re_rwset); 142119040Seric rewrite(tv, 4); 142219040Seric 142319040Seric /* save the result for the command line/RCPT argument */ 142411278Seric cataddr(tv, buf, sizeof buf); 14253149Seric a->q_user = buf; 14263149Seric 14273149Seric return (a); 14283149Seric } 14293188Seric /* 14304228Seric ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 14314228Seric ** 14324228Seric ** Parameters: 14334228Seric ** pvp -- parameter vector to rebuild. 14344228Seric ** buf -- buffer to build the string into. 14354228Seric ** sz -- size of buf. 14364228Seric ** 14374228Seric ** Returns: 14384228Seric ** none. 14394228Seric ** 14404228Seric ** Side Effects: 14414228Seric ** Destroys buf. 14424228Seric */ 14434228Seric 1444*56326Seric void 14454228Seric cataddr(pvp, buf, sz) 14464228Seric char **pvp; 14474228Seric char *buf; 14484228Seric register int sz; 14494228Seric { 14504228Seric bool oatomtok = FALSE; 1451*56326Seric bool natomtok; 14524228Seric register int i; 14534228Seric register char *p; 14544228Seric 14558423Seric if (pvp == NULL) 14568423Seric { 145723109Seric (void) strcpy(buf, ""); 14588423Seric return; 14598423Seric } 14604228Seric p = buf; 146111156Seric sz -= 2; 14624228Seric while (*pvp != NULL && (i = strlen(*pvp)) < sz) 14634228Seric { 14648078Seric natomtok = (toktype(**pvp) == ATM); 14654228Seric if (oatomtok && natomtok) 14669042Seric *p++ = SpaceSub; 14674228Seric (void) strcpy(p, *pvp); 14684228Seric oatomtok = natomtok; 14694228Seric p += i; 147011156Seric sz -= i + 1; 14714228Seric pvp++; 14724228Seric } 14734228Seric *p = '\0'; 14744228Seric } 14754228Seric /* 14763188Seric ** SAMEADDR -- Determine if two addresses are the same 14773188Seric ** 14783188Seric ** This is not just a straight comparison -- if the mailer doesn't 14793188Seric ** care about the host we just ignore it, etc. 14803188Seric ** 14813188Seric ** Parameters: 14823188Seric ** a, b -- pointers to the internal forms to compare. 14833188Seric ** 14843188Seric ** Returns: 14853188Seric ** TRUE -- they represent the same mailbox. 14863188Seric ** FALSE -- they don't. 14873188Seric ** 14883188Seric ** Side Effects: 14893188Seric ** none. 14903188Seric */ 14913188Seric 14923188Seric bool 14939374Seric sameaddr(a, b) 14943188Seric register ADDRESS *a; 14953188Seric register ADDRESS *b; 14963188Seric { 14973188Seric /* if they don't have the same mailer, forget it */ 14983188Seric if (a->q_mailer != b->q_mailer) 14993188Seric return (FALSE); 15003188Seric 15013188Seric /* if the user isn't the same, we can drop out */ 1502*56326Seric if (strcasecmp(a->q_user, b->q_user)) 15033188Seric return (FALSE); 15043188Seric 15053188Seric /* if the mailer ignores hosts, we have succeeded! */ 150610690Seric if (bitnset(M_LOCAL, a->q_mailer->m_flags)) 15073188Seric return (TRUE); 15083188Seric 15093188Seric /* otherwise compare hosts (but be careful for NULL ptrs) */ 15103188Seric if (a->q_host == NULL || b->q_host == NULL) 15113188Seric return (FALSE); 1512*56326Seric if (strcasecmp(a->q_host, b->q_host)) 15133188Seric return (FALSE); 15143188Seric 15153188Seric return (TRUE); 15163188Seric } 15173234Seric /* 15183234Seric ** PRINTADDR -- print address (for debugging) 15193234Seric ** 15203234Seric ** Parameters: 15213234Seric ** a -- the address to print 15223234Seric ** follow -- follow the q_next chain. 15233234Seric ** 15243234Seric ** Returns: 15253234Seric ** none. 15263234Seric ** 15273234Seric ** Side Effects: 15283234Seric ** none. 15293234Seric */ 15303234Seric 1531*56326Seric void 15323234Seric printaddr(a, follow) 15333234Seric register ADDRESS *a; 15343234Seric bool follow; 15353234Seric { 15365001Seric bool first = TRUE; 15375001Seric 15383234Seric while (a != NULL) 15393234Seric { 15405001Seric first = FALSE; 15414443Seric printf("%x=", a); 15424085Seric (void) fflush(stdout); 154340973Sbostic printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n", 154440973Sbostic a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name, 154540973Sbostic a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>"); 15468181Seric printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags, 15478181Seric a->q_alias); 15488181Seric printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home, 15498181Seric a->q_fullname); 15504996Seric 15513234Seric if (!follow) 15523234Seric return; 15534996Seric a = a->q_next; 15543234Seric } 15555001Seric if (first) 15564443Seric printf("[NULL]\n"); 15573234Seric } 15584317Seric 15597682Seric /* 15607682Seric ** REMOTENAME -- return the name relative to the current mailer 15617682Seric ** 15627682Seric ** Parameters: 15637682Seric ** name -- the name to translate. 15648069Seric ** m -- the mailer that we want to do rewriting relative 15658069Seric ** to. 15668069Seric ** senderaddress -- if set, uses the sender rewriting rules 15678069Seric ** rather than the recipient rewriting rules. 156810310Seric ** canonical -- if set, strip out any comment information, 156910310Seric ** etc. 1570*56326Seric ** headeraddress -- if set, use header specific rewriting 1571*56326Seric ** rulesets and uurelativize if M_RELATIVIZE is set. 15727682Seric ** 15737682Seric ** Returns: 15747682Seric ** the text string representing this address relative to 15757682Seric ** the receiving mailer. 15767682Seric ** 15777682Seric ** Side Effects: 15787682Seric ** none. 15797682Seric ** 15807682Seric ** Warnings: 15817682Seric ** The text string returned is tucked away locally; 15827682Seric ** copy it if you intend to save it. 15837682Seric */ 15847682Seric 15857682Seric char * 1586*56326Seric remotename(name, m, senderaddress, canonical, headeraddress) 15877682Seric char *name; 1588*56326Seric MAILER *m; 15898069Seric bool senderaddress; 159010310Seric bool canonical; 1591*56326Seric bool headeraddress; 15927682Seric { 15938069Seric register char **pvp; 15948069Seric char *fancy; 1595*56326Seric char *oldg = macvalue('g', CurEnv); 15967682Seric static char buf[MAXNAME]; 15977682Seric char lbuf[MAXNAME]; 159816914Seric char pvpbuf[PSBUFSIZE]; 15997682Seric 16007755Seric if (tTd(12, 1)) 16017755Seric printf("remotename(%s)\n", name); 16027755Seric 160310177Seric /* don't do anything if we are tagging it as special */ 1604*56326Seric if ((senderaddress ? 1605*56326Seric (headeraddress ? m->m_sh_rwset : m->m_se_rwset) : 1606*56326Seric (headeraddress ? m->m_rh_rwset : m->m_re_rwset)) < 0) 160710177Seric return (name); 160810177Seric 16097682Seric /* 16108181Seric ** Do a heuristic crack of this name to extract any comment info. 16118181Seric ** This will leave the name as a comment and a $g macro. 16127889Seric */ 16137889Seric 161410310Seric if (canonical) 161516155Seric fancy = "\001g"; 161610310Seric else 161710310Seric fancy = crackaddr(name); 16187889Seric 16198181Seric /* 16208181Seric ** Turn the name into canonical form. 16218181Seric ** Normally this will be RFC 822 style, i.e., "user@domain". 16228181Seric ** If this only resolves to "user", and the "C" flag is 16238181Seric ** specified in the sending mailer, then the sender's 16248181Seric ** domain will be appended. 16258181Seric */ 16268181Seric 162716914Seric pvp = prescan(name, '\0', pvpbuf); 16287889Seric if (pvp == NULL) 16297889Seric return (name); 16308181Seric rewrite(pvp, 3); 1631*56326Seric if (CurEnv->e_fromdomain != NULL) 16328181Seric { 16338181Seric /* append from domain to this address */ 16348181Seric register char **pxp = pvp; 16358181Seric 16369594Seric /* see if there is an "@domain" in the current name */ 16378181Seric while (*pxp != NULL && strcmp(*pxp, "@") != 0) 16388181Seric pxp++; 16398181Seric if (*pxp == NULL) 16408181Seric { 16419594Seric /* no.... append the "@domain" from the sender */ 1642*56326Seric register char **qxq = CurEnv->e_fromdomain; 16438181Seric 16449594Seric while ((*pxp++ = *qxq++) != NULL) 16459594Seric continue; 164611726Seric rewrite(pvp, 3); 16478181Seric } 16488181Seric } 16498181Seric 16508181Seric /* 16518959Seric ** Do more specific rewriting. 1652*56326Seric ** Rewrite using ruleset 1 or 2 for envelope addresses and 1653*56326Seric ** 5 or 6 for header addresses depending on whether this 1654*56326Seric ** is a sender address or not. 16558181Seric ** Then run it through any receiving-mailer-specific rulesets. 16568181Seric */ 16578181Seric 16588069Seric if (senderaddress) 16597755Seric { 1660*56326Seric if (headeraddress) 1661*56326Seric { 1662*56326Seric rewrite(pvp, SplitRewriting ? 5 : 1); 1663*56326Seric if (m->m_sh_rwset > 0) 1664*56326Seric rewrite(pvp, m->m_sh_rwset); 1665*56326Seric } 1666*56326Seric else 1667*56326Seric { 1668*56326Seric rewrite(pvp, 1); 1669*56326Seric if (m->m_se_rwset > 0) 1670*56326Seric rewrite(pvp, m->m_se_rwset); 1671*56326Seric } 16728069Seric } 16738069Seric else 16748069Seric { 1675*56326Seric if (headeraddress) 1676*56326Seric { 1677*56326Seric rewrite(pvp, SplitRewriting ? 6 : 2); 1678*56326Seric if (m->m_rh_rwset > 0) 1679*56326Seric rewrite(pvp, m->m_rh_rwset); 1680*56326Seric } 1681*56326Seric else 1682*56326Seric { 1683*56326Seric rewrite(pvp, 2); 1684*56326Seric if (m->m_re_rwset > 0) 1685*56326Seric rewrite(pvp, m->m_re_rwset); 1686*56326Seric } 16877682Seric } 16887682Seric 16898181Seric /* 16908959Seric ** Do any final sanitation the address may require. 16918959Seric ** This will normally be used to turn internal forms 16928959Seric ** (e.g., user@host.LOCAL) into external form. This 16938959Seric ** may be used as a default to the above rules. 16948959Seric */ 16958959Seric 16968959Seric rewrite(pvp, 4); 16978959Seric 16988959Seric /* 1699*56326Seric ** Check if we're supposed to do make the address 1700*56326Seric ** UUCP !-relative to the rcpt host vs ourselves. 1701*56326Seric */ 1702*56326Seric 1703*56326Seric if (headeraddress && bitnset(M_RELATIVIZE, m->m_flags)) 1704*56326Seric uurelativize("\001k", "\001h", pvp); 1705*56326Seric 1706*56326Seric /* 17078181Seric ** Now restore the comment information we had at the beginning. 17088181Seric */ 17098181Seric 17107682Seric cataddr(pvp, lbuf, sizeof lbuf); 1711*56326Seric define('g', lbuf, CurEnv); 1712*56326Seric expand(fancy, buf, &buf[sizeof buf - 1], CurEnv); 1713*56326Seric define('g', oldg, CurEnv); 17147682Seric 17157682Seric if (tTd(12, 1)) 17167755Seric printf("remotename => `%s'\n", buf); 17177682Seric return (buf); 17187682Seric } 171951317Seric /* 1720*56326Seric ** UURELATIVIZE -- Make an address !-relative to recipient/sender nodes 172151317Seric ** 172251317Seric ** Parameters: 1723*56326Seric ** from -- the sending node (usually "$k" or "$w") 1724*56326Seric ** to -- the receiving node (usually "$h") 1725*56326Seric ** pvp -- address vector 172651317Seric ** 172751317Seric ** Returns: 172851317Seric ** none. 1729*56326Seric ** 1730*56326Seric ** Side Effects: 1731*56326Seric ** The pvp is rewritten to be relative the "to" node 1732*56326Seric ** wrt the "from" node. In other words, if the pvp 1733*56326Seric ** is headed by "to!" that part is stripped; otherwise 1734*56326Seric ** "from!" is prepended. Exception: "to!user" addresses 1735*56326Seric ** with no '!'s in the user part are sent as is. 1736*56326Seric ** 1737*56326Seric ** Bugs: 1738*56326Seric ** The pvp may overflow, but we don't catch it. 173951317Seric */ 174051317Seric 1741*56326Seric static void 1742*56326Seric uurelativize(from, to, pvp) 1743*56326Seric const char *from, *to; 1744*56326Seric char **pvp; 174551317Seric { 1746*56326Seric register char **pxp = pvp; 1747*56326Seric char expfrom[MAXNAME], expto[MAXNAME]; 174851317Seric 1749*56326Seric expand(from, expfrom, &expfrom[sizeof expfrom - 1], CurEnv); 1750*56326Seric expand(to, expto, &expto[sizeof expto - 1], CurEnv); 175151317Seric 1752*56326Seric /* 1753*56326Seric * supposing that we've got something, should 1754*56326Seric * we add "from!" or remove "to!"? 1755*56326Seric */ 1756*56326Seric if (pvp[0] != NULL) 1757*56326Seric if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 || 1758*56326Seric /*strcasecmp?*/ strcmp(pvp[0], expto) != 0) 1759*56326Seric { 1760*56326Seric /* either local name, no UUCP address, */ 1761*56326Seric /* or not to "to!" ==> prepend address with "from!" */ 176251317Seric 1763*56326Seric /* already there? */ 1764*56326Seric if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 || 1765*56326Seric /*strcasecmp?*/ strcmp(pvp[0], expfrom) != 0) 1766*56326Seric { 176751317Seric 1768*56326Seric /* no, put it there */ 1769*56326Seric while (*pxp != NULL) 1770*56326Seric pxp++; 1771*56326Seric do 1772*56326Seric pxp[2] = *pxp; 1773*56326Seric while (pxp-- != pvp); 1774*56326Seric pvp[0] = newstr(expfrom); 1775*56326Seric pvp[1] = "!"; 1776*56326Seric } 1777*56326Seric } 1778*56326Seric else 1779*56326Seric { 1780*56326Seric /* address is to "to!" -- remove if not "to!user" */ 1781*56326Seric for (pxp = &pvp[2]; 1782*56326Seric *pxp != NULL && strcmp(*pxp, "!") != 0; pxp++) 1783*56326Seric ; 1784*56326Seric if (*pxp != NULL) 1785*56326Seric for (pxp = pvp; *pxp != NULL; pxp++) 1786*56326Seric *pxp = pxp[2]; 1787*56326Seric } 178851317Seric } 1789