|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T p
Length: 42980 (0xa7e4) Types: TextFile Names: »parseaddr.c,v«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit └─⟦bfebc70e2⟧ »EurOpenD3/mail/sendmail-5.65b+IDA-1.4.3.tar.Z« └─⟦f9e35cd84⟧ └─⟦this⟧ »sendmail/src/RCS/parseaddr.c,v«
head 5.13; branch 5.13.0; access; symbols UICSO:5.13.0 VANILLA:5.13; locks; strict; comment @ * @; 5.13 date 90.06.20.08.36.07; author paul; state Exp; branches 5.13.0.1; next ; 5.13.0.1 date 90.06.20.09.43.24; author paul; state Exp; branches; next 5.13.0.2; 5.13.0.2 date 90.07.06.19.12.11; author paul; state Exp; branches; next 5.13.0.3; 5.13.0.3 date 90.09.17.09.33.01; author paul; state Exp; branches; next 5.13.0.4; 5.13.0.4 date 90.10.13.18.46.49; author paul; state Exp; branches; next 5.13.0.5; 5.13.0.5 date 90.11.02.15.03.33; author paul; state Exp; branches; next 5.13.0.6; 5.13.0.6 date 90.11.11.10.36.58; author paul; state Exp; branches; next 5.13.0.7; 5.13.0.7 date 90.11.24.03.05.28; author paul; state Exp; branches; next 5.13.0.8; 5.13.0.8 date 90.11.26.20.36.53; author paul; state Exp; branches; next 5.13.0.9; 5.13.0.9 date 90.11.28.16.30.48; author paul; state Exp; branches; next 5.13.0.10; 5.13.0.10 date 91.02.17.02.35.50; author paul; state Exp; branches; next 5.13.0.11; 5.13.0.11 date 91.02.17.04.42.34; author paul; state Exp; branches; next 5.13.0.12; 5.13.0.12 date 91.03.06.15.16.42; author paul; state Exp; branches; next ; desc @@ 5.13 log @5.64 Berkeley release @ text @/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char sccsid[] = "@@(#)parseaddr.c 5.13 (Berkeley) 6/1/90"; #endif /* not lint */ # include "sendmail.h" /* ** PARSEADDR -- Parse an address ** ** Parses an address and breaks it up into three parts: a ** net to transmit the message on, the host to transmit it ** to, and a user on that host. These are loaded into an ** ADDRESS header with the values squirreled away if necessary. ** The "user" part may not be a real user; the process may ** just reoccur on that machine. For example, on a machine ** with an arpanet connection, the address ** csvax.bill@@berkeley ** will break up to a "user" of 'csvax.bill' and a host ** of 'berkeley' -- to be transmitted over the arpanet. ** ** Parameters: ** addr -- the address to parse. ** a -- a pointer to the address descriptor buffer. ** If NULL, a header will be created. ** copyf -- determines what shall be copied: ** -1 -- don't copy anything. The printname ** (q_paddr) is just addr, and the ** user & host are allocated internally ** to parse. ** 0 -- copy out the parsed user & host, but ** don't copy the printname. ** +1 -- copy everything. ** delim -- the character to terminate the address, passed ** to prescan. ** ** Returns: ** A pointer to the address descriptor header (`a' if ** `a' is non-NULL). ** NULL on error. ** ** Side Effects: ** none */ /* following delimiters are inherent to the internal algorithms */ # define DELIMCHARS "\001()<>,;\\\"\r\n" /* word delimiters */ ADDRESS * parseaddr(addr, a, copyf, delim) char *addr; register ADDRESS *a; int copyf; char delim; { register char **pvp; register struct mailer *m; char pvpbuf[PSBUFSIZE]; extern char **prescan(); extern ADDRESS *buildaddr(); /* ** Initialize and prescan address. */ CurEnv->e_to = addr; if (tTd(20, 1)) printf("\n--parseaddr(%s)\n", addr); pvp = prescan(addr, delim, pvpbuf); if (pvp == NULL) return (NULL); /* ** Apply rewriting rules. ** Ruleset 0 does basic parsing. It must resolve. */ rewrite(pvp, 3); rewrite(pvp, 0); /* ** See if we resolved to a real mailer. */ if (pvp[0][0] != CANONNET) { setstat(EX_USAGE); usrerr("cannot resolve name"); return (NULL); } /* ** Build canonical address from pvp. */ a = buildaddr(pvp, a); if (a == NULL) return (NULL); m = a->q_mailer; /* ** Make local copies of the host & user and then ** transport them out. */ if (copyf > 0) { extern char *DelimChar; char savec = *DelimChar; *DelimChar = '\0'; a->q_paddr = newstr(addr); *DelimChar = savec; } else a->q_paddr = addr; if (a->q_user == NULL) a->q_user = ""; if (a->q_host == NULL) a->q_host = ""; if (copyf >= 0) { a->q_host = newstr(a->q_host); if (a->q_user != a->q_paddr) a->q_user = newstr(a->q_user); } /* ** Convert host name to lower case if requested. ** User name will be done later. */ if (!bitnset(M_HST_UPPER, m->m_flags)) makelower(a->q_host); /* ** Compute return value. */ if (tTd(20, 1)) { printf("parseaddr-->"); printaddr(a, FALSE); } return (a); } \f /* ** LOWERADDR -- map UPPER->lower case on addresses as requested. ** ** Parameters: ** a -- address to be mapped. ** ** Returns: ** none. ** ** Side Effects: ** none. */ loweraddr(a) register ADDRESS *a; { register MAILER *m = a->q_mailer; if (!bitnset(M_USR_UPPER, m->m_flags)) makelower(a->q_user); } \f /* ** PRESCAN -- Prescan name and make it canonical ** ** Scans a name and turns it into a set of tokens. This process ** deletes blanks and comments (in parentheses). ** ** This routine knows about quoted strings and angle brackets. ** ** There are certain subtleties to this routine. The one that ** comes to mind now is that backslashes on the ends of names ** are silently stripped off; this is intentional. The problem ** is that some versions of sndmsg (like at LBL) set the kill ** character to something other than @@ when reading addresses; ** so people type "csvax.eric\@@berkeley" -- which screws up the ** berknet mailer. ** ** Parameters: ** addr -- the name to chomp. ** delim -- the delimiter for the address, normally ** '\0' or ','; \0 is accepted in any case. ** If '\t' then we are reading the .cf file. ** pvpbuf -- place to put the saved text -- note that ** the pointers are static. ** ** Returns: ** A pointer to a vector of tokens. ** NULL on error. ** ** Side Effects: ** sets DelimChar to point to the character matching 'delim'. */ /* states and character types */ # define OPR 0 /* operator */ # define ATM 1 /* atom */ # define QST 2 /* in quoted string */ # define SPC 3 /* chewing up spaces */ # define ONE 4 /* pick up one character */ # define NSTATES 5 /* number of states */ # define TYPE 017 /* mask to select state type */ /* meta bits for table */ # define M 020 /* meta character; don't pass through */ # define B 040 /* cause a break */ # define MB M|B /* meta-break */ 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, }; # define NOCHAR -1 /* signal nothing in lookahead token */ char *DelimChar; /* set to point to the delimiter */ char ** prescan(addr, delim, pvpbuf) char *addr; char delim; char pvpbuf[]; { register char *p; register char *q; register int c; char **avp; bool bslashmode; int cmntcnt; int anglecnt; char *tok; int state; int newstate; static char *av[MAXATOM+1]; extern int errno; /* make sure error messages don't have garbage on them */ errno = 0; q = pvpbuf; bslashmode = FALSE; cmntcnt = 0; anglecnt = 0; avp = av; state = OPR; c = NOCHAR; p = addr; if (tTd(22, 45)) { printf("prescan: "); xputs(p); (void) putchar('\n'); } do { /* read a token */ tok = q; for (;;) { /* store away any old lookahead character */ if (c != NOCHAR) { /* see if there is room */ if (q >= &pvpbuf[PSBUFSIZE - 5]) { usrerr("Address too long"); DelimChar = p; return (NULL); } /* squirrel it away */ *q++ = c; } /* read a new input character */ c = *p++; if (c == '\0') break; c &= ~0200; if (tTd(22, 101)) printf("c=%c, s=%d; ", c, state); /* chew up special characters */ *q = '\0'; if (bslashmode) { /* kludge \! for naive users */ if (c != '!') c |= 0200; bslashmode = FALSE; } else if (c == '\\') { bslashmode = TRUE; c = NOCHAR; } else if (state == QST) { /* do nothing, just avoid next clauses */ } else if (c == '(') { cmntcnt++; c = NOCHAR; } else if (c == ')') { if (cmntcnt <= 0) { usrerr("Unbalanced ')'"); DelimChar = p; return (NULL); } else cmntcnt--; } else if (cmntcnt > 0) c = NOCHAR; else if (c == '<') anglecnt++; else if (c == '>') { if (anglecnt <= 0) { usrerr("Unbalanced '>'"); DelimChar = p; return (NULL); } anglecnt--; } else if (delim == ' ' && isspace(c)) c = ' '; if (c == NOCHAR) continue; /* see if this is end of input */ if (c == delim && anglecnt <= 0 && state != QST) break; newstate = StateTab[state][toktype(c)]; if (tTd(22, 101)) printf("ns=%02o\n", newstate); state = newstate & TYPE; if (bitset(M, newstate)) c = NOCHAR; if (bitset(B, newstate)) break; } /* new token */ if (tok != q) { *q++ = '\0'; if (tTd(22, 36)) { printf("tok="); xputs(tok); (void) putchar('\n'); } if (avp >= &av[MAXATOM]) { syserr("prescan: too many tokens"); DelimChar = p; return (NULL); } *avp++ = tok; } } while (c != '\0' && (c != delim || anglecnt > 0)); *avp = NULL; DelimChar = --p; if (cmntcnt > 0) usrerr("Unbalanced '('"); else if (anglecnt > 0) usrerr("Unbalanced '<'"); else if (state == QST) usrerr("Unbalanced '\"'"); else if (av[0] != NULL) return (av); return (NULL); } \f /* ** TOKTYPE -- return token type ** ** Parameters: ** c -- the character in question. ** ** Returns: ** Its type. ** ** Side Effects: ** none. */ toktype(c) register char c; { static char buf[50]; static bool firstime = TRUE; if (firstime) { firstime = FALSE; expand("\001o", buf, &buf[sizeof buf - 1], CurEnv); (void) strcat(buf, DELIMCHARS); } if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS) return (ONE); if (c == '"') return (QST); if (!isascii(c)) return (ATM); if (isspace(c) || c == ')') return (SPC); if (iscntrl(c) || index(buf, c) != NULL) return (OPR); return (ATM); } \f /* ** REWRITE -- apply rewrite rules to token vector. ** ** This routine is an ordered production system. Each rewrite ** rule has a LHS (called the pattern) and a RHS (called the ** rewrite); 'rwr' points the the current rewrite rule. ** ** For each rewrite rule, 'avp' points the address vector we ** are trying to match against, and 'pvp' points to the pattern. ** If pvp points to a special match value (MATCHZANY, MATCHANY, ** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp ** matched is saved away in the match vector (pointed to by 'mvp'). ** ** When a match between avp & pvp does not match, we try to ** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS ** we must also back out the match in mvp. If we reach a ** MATCHANY or MATCHZANY we just extend the match and start ** over again. ** ** When we finally match, we rewrite the address vector ** and try over again. ** ** Parameters: ** pvp -- pointer to token vector. ** ** Returns: ** none. ** ** Side Effects: ** pvp is modified. */ struct match { char **first; /* first token matched */ char **last; /* last token matched */ }; # define MAXMATCH 9 /* max params per rewrite */ rewrite(pvp, ruleset) char **pvp; int ruleset; { register char *ap; /* address pointer */ register char *rp; /* rewrite pointer */ register char **avp; /* address vector pointer */ register char **rvp; /* rewrite vector pointer */ register struct match *mlp; /* cur ptr into mlist */ register struct rewrite *rwr; /* pointer to current rewrite rule */ struct match mlist[MAXMATCH]; /* stores match on LHS */ char *npvp[MAXATOM+1]; /* temporary space for rebuild */ if (OpMode == MD_TEST || tTd(21, 2)) { printf("rewrite: ruleset %2d input:", ruleset); printav(pvp); } if (pvp == NULL) return; /* ** Run through the list of rewrite rules, applying ** any that match. */ for (rwr = RewriteRules[ruleset]; rwr != NULL; ) { if (tTd(21, 12)) { printf("-----trying rule:"); printav(rwr->r_lhs); } /* try to match on this rule */ mlp = mlist; rvp = rwr->r_lhs; avp = pvp; while ((ap = *avp) != NULL || *rvp != NULL) { rp = *rvp; if (tTd(21, 35)) { printf("ap="); xputs(ap); printf(", rp="); xputs(rp); printf("\n"); } if (rp == NULL) { /* end-of-pattern before end-of-address */ goto backup; } if (ap == NULL && *rp != MATCHZANY) { /* end-of-input */ break; } switch (*rp) { register STAB *s; case MATCHCLASS: case MATCHNCLASS: /* match any token in (not in) a class */ s = stab(ap, ST_CLASS, ST_FIND); if (s == NULL || !bitnset(rp[1], s->s_class)) { if (*rp == MATCHCLASS) goto backup; } else if (*rp == MATCHNCLASS) goto backup; /* explicit fall-through */ case MATCHONE: case MATCHANY: /* match exactly one token */ mlp->first = avp; mlp->last = avp++; mlp++; break; case MATCHZANY: /* match zero or more tokens */ mlp->first = avp; mlp->last = avp - 1; mlp++; break; default: /* must have exact match */ if (strcasecmp(rp, ap)) goto backup; avp++; break; } /* successful match on this token */ rvp++; continue; backup: /* match failed -- back up */ while (--rvp >= rwr->r_lhs) { rp = *rvp; if (*rp == MATCHANY || *rp == MATCHZANY) { /* extend binding and continue */ avp = ++mlp[-1].last; avp++; rvp++; break; } avp--; if (*rp == MATCHONE || *rp == MATCHCLASS || *rp == MATCHNCLASS) { /* back out binding */ mlp--; } } if (rvp < rwr->r_lhs) { /* total failure to match */ break; } } /* ** See if we successfully matched */ if (rvp < rwr->r_lhs || *rvp != NULL) { if (tTd(21, 10)) printf("----- rule fails\n"); rwr = rwr->r_next; continue; } rvp = rwr->r_rhs; if (tTd(21, 12)) { printf("-----rule matches:"); printav(rvp); } rp = *rvp; if (*rp == CANONUSER) { rvp++; rwr = rwr->r_next; } else if (*rp == CANONHOST) { rvp++; rwr = NULL; } else if (*rp == CANONNET) rwr = NULL; /* substitute */ for (avp = npvp; *rvp != NULL; rvp++) { register struct match *m; register char **pp; rp = *rvp; if (*rp == MATCHREPL) { /* substitute from LHS */ m = &mlist[rp[1] - '1']; if (m >= mlp) { syserr("rewrite: ruleset %d: replacement out of bounds", ruleset); return; } if (tTd(21, 15)) { printf("$%c:", rp[1]); pp = m->first; while (pp <= m->last) { printf(" %x=\"", *pp); (void) fflush(stdout); printf("%s\"", *pp++); } printf("\n"); } pp = m->first; while (pp <= m->last) { if (avp >= &npvp[MAXATOM]) { syserr("rewrite: expansion too long"); return; } *avp++ = *pp++; } } else { /* vanilla replacement */ if (avp >= &npvp[MAXATOM]) { toolong: syserr("rewrite: expansion too long"); return; } *avp++ = rp; } } *avp++ = NULL; /* ** Check for any hostname lookups. */ for (rvp = npvp; *rvp != NULL; rvp++) { char **hbrvp; char **xpvp; int trsize; char *olddelimchar; char buf[MAXNAME + 1]; char *pvpb1[MAXATOM + 1]; char pvpbuf[PSBUFSIZE]; extern char *DelimChar; if (**rvp != HOSTBEGIN) continue; /* ** Got a hostname lookup. ** ** This could be optimized fairly easily. */ hbrvp = rvp; /* extract the match part */ while (*++rvp != NULL && **rvp != HOSTEND) continue; if (*rvp != NULL) *rvp++ = NULL; /* save the remainder of the input string */ trsize = (int) (avp - rvp + 1) * sizeof *rvp; bcopy((char *) rvp, (char *) pvpb1, trsize); /* look it up */ cataddr(++hbrvp, buf, sizeof buf); maphostname(buf, sizeof buf); /* scan the new host name */ olddelimchar = DelimChar; xpvp = prescan(buf, '\0', pvpbuf); DelimChar = olddelimchar; if (xpvp == NULL) { syserr("rewrite: cannot prescan canonical hostname: %s", buf); return; } /* append it to the token list */ for (avp = --hbrvp; *xpvp != NULL; xpvp++) { *avp++ = newstr(*xpvp); if (avp >= &npvp[MAXATOM]) goto toolong; } /* restore the old trailing information */ for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) if (avp >= &npvp[MAXATOM]) goto toolong; break; } /* ** Check for subroutine calls. */ if (*npvp != NULL && **npvp == CALLSUBR) { bcopy((char *) &npvp[2], (char *) pvp, (int) (avp - npvp - 2) * sizeof *avp); if (tTd(21, 3)) printf("-----callsubr %s\n", npvp[1]); rewrite(pvp, atoi(npvp[1])); } else { bcopy((char *) npvp, (char *) pvp, (int) (avp - npvp) * sizeof *avp); } if (tTd(21, 4)) { printf("rewritten as:"); printav(pvp); } } if (OpMode == MD_TEST || tTd(21, 2)) { printf("rewrite: ruleset %2d returns:", ruleset); printav(pvp); } } \f /* ** BUILDADDR -- build address from token vector. ** ** Parameters: ** tv -- token vector. ** a -- pointer to address descriptor to fill. ** If NULL, one will be allocated. ** ** Returns: ** NULL if there was an error. ** 'a' otherwise. ** ** Side Effects: ** fills in 'a' */ ADDRESS * buildaddr(tv, a) register char **tv; register ADDRESS *a; { static char buf[MAXNAME]; struct mailer **mp; register struct mailer *m; if (a == NULL) a = (ADDRESS *) xalloc(sizeof *a); bzero((char *) a, sizeof *a); /* figure out what net/mailer to use */ if (**tv != CANONNET) { syserr("buildaddr: no net"); return (NULL); } tv++; if (!strcasecmp(*tv, "error")) { if (**++tv == CANONHOST) { setstat(atoi(*++tv)); tv++; } if (**tv != CANONUSER) syserr("buildaddr: error: no user"); buf[0] = '\0'; while (*++tv != NULL) { if (buf[0] != '\0') (void) strcat(buf, " "); (void) strcat(buf, *tv); } usrerr(buf); return (NULL); } for (mp = Mailer; (m = *mp++) != NULL; ) { if (!strcasecmp(m->m_name, *tv)) break; } if (m == NULL) { syserr("buildaddr: unknown mailer %s", *tv); return (NULL); } a->q_mailer = m; /* figure out what host (if any) */ tv++; if (!bitnset(M_LOCAL, m->m_flags)) { if (**tv++ != CANONHOST) { syserr("buildaddr: no host"); return (NULL); } buf[0] = '\0'; while (*tv != NULL && **tv != CANONUSER) (void) strcat(buf, *tv++); a->q_host = newstr(buf); } else a->q_host = NULL; /* figure out the user */ if (*tv == NULL || **tv != CANONUSER) { syserr("buildaddr: no user"); return (NULL); } /* rewrite according recipient mailer rewriting rules */ rewrite(++tv, 2); if (m->m_r_rwset > 0) rewrite(tv, m->m_r_rwset); rewrite(tv, 4); /* save the result for the command line/RCPT argument */ cataddr(tv, buf, sizeof buf); a->q_user = buf; return (a); } \f /* ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) ** ** Parameters: ** pvp -- parameter vector to rebuild. ** buf -- buffer to build the string into. ** sz -- size of buf. ** ** Returns: ** none. ** ** Side Effects: ** Destroys buf. */ cataddr(pvp, buf, sz) char **pvp; char *buf; register int sz; { bool oatomtok = FALSE; bool natomtok = FALSE; register int i; register char *p; if (pvp == NULL) { (void) strcpy(buf, ""); return; } p = buf; sz -= 2; while (*pvp != NULL && (i = strlen(*pvp)) < sz) { natomtok = (toktype(**pvp) == ATM); if (oatomtok && natomtok) *p++ = SpaceSub; (void) strcpy(p, *pvp); oatomtok = natomtok; p += i; sz -= i + 1; pvp++; } *p = '\0'; } \f /* ** SAMEADDR -- Determine if two addresses are the same ** ** This is not just a straight comparison -- if the mailer doesn't ** care about the host we just ignore it, etc. ** ** Parameters: ** a, b -- pointers to the internal forms to compare. ** ** Returns: ** TRUE -- they represent the same mailbox. ** FALSE -- they don't. ** ** Side Effects: ** none. */ bool sameaddr(a, b) register ADDRESS *a; register ADDRESS *b; { /* 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 (strcmp(a->q_user, b->q_user) != 0) return (FALSE); /* if the mailer ignores hosts, we have succeeded! */ if (bitnset(M_LOCAL, a->q_mailer->m_flags)) return (TRUE); /* otherwise compare hosts (but be careful for NULL ptrs) */ if (a->q_host == NULL || b->q_host == NULL) return (FALSE); if (strcmp(a->q_host, b->q_host) != 0) return (FALSE); return (TRUE); } \f /* ** PRINTADDR -- print address (for debugging) ** ** Parameters: ** a -- the address to print ** follow -- follow the q_next chain. ** ** Returns: ** none. ** ** Side Effects: ** none. */ printaddr(a, follow) register ADDRESS *a; bool follow; { bool first = TRUE; while (a != NULL) { first = FALSE; printf("%x=", a); (void) fflush(stdout); printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n", a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name, a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>"); printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags, a->q_alias); printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home, a->q_fullname); if (!follow) return; a = a->q_next; } if (first) printf("[NULL]\n"); } \f /* ** REMOTENAME -- return the name relative to the current mailer ** ** Parameters: ** name -- the name to translate. ** m -- the mailer that we want to do rewriting relative ** to. ** senderaddress -- if set, uses the sender rewriting rules ** rather than the recipient rewriting rules. ** canonical -- if set, strip out any comment information, ** etc. ** ** Returns: ** the text string representing this address relative to ** the receiving mailer. ** ** Side Effects: ** none. ** ** Warnings: ** The text string returned is tucked away locally; ** copy it if you intend to save it. */ char * remotename(name, m, senderaddress, canonical) char *name; struct mailer *m; bool senderaddress; bool canonical; { register char **pvp; char *fancy; extern char *macvalue(); char *oldg = macvalue('g', CurEnv); static char buf[MAXNAME]; char lbuf[MAXNAME]; char pvpbuf[PSBUFSIZE]; extern char **prescan(); 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) return (name); /* ** Do a heuristic crack of this name to extract any comment info. ** This will leave the name as a comment and a $g macro. */ if (canonical) fancy = "\001g"; else fancy = crackaddr(name); /* ** Turn the name into canonical form. ** Normally this will be RFC 822 style, i.e., "user@@domain". ** If this only resolves to "user", and the "C" flag is ** specified in the sending mailer, then the sender's ** domain will be appended. */ pvp = prescan(name, '\0', pvpbuf); if (pvp == NULL) return (name); rewrite(pvp, 3); if (CurEnv->e_fromdomain != NULL) { /* append from domain to this address */ register char **pxp = pvp; /* see if there is an "@@domain" in the current name */ while (*pxp != NULL && strcmp(*pxp, "@@") != 0) pxp++; if (*pxp == NULL) { /* no.... append the "@@domain" from the sender */ register char **qxq = CurEnv->e_fromdomain; while ((*pxp++ = *qxq++) != NULL) continue; rewrite(pvp, 3); } } /* ** Do more specific rewriting. ** 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 (senderaddress) { rewrite(pvp, 1); if (m->m_s_rwset > 0) rewrite(pvp, m->m_s_rwset); } else { rewrite(pvp, 2); if (m->m_r_rwset > 0) rewrite(pvp, m->m_r_rwset); } /* ** Do any final sanitation the address may require. ** This will normally be used to turn internal forms ** (e.g., user@@host.LOCAL) into external form. This ** may be used as a default to the above rules. */ rewrite(pvp, 4); /* ** Now restore the comment information we had at the beginning. */ cataddr(pvp, lbuf, sizeof lbuf); define('g', lbuf, CurEnv); expand(fancy, buf, &buf[sizeof buf - 1], CurEnv); define('g', oldg, CurEnv); if (tTd(12, 1)) printf("remotename => `%s'\n", buf); return (buf); } @ 5.13.0.1 log @IDA patches @ text @a444 4 #ifdef MACVALUE if (c == MACVALUE) return (ONE); #endif MACVALUE a494 1 static int nrw; a499 8 nrw = 0; _rewrite(pvp, ruleset); } _rewrite(pvp, ruleset) char **pvp; int ruleset; { a507 2 char tokbuf[MAXNAME+1]; /* for concatenated class tokens */ int nloops, nmatches = 0; /* for looping rule checks */ d512 1 a512 1 printcav(pvp); a516 10 if (++nrw > 100) { char buf[MAXLINE]; buf[0] = buf[MAXLINE-1] = 0; while (*pvp) strncat(buf, *pvp++, sizeof buf); syserr("address causes rewrite loop: <%s>", buf); return; } d527 1 a527 1 printcav(rwr->r_lhs); a533 1 nloops = 0; a535 5 if (nloops++ > 200) { syserr("Looping on ruleset %d, rule %d", ruleset, rwr-RewriteRules[ruleset]); break; } a558 1 char **oldavp; d560 1 d562 1 a562 1 /* match any single token not in a class */ d564 3 a566 1 if (s != NULL && bitnset(rp[1], s->s_class)) d568 2 a569 16 /* match exactly one token */ mlp->first = avp; mlp->last = avp++; mlp++; break; case MATCHCLASS: /* match any token in a class */ /* slow, concat version by lel@@ida.liu.se */ /* handles multi-token class matches, though */ oldavp = avp; *tokbuf = NULL; do { if (*avp == NULL) { avp = oldavp; a570 4 } strcat(tokbuf, *avp++); s = stab(tokbuf, ST_CLASS, ST_FIND); } while (s == NULL || !bitnset(rp[1], s->s_class)); d572 1 a572 4 mlp->first = oldavp; mlp->last = avp-1; mlp++; break; a605 31 if (*rp == MATCHCLASS) { register STAB *s; char **oldavp; /* attempt to extend binding */ /* slow, concat version by lel@@ida.liu.se */ oldavp = avp; *tokbuf = NULL; for (avp = mlp[-1].first; avp <= mlp[-1].last; avp++) strcat(tokbuf, *avp); do { if (*avp == NULL) { /* back out binding */ avp = oldavp; mlp--; goto cantextend; } strcat(tokbuf, *avp++); s = stab(tokbuf, ST_CLASS, ST_FIND); } while (s == NULL || !bitnset(rp[1], s->s_class)); /* found an extension */ mlp[-1].last = avp-1; rvp++; break; } cantextend: d615 2 a616 1 if (*rp == MATCHONE || *rp == MATCHNCLASS) a638 1 nmatches = 0; a641 8 if (nmatches++ > 200) { syserr("Loop in ruleset %d, rule %d (too many matches)", ruleset, rwr-RewriteRules[ruleset]); rwr = rwr->r_next; nmatches = 0; continue; } d646 1 a646 1 printcav(rvp); a653 1 nmatches = 0; d676 1 a676 2 syserr("rewrite: ruleset %d: replacement #%c out of bounds", ruleset, rp[1]); d711 1 a711 13 #ifdef MACVALUE if (*rp == MACVALUE) { extern char *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; } else #endif MACVALUE *avp++ = rp; d722 1 a722 1 char **hbrvp, **ubrvp; d726 1 a726 1 char hbuf[MAXNAME + 1], ubuf[MAXNAME + 1]; a728 2 bool match, defaultpart; char begintype, db; d731 1 a731 1 if (**rvp != HOSTBEGIN && **rvp != KEYBEGIN) d735 1 a735 1 ** Got a hostname or database lookup. a739 1 begintype = **rvp; a740 1 ubrvp = NULL; a741 6 /* read database name if that's what we're up for */ if (begintype == KEYBEGIN) { if (*++rvp != NULL) db = **rvp; } d743 1 a743 3 if (begintype == HOSTBEGIN) while (*++rvp != NULL && **rvp != HOSTEND && **rvp != CANONUSER) a744 13 else while (*++rvp != NULL && **rvp != KEYEND && **rvp != CANONHOST && **rvp != CANONUSER) continue; /* got a sprintf argument? */ if (**rvp == CANONHOST) { *rvp = NULL; ubrvp = rvp+1; while (*++rvp != NULL && **rvp != KEYEND && **rvp != CANONUSER) continue; } defaultpart = **rvp == CANONUSER; d752 5 a756 19 /* Look it up (lowercase version) */ cataddr(hbrvp + (begintype == HOSTBEGIN ? 1 : 2), hbuf, sizeof hbuf); if (begintype == HOSTBEGIN) match = maphostname(hbuf, sizeof hbuf); else { if (ubrvp == NULL) { /* no sprintf argument part */ match = mapkey(db, hbuf, sizeof hbuf, NULL); } else { cataddr(ubrvp, ubuf, sizeof ubuf); match = mapkey(db, hbuf, sizeof hbuf, ubuf); } } if (match || !defaultpart) { /* scan the new route/host name */ d758 1 a758 1 xpvp = prescan(hbuf, '\0', pvpbuf); d760 3 a762 6 if (xpvp == NULL) { syserr("rewrite: cannot prescan %s: %s", begintype == HOSTBEGIN ? "new hostname" : "dbm lookup result", hbuf); d767 2 a768 1 for (avp = hbrvp; *xpvp != NULL; xpvp++) { a772 3 } else avp = hbrvp; d775 1 a775 10 rvp = avp - 1; for (xpvp = pvpb1; *xpvp != NULL; xpvp++) { if (defaultpart && (begintype == HOSTBEGIN ? **xpvp == HOSTEND : **xpvp == KEYEND)) { defaultpart = FALSE; rvp = avp - 1; } else if (!defaultpart || !match) *avp++ = *xpvp; a777 2 } *avp++ = NULL; d779 1 a779 1 /*break;*/ a783 1 ** Then copy vector back into original space. d786 10 a795 3 callsubr(npvp); for (avp = npvp; *avp++ != NULL;); d798 1 a798 1 d802 1 a802 1 printcav(pvp); d809 1 a809 1 printcav(pvp); a812 46 ** CALLSUBR -- call subroutines in rewrite vector ** ** Parameters: ** pvp -- pointer to token vector. ** ** Returns: ** none. ** ** Side Effects: ** pvp is modified. */ callsubr(pvp) char **pvp; { char **rvp; int subr; for (; *pvp != NULL; pvp++) if (**pvp == CALLSUBR) { 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; } } \f /* d880 5 a884 2 if (**++tv != CANONHOST) { if (!bitnset(M_LOCAL, m->m_flags)) { a887 5 else a->q_host = NULL; } else { d889 2 a890 2 while (*++tv != NULL && **tv != CANONUSER) (void) strcat(buf, *tv); d893 2 a902 3 /* define tohost before running mailer rulesets */ define('h', a->q_host, CurEnv); d905 2 a906 2 if (m->m_re_rwset > 0) rewrite(tv, m->m_re_rwset); d987 1 a987 1 if (strcasecmp(a->q_user, b->q_user)) d997 1 a997 1 if (strcasecmp(a->q_host, b->q_host)) a1053 2 ** headeraddress -- if set, use header specific rewriting ** rulesets and uurelativize if M_RELATIVIZE is set. d1068 1 a1068 1 remotename(name, m, senderaddress, canonical, headeraddress) a1072 1 bool headeraddress; d1088 1 a1088 3 if ((senderaddress ? (headeraddress ? m->m_sh_rwset : m->m_se_rwset) : (headeraddress ? m->m_rh_rwset : m->m_re_rwset)) < 0) d1134 2 a1135 3 ** 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. d1139 1 a1139 7 if (senderaddress) { if (headeraddress) { rewrite(pvp, SplitRewriting ? 5 : 1); if (m->m_sh_rwset > 0) rewrite(pvp, m->m_sh_rwset); } else d1142 2 a1143 2 if (m->m_se_rwset > 0) rewrite(pvp, m->m_se_rwset); a1144 1 } a1146 7 if (headeraddress) { rewrite(pvp, SplitRewriting ? 6 : 2); if (m->m_rh_rwset > 0) rewrite(pvp, m->m_rh_rwset); } else { d1148 2 a1149 2 if (m->m_re_rwset > 0) rewrite(pvp, m->m_re_rwset); a1150 1 } a1161 8 ** Check if we're supposed to do make the address ** UUCP !-relative to the rcpt host vs ourselves. */ if (headeraddress && bitnset(M_RELATIVIZE, m->m_flags)) uurelativize("\001k", "\001h", pvp); /* a1172 67 } \f /* ** 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. */ uurelativize(from, to, pvp) 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]; } @ 5.13.0.2 log @Date: Fri, 6 Jul 90 19:06:37 -0500 From: Neil Rickert <rickert@@cs.niu.edu> To: Paul-Pomes@@uiuc.edu Subject: Sendmail patches. Cc: mdb@@kosciusko.esd.3com.com The Sendmail.mc patches effect the UUCP mailer and the UUCP-A mailer. First, for the UUCP mailer. The main effect is that a header address of 'user@@full.domain.name' will now go out as 'full.domain.name!user'. Before this patch it used to go out as 'myuucpname!full.domain.name!user'. The patch defines ruleset 13 for the UUCP header addresses. The main purpose is to improve mail headers for MX addresses relayed through UUCP hosts. It will not effect normal UUCP addresses, nor domain addresses which are translated to UUCP addresses in uucpxtable. - The patch for the UUCP-A mailer consists of changes to ruleset S21. It requires the code patch to parseaddr.c to be effective. (If the patch is installed without the code change there are no bad effects - it is just not fully functional). The change is as follows: If a local UUCP neighbour address of 'user@@uuhost.UUCP' is in a header for the TCP mailers of the UUCP-A mailer, the address previously was sent out as 'user%uuhost.UUCP@@our.domain.name'. This can result in problems if the recipient is using a route stripping algorithm similar to that in the IDA package, for the recipient mailer will reduce the address to 'user@@uuhost.UUCP', and this may prevent replies if uuhost is not on the uumap. With the change, the address will instead be sent out as 'uuhost!user@@our.domain.name' which is less likely to be stripped with route stripping algorithms, since the 'uuhost' domain is not qualified. A second change occurs only for the UUCP-A mailer. Previously an address 'user@@uuhost.UUCP' was sent as 'user%uuhost.UUCP@@our.domain.name' or as 'uuhost!user@@our.domain.name' even if the recipient host is uuhost itself. With this change, if the recipient domain is uuhost itself, the address will remain as 'user@@uuhost.UUCP' without the extraneous routing. It is only this latter portion, for UUCP-A, which will not do anything unless the code changes are also made. ------------------------------------ The code patch to parseaddr.c is to allow $&x to appear on the left hand side of a rewriting rule. In the previous behaviour, $&x could be used on the RHS of a rule, for delayed evaluation of the macro $x. But when used on the left hand side it would never match anything. With the patch $&x also allows delayed evaluation as a left matching string, and the matched value - possibly including null - is assigned to a $n variable. @ text @a638 24 #ifdef MACVALUE case MACVALUE: { extern char *macvalue(); char *p = macvalue(rp[1], CurEnv); if (tTd(21, 2)) printf("expanding runtime macro '%c' to \"%s\"\n", rp[1], p ? p : "(null)"); oldavp = avp; if (p) while (*p) { if (*avp == NULL || strncasecmp(p,*avp,strlen(*avp))) { avp = oldavp; goto backup; } p += strlen(*avp++); } mlp->first = oldavp; mlp->last = avp -1; } mlp++; break; #endif MACVALUE @ 5.13.0.3 log @Corrected use of mapkey return value. @ text @d913 1 a913 1 match = (mapkey(db, hbuf, sizeof hbuf, NULL) != NULL); d918 1 a918 1 match = (mapkey(db, hbuf, sizeof hbuf, ubuf) != NULL); @ 5.13.0.4 log @Bruce Lilly (bruce%balilly@@sonyd1.broadcast.sony.com) fix for not mangling named lists. @ text @a369 3 if (c == ';') /* semicolons are not tokens */ c = NOCHAR; d857 1 a857 2 char begintype; char db = '\0'; @ 5.13.0.5 log @Add some (char) casts to cheer up gcc. @ text @d540 1 a540 1 (void) strncat(buf, *pvp++, sizeof buf); d612 1 a612 1 *tokbuf = (char) NULL; d618 1 a618 1 (void) strcat(tokbuf, *avp++); d691 1 a691 1 *tokbuf = (char) NULL; d694 1 a694 1 (void) strcat(tokbuf, *avp); d703 1 a703 1 (void) strcat(tokbuf, *avp++); @ 5.13.0.6 log @From: forys@@snake.utah.edu (Jeff Forys) Newsgroups: comp.mail.sendmail Date: 10 Nov 90 01:48:40 MST Organization: University of Utah CS Dept Subject: Sendmail 5.65 can loop on certain addresses +FIX Index: usr.lib/sendmail/src/parseaddr.c 4.3BSD This applies to all known versions of sendmail (through "5.65"). Description: Sendmail reserves characters in the range 020 - 036 for coding its rewrite rules. If one of these characters shows up in an address, the results are unpredictable (from a user's point of view). Specifically, an address containing a "replacement on RHS" rule can cause sendmail to go into an infinite loop while processing various rewrite rules. I suppose this could be used for a denial-of-service attack; if an evil-minded person fired off a bunch of these in your direction, you would end up with an equal number of sendmail processes churning endlessly away on your CPU. Of course, whence such an "attack" originates would be painfully obvious! @ text @a88 3 if (invalidaddr(addr)) return (NULL); a189 39 } \f /* ** 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. */ invalidaddr(addr) char *addr; { register char *cp; extern int errno; /* 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); @ 5.13.0.7 log @Commented out tokens following #endif statements. @ text @d25 1 a25 1 #include "sendmail.h" d493 1 a493 1 #endif /* MACVALUE */ d706 1 a706 1 #endif /* MACVALUE */ d882 1 a882 1 #endif /* MACVALUE */ @ 5.13.0.8 log @Deleted un-needed initialization. @ text @d1207 1 a1207 1 bool natomtok; @ 5.13.0.9 log @Added char *mapkey() declaration. @ text @d904 1 a904 1 extern char *DelimChar, *mapkey(); @ 5.13.0.10 log @Replaced an occurence of struct mailer with MAILER. @ text @a553 1 static d1343 1 a1343 1 MAILER *m; @ 5.13.0.11 log @Added static keyword to declaration for toktype(), _rewrite(), callsubr(), uurelativize() and buildaddr(). @ text @a26 2 static toktype(), _rewrite(), callsubr(), uurelativize(); d79 1 a79 1 static ADDRESS *buildaddr(); a475 1 static a1047 1 static d1097 1 a1097 1 static ADDRESS * a1496 1 static @ 5.13.0.12 log @ANSIfied. @ text @d27 1 a27 17 #ifdef __STDC__ # ifdef CC_WONT_PROMOTE static toktype(char); # else /* !CC_WONT_PROMOTE */ static toktype(int); /* char -> int */ # endif /* CC_WONT_PROMOTE */ static void _rewrite(char **, int); static void callsubr(char **); static ADDRESS * buildaddr(char **, ADDRESS *); static void uurelativize(const char *, const char *, char **); #else /* !__STDC__ */ static toktype(); static void _rewrite(); static void callsubr(); static ADDRESS * buildaddr(); static void uurelativize(); #endif /* __STDC__ */ a28 2 char *DelimChar; /* set to point to the delimiter */ d80 2 d87 1 a87 1 CurEnv->e_to = (char *)addr; d133 1 a187 1 void a208 1 bool d210 1 a210 1 const char *addr; d212 1 a212 1 register const char *cp; d294 2 a548 1 void d557 1 a557 1 static void d691 1 a692 1 d877 1 a878 1 d908 1 d1051 1 a1051 1 static void d1053 1 a1053 1 char **pvp; a1205 1 void a1291 1 void d1355 1 d1360 2 d1501 1 a1501 1 static void d1503 1 a1503 1 const char *from, *to; @