diff options
Diffstat (limited to 'usr.sbin/sendmail/src/parseaddr.c')
-rw-r--r-- | usr.sbin/sendmail/src/parseaddr.c | 526 |
1 files changed, 385 insertions, 141 deletions
diff --git a/usr.sbin/sendmail/src/parseaddr.c b/usr.sbin/sendmail/src/parseaddr.c index 7507b8371be41..3b466f9ba7096 100644 --- a/usr.sbin/sendmail/src/parseaddr.c +++ b/usr.sbin/sendmail/src/parseaddr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1983, 1995 Eric P. Allman + * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * @@ -33,7 +33,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)parseaddr.c 8.87.1.1 (Berkeley) 9/16/96"; +static char sccsid[] = "@(#)parseaddr.c 8.114 (Berkeley) 9/20/96"; #endif /* not lint */ # include "sendmail.h" @@ -169,7 +169,7 @@ parseaddr(addr, a, flags, delim, delimptr, e) ** If there was a parsing failure, mark it for queueing. */ - if (queueup) + if (queueup && OpMode != MD_INITALIAS) { char *msg = "Transient parse error -- message queued for future delivery"; @@ -220,14 +220,6 @@ invalidaddr(addr, delimptr) if (savedelim != '\0') *delimptr = '\0'; } -#if 0 - /* for testing.... */ - if (strcmp(addr, "INvalidADDR") == 0) - { - usrerr("553 INvalid ADDRess"); - goto addrfailure; - } -#endif for (; *addr != '\0'; addr++) { if ((*addr & 0340) == 0200) @@ -241,7 +233,6 @@ invalidaddr(addr, delimptr) } setstat(EX_USAGE); usrerr("553 Address contained invalid control characters"); - addrfailure: if (delimptr != NULL && savedelim != '\0') *delimptr = savedelim; return TRUE; @@ -343,12 +334,12 @@ allocaddr(a, flags, paddr) static short StateTab[NSTATES][NSTATES] = { /* oldst chtype> OPR ATM QST SPC ONE ILL */ - /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB, - /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB, - /*QST*/ QST, QST, OPR, QST, QST, QST, - /*SPC*/ OPR, ATM, QST, SPC|M, ONE, ILL|MB, - /*ONE*/ OPR, OPR, OPR, OPR, OPR, ILL|MB, - /*ILL*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M, + /*OPR*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB }, + /*ATM*/ { OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB }, + /*QST*/ { QST, QST, OPR, QST, QST, QST }, + /*SPC*/ { OPR, ATM, QST, SPC|M, ONE, ILL|MB }, + /*ONE*/ { OPR, OPR, OPR, OPR, OPR, ILL|MB }, + /*ILL*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M }, }; /* token type table -- it gets modified with $o characters */ @@ -444,6 +435,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) register int c; char **avp; bool bslashmode; + bool route_syntax; int cmntcnt; int anglecnt; char *tok; @@ -483,6 +475,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) q = pvpbuf; bslashmode = FALSE; + route_syntax = FALSE; cmntcnt = 0; anglecnt = 0; avp = av; @@ -510,7 +503,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) if (q >= &pvpbuf[pvpbsize - 5]) { usrerr("553 Address too long"); - if (strlen(addr) > MAXNAME) + if (strlen(addr) > (SIZE_T) MAXNAME) addr[MAXNAME] = '\0'; returnnull: if (delimptr != NULL) @@ -548,9 +541,19 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) p--; } - else if (c == delim && anglecnt <= 0 && - cmntcnt <= 0 && state != QST) - break; + else if (c == delim && cmntcnt <= 0 && state != QST) + { + if (anglecnt <= 0) + break; + + /* special case for better error management */ + if (delim == ',' && !route_syntax) + { + usrerr("653 Unbalanced '<'"); + c = '>'; + p--; + } + } if (tTd(22, 101)) printf("c=%c, s=%d; ", c, state); @@ -600,7 +603,15 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) else if (cmntcnt > 0) c = NOCHAR; else if (c == '<') + { + char *q = p; + anglecnt++; + while (isascii(*q) && isspace(*q)) + q++; + if (*q == '@') + route_syntax = TRUE; + } else if (c == '>') { if (anglecnt <= 0) @@ -610,6 +621,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) } else anglecnt--; + route_syntax = FALSE; } else if (delim == ' ' && isascii(c) && isspace(c)) c = ' '; @@ -743,10 +755,11 @@ rewrite(pvp, ruleset, reclevel, e) int loopcount; struct match mlist[MAXMATCH]; /* stores match on LHS */ char *npvp[MAXATOM+1]; /* temporary space for rebuild */ + extern int callsubr __P((char**, int, ENVELOPE *)); if (OpMode == MD_TEST || tTd(21, 1)) { - printf("rewrite: ruleset %2d input:", ruleset); + printf("rewrite: ruleset %3d input:", ruleset); printav(pvp); } if (ruleset < 0 || ruleset >= MAXRWSETS) @@ -772,6 +785,12 @@ rewrite(pvp, ruleset, reclevel, e) loopcount = 0; for (rwr = RewriteRules[ruleset]; rwr != NULL; ) { + int stat; + + /* if already canonical, quit now */ + if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET) + break; + if (tTd(21, 12)) { printf("-----trying rule:"); @@ -1002,8 +1021,6 @@ rewrite(pvp, ruleset, reclevel, e) rvp++; rwr = NULL; } - else if ((*rp & 0377) == CANONNET) - rwr = NULL; /* substitute */ for (avp = npvp; *rvp != NULL; rvp++) @@ -1091,6 +1108,7 @@ rewrite(pvp, ruleset, reclevel, e) char *argvect[10]; char pvpbuf[PSBUFSIZE]; char *nullpvp[1]; + extern char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *)); if ((**rvp & 0377) != HOSTBEGIN && (**rvp & 0377) != LOOKUPBEGIN) @@ -1173,55 +1191,7 @@ rewrite(pvp, ruleset, reclevel, e) /* look it up */ cataddr(key_rvp, NULL, buf, sizeof buf, '\0'); argvect[0] = buf; - if (e->e_sendmode == SM_DEFER) - { - /* don't do any map lookups */ - if (tTd(60, 1)) - printf("map_lookup(%s, %s) => DEFERRED\n", - mapname, buf); - replac = NULL; - rstat = EX_TEMPFAIL; - } - else if (map != NULL && bitset(MF_OPEN, map->s_map.map_mflags)) - { - auto int stat = EX_OK; - - if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags)) - stripquotes(buf); - - /* XXX should try to auto-open the map here */ - - if (tTd(60, 1)) - printf("map_lookup(%s, %s) => ", - mapname, buf); - replac = (*map->s_map.map_class->map_lookup)(&map->s_map, - buf, argvect, &stat); - if (tTd(60, 1)) - printf("%s (%d)\n", - replac ? replac : "NOT FOUND", - stat); - - /* should recover if stat == EX_TEMPFAIL */ - if (stat == EX_TEMPFAIL) - { - rstat = EX_TEMPFAIL; - if (tTd(60, 1)) - printf("map_lookup(%s, %s) tempfail: errno=%d\n", - mapname, buf, errno); - if (e->e_message == NULL) - { - char mbuf[300]; - - snprintf(mbuf, sizeof mbuf, - "%.80s map: lookup (%s): deferred", - mapname, - shortenstring(buf, 203)); - e->e_message = newstr(mbuf); - } - } - } - else - replac = NULL; + replac = map_lookup(map, buf, argvect, &rstat, e); /* if no replacement, use default */ if (replac == NULL && default_rvp != NULL) @@ -1273,38 +1243,16 @@ rewrite(pvp, ruleset, reclevel, e) ** Check for subroutine calls. */ - if (*npvp != NULL && (**npvp & 0377) == CALLSUBR) - { - int stat; + stat = callsubr(npvp, reclevel, e); + if (rstat == EX_OK || stat == EX_TEMPFAIL) + rstat = stat; - if (npvp[1] == NULL) - { - syserr("parseaddr: NULL subroutine call in ruleset %d, rule %d", - ruleset, ruleno); - *pvp = NULL; - } - else - { - int ruleset; - STAB *s; - - bcopy((char *) &npvp[2], (char *) pvp, - (int) (avp - npvp - 2) * sizeof *avp); - if (tTd(21, 3)) - printf("-----callsubr %s\n", npvp[1]); - ruleset = strtorwset(npvp[1], NULL, ST_FIND); - stat = rewrite(pvp, ruleset, reclevel, e); - if (rstat == EX_OK || stat == EX_TEMPFAIL) - rstat = stat; - if (*pvp != NULL && (**pvp & 0377) == CANONNET) - rwr = NULL; - } - } - else - { - bcopy((char *) npvp, (char *) pvp, - (int) (avp - npvp) * sizeof *avp); - } + /* copy vector back into original space. */ + for (avp = npvp; *avp++ != NULL;) + continue; + bcopy((char *) npvp, (char *) pvp, + (int) (avp - npvp) * sizeof *avp); + if (tTd(21, 4)) { printf("rewritten as:"); @@ -1314,13 +1262,192 @@ rewrite(pvp, ruleset, reclevel, e) if (OpMode == MD_TEST || tTd(21, 1)) { - printf("rewrite: ruleset %2d returns:", ruleset); + printf("rewrite: ruleset %3d returns:", ruleset); printav(pvp); } return rstat; } /* +** CALLSUBR -- call subroutines in rewrite vector +** +** Parameters: +** pvp -- pointer to token vector. +** reclevel -- the current recursion level. +** e -- the current envelope. +** +** Returns: +** The status from the subroutine call. +** +** Side Effects: +** pvp is modified. +*/ + +int +callsubr(pvp, reclevel, e) + char **pvp; + int reclevel; + ENVELOPE *e; +{ + char **avp; + char **rvp; + register int i; + int subr; + int stat; + int rstat = EX_OK; + char *tpvp[MAXATOM + 1]; + + for (avp = pvp; *avp != NULL; avp++) + { + if ((**avp & 0377) == CALLSUBR && avp[1] != NULL) + { + subr = strtorwset(avp[1], NULL, ST_FIND); + if (subr < 0) + { + syserr("Unknown ruleset %s", avp[1]); + return EX_CONFIG; + } + + if (tTd(21, 3)) + printf("-----callsubr %s (%d)\n", avp[1], subr); + + /* + ** Take care of possible inner calls first. + ** use a full size temporary buffer to avoid + ** overflows in rewrite, but strip off the + ** subroutine call. + */ + + for (i = 2; avp[i] != NULL; i++) + tpvp[i - 2] = avp[i]; + tpvp[i - 2] = NULL; + + stat = callsubr(tpvp, reclevel, e); + if (rstat == EX_OK || stat == EX_TEMPFAIL) + rstat = stat; + + /* + ** Now we need to call the ruleset specified for + ** the subroutine. we can do this with the + ** temporary buffer that we set up earlier, + ** since it has all the data we want to rewrite. + */ + + stat = rewrite(tpvp, subr, reclevel, e); + if (rstat == EX_OK || stat == EX_TEMPFAIL) + rstat = stat; + + /* + ** Find length of tpvp and current offset into + ** pvp, if the total is greater than MAXATOM, + ** then it would overflow the buffer if we copied + ** it back in to pvp, in which case we throw a + ** fit. + */ + + for (rvp = tpvp; *rvp != NULL; rvp++) + continue; + if (((rvp - tpvp) + (avp - pvp)) > MAXATOM) + { + syserr("554 callsubr: expansion too long"); + return EX_DATAERR; + } + + /* + ** Now we can copy the rewritten code over + ** the initial subroutine call in the buffer. + */ + + for (i = 0; tpvp[i] != NULL; i++) + avp[i] = tpvp[i]; + avp[i] = NULL; + + /* + ** If we got this far, we've processed the left + ** most subroutine, and recursively called ourselves + ** to handle any other subroutines. We're done. + */ + + break; + } + } + return rstat; +} +/* +** MAP_LOOKUP -- do lookup in map +** +** Parameters: +** map -- the map to use for the lookup. +** key -- the key to look up. +** argvect -- arguments to pass to the map lookup. +** pstat -- a pointer to an integer in which to store the +** status from the lookup. +** e -- the current envelope. +** +** Returns: +** The result of the lookup. +** NULL -- if there was no data for the given key. +*/ + +char * +map_lookup(map, key, argvect, pstat, e) + STAB *map; + char key[]; + char **argvect; + int *pstat; + ENVELOPE *e; +{ + auto int stat = EX_OK; + char *replac; + + if (e->e_sendmode == SM_DEFER) + { + /* don't do any map lookups */ + if (tTd(60, 1)) + printf("map_lookup(%s, %s) => DEFERRED\n", + map->s_name, key); + *pstat = EX_TEMPFAIL; + return NULL; + } + if (map == NULL || !bitset(MF_OPEN, map->s_map.map_mflags)) + return NULL; + + if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags)) + stripquotes(key); + + /* XXX should try to auto-open the map here */ + + if (tTd(60, 1)) + printf("map_lookup(%s, %s) => ", + map->s_name, key); + replac = (*map->s_map.map_class->map_lookup)(&map->s_map, + key, argvect, &stat); + if (tTd(60, 1)) + printf("%s (%d)\n", + replac != NULL ? replac : "NOT FOUND", + stat); + + /* should recover if stat == EX_TEMPFAIL */ + if (stat == EX_TEMPFAIL && !bitset(MF_NODEFER, map->s_map.map_mflags)) + { + *pstat = EX_TEMPFAIL; + if (tTd(60, 1)) + printf("map_lookup(%s, %s) tempfail: errno=%d\n", + map->s_name, key, errno); + if (e->e_message == NULL) + { + char mbuf[300]; + + snprintf(mbuf, sizeof mbuf, + "%.80s map: lookup (%s): deferred", + map->s_name, + shortenstring(key, 203)); + e->e_message = newstr(mbuf); + } + } + return replac; +} +/* ** BUILDADDR -- build address from token vector. ** ** Parameters: @@ -1345,17 +1472,17 @@ struct errcodes int ec_code; /* numeric code */ } ErrorCodes[] = { - "usage", EX_USAGE, - "nouser", EX_NOUSER, - "nohost", EX_NOHOST, - "unavailable", EX_UNAVAILABLE, - "software", EX_SOFTWARE, - "tempfail", EX_TEMPFAIL, - "protocol", EX_PROTOCOL, + { "usage", EX_USAGE }, + { "nouser", EX_NOUSER }, + { "nohost", EX_NOHOST }, + { "unavailable", EX_UNAVAILABLE }, + { "software", EX_SOFTWARE }, + { "tempfail", EX_TEMPFAIL }, + { "protocol", EX_PROTOCOL }, #ifdef EX_CONFIG - "config", EX_CONFIG, + { "config", EX_CONFIG }, #endif - NULL, EX_UNAVAILABLE, + { NULL, EX_UNAVAILABLE } }; ADDRESS * @@ -1386,7 +1513,7 @@ buildaddr(tv, a, flags, e) bzero((char *) a, sizeof *a); /* set up default error return flags */ - a->q_flags |= QPINGONFAILURE|QPINGONDELAY; + a->q_flags |= DefaultNotify; /* figure out what net/mailer to use */ if (*tv == NULL || (**tv & 0377) != CANONNET) @@ -1407,7 +1534,7 @@ badaddr: mname = *++tv; /* extract host and user portions */ - if ((**++tv & 0377) == CANONHOST) + if (*++tv != NULL && (**tv & 0377) == CANONHOST) hostp = ++tv; else hostp = NULL; @@ -1433,6 +1560,8 @@ badaddr: if (strchr(hbuf, '.') != NULL) { + extern int dsntoexitstat __P((char *)); + a->q_status = newstr(hbuf); setstat(dsntoexitstat(hbuf)); } @@ -1703,27 +1832,27 @@ struct qflags struct qflags AddressFlags[] = { - "QDONTSEND", QDONTSEND, - "QBADADDR", QBADADDR, - "QGOODUID", QGOODUID, - "QPRIMARY", QPRIMARY, - "QQUEUEUP", QQUEUEUP, - "QSENT", QSENT, - "QNOTREMOTE", QNOTREMOTE, - "QSELFREF", QSELFREF, - "QVERIFIED", QVERIFIED, - "QBOGUSSHELL", QBOGUSSHELL, - "QUNSAFEADDR", QUNSAFEADDR, - "QPINGONSUCCESS", QPINGONSUCCESS, - "QPINGONFAILURE", QPINGONFAILURE, - "QPINGONDELAY", QPINGONDELAY, - "QHASNOTIFY", QHASNOTIFY, - "QRELAYED", QRELAYED, - "QEXPANDED", QEXPANDED, - "QDELIVERED", QDELIVERED, - "QDELAYED", QDELAYED, - "QTHISPASS", QTHISPASS, - NULL + { "QDONTSEND", QDONTSEND }, + { "QBADADDR", QBADADDR }, + { "QGOODUID", QGOODUID }, + { "QPRIMARY", QPRIMARY }, + { "QQUEUEUP", QQUEUEUP }, + { "QSENT", QSENT }, + { "QNOTREMOTE", QNOTREMOTE }, + { "QSELFREF", QSELFREF }, + { "QVERIFIED", QVERIFIED }, + { "QBOGUSSHELL", QBOGUSSHELL }, + { "QUNSAFEADDR", QUNSAFEADDR }, + { "QPINGONSUCCESS", QPINGONSUCCESS }, + { "QPINGONFAILURE", QPINGONFAILURE }, + { "QPINGONDELAY", QPINGONDELAY }, + { "QHASNOTIFY", QHASNOTIFY }, + { "QRELAYED", QRELAYED }, + { "QEXPANDED", QEXPANDED }, + { "QDELIVERED", QDELIVERED }, + { "QDELAYED", QDELAYED }, + { "QTHISPASS", QTHISPASS }, + { NULL } }; void @@ -1781,10 +1910,14 @@ printaddr(a, follow) a->q_owner == NULL ? "(none)" : a->q_owner, a->q_home == NULL ? "(none)" : a->q_home, a->q_fullname == NULL ? "(none)" : a->q_fullname); - printf("\torcpt=\"%s\", statmta=%s, rstatus=%s\n", + printf("\torcpt=\"%s\", statmta=%s, status=%s\n", a->q_orcpt == NULL ? "(none)" : a->q_orcpt, a->q_statmta == NULL ? "(none)" : a->q_statmta, + a->q_status == NULL ? "(none)" : a->q_status); + printf("\trstatus=\"%s\"\n", a->q_rstatus == NULL ? "(none)" : a->q_rstatus); + printf("\tspecificity=%d, statdate=%s\n", + a->q_specificity, ctime(&a->q_statdate)); if (!follow) return; @@ -1993,7 +2126,12 @@ maplocaluser(a, sendq, aliaslevel, e) if (pvp == NULL) return; - (void) rewrite(pvp, 5, 0, e); + if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL) + { + a->q_flags |= QQUEUEUP; + a->q_status = "4.4.3"; + return; + } if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) return; @@ -2154,5 +2292,111 @@ dequote_map(map, name, av, statp) quotemode || quotecnt <= 0 || spacecnt != 0) return NULL; *q++ = '\0'; - return name; + return map_rewrite(map, name, strlen(name), NULL); +} +/* +** RSCHECK -- check string(s) for validity using rewriting sets +** +** Parameters: +** rwset -- the rewriting set to use. +** p1 -- the first string to check. +** p2 -- the second string to check -- may be null. +** e -- the current envelope. +** +** Returns: +** EX_OK -- if the rwset doesn't resolve to $#error +** else -- the failure status (message printed) +*/ + +int +rscheck(rwset, p1, p2, e) + char *rwset; + char *p1; + char *p2; + ENVELOPE *e; +{ + char *buf; + int bufsize; + int saveexitstat; + int rstat; + char **pvp; + int rsno; + auto ADDRESS a1; + bool saveQuickAbort = QuickAbort; + char buf0[MAXLINE]; + char pvpbuf[PSBUFSIZE]; + extern char MsgBuf[]; + + if (tTd(48, 2)) + printf("rscheck(%s, %s, %s)\n", rwset, p1, + p2 == NULL ? "(NULL)" : p2); + + rsno = strtorwset(rwset, NULL, ST_FIND); + if (rsno < 0) + return EX_OK; + + if (p2 != NULL) + { + bufsize = strlen(p1) + strlen(p2) + 2; + if (bufsize > sizeof buf0) + buf = xalloc(bufsize); + else + { + buf = buf0; + bufsize = sizeof buf0; + } + (void) snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); + } + else + { + bufsize = strlen(p1) + 1; + if (bufsize > sizeof buf0) + buf = xalloc(bufsize); + else + { + buf = buf0; + bufsize = sizeof buf0; + } + (void) snprintf(buf, bufsize, "%s", p1); + } + pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL); + if (pvp == NULL) + { + rstat = EX_DATAERR; + goto finis; + } + (void) rewrite(pvp, rsno, 0, e); + if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET || + pvp[1] == NULL || strcmp(pvp[1], "error") != 0) + return EX_OK; + + /* got an error -- process it */ + saveexitstat = ExitStat; + QuickAbort = FALSE; + (void) buildaddr(pvp, &a1, 0, e); + QuickAbort = saveQuickAbort; + rstat = ExitStat; + ExitStat = saveexitstat; + +#ifdef LOG + if (LogLevel >= 4) + { + if (p2 == NULL) + syslog(LOG_NOTICE, "Ruleset %s (%s) rejection: %s", + rwset, p1, MsgBuf); + else + syslog(LOG_NOTICE, "Ruleset %s (%s, %s) rejection: %s", + rwset, p1, p2, MsgBuf); + } +#endif + + if (QuickAbort) + longjmp(TopFrame, 2); + + /* clean up */ + finis: + setstat(rstat); + if (buf != buf0) + free(buf); + return rstat; } |