Forget about the whole fallback idea, it can't work because an envelope can only successfully match a single rule for _very_ good reasons that are not related to the implementation itself but to how SMTP works more generally. The ruleset does a first-match evaluation and once it found a rule that matched, that is the only rule it can consider for that envelope otherwise TONS of unfixable issues arise. I can explain if there's interest, but it's very technical and unrelated to your issue.
The only reason you are trying to fallback is because aliases_get() did not have the same wildcard logic as virtual_get(), otherwise with a configuration like: match from any for domain "domain.com" action "local_mail" and an alias record of "@: username" (or an inlined { "@" = username }), the use case you have is solved without having to think about a fallback rule. Care to try the following diff ? Index: aliases.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/aliases.c,v retrieving revision 1.78 diff -u -p -r1.78 aliases.c --- aliases.c 28 Apr 2020 21:46:43 -0000 1.78 +++ aliases.c 14 Apr 2022 22:40:24 -0000 @@ -66,8 +66,15 @@ aliases_get(struct expand *expand, const /* no user-part tag, try looking up user */ ret = table_lookup(mapping, K_ALIAS, buf, &lk); + if (ret < 0) + return (-1); + if (ret) + goto expand; + + /* Failed ? We lookup for a *global* catch all */ + ret = table_lookup(mapping, K_ALIAS, "@", &lk); if (ret <= 0) - return ret; + return (ret); expand: /* foreach node in table_alias expandtree, we merge */ April 14, 2022 9:00 PM, "Beau Ford" <bf...@0x.co> wrote: > On Thu, 14 Apr 2022, Beau Ford wrote: > >> 1) Am I still using my aliases that are defined in: >> >> table aliases file:/usr/local/etc/mail/aliases >> action "local_mail" mbox alias <aliases> >> >> ... or are those superceded now by the catchall ? > > It turns out I am *not* using my aliases anymore and that my rules, as > they are written, are mutually exclusive. > > If I order them like this: > > match from any for domain "domain.com" action "local_mail" > match from any for domain "domain.com" action "catchall" > > ... then my aliases table is processed, like normal, and the catchall is > ignored. This makes sense, because the rule matches and it completes - > there is no reason to move on to the catchall address. > > Nonexistent addresses (that I hoped to get with the catchall) bomb out > with 550 Invalid recipient. > > HOWEVER, if I reverse the order: > > match from any for domain "domain.com" action "catchall" > match from any for domain "domain.com" action "local_mail" > > ... then the catch-all works and I lose all of my aliases. My aliases > table is not used. Again, makes sense - the first rule matches and > completes. > > ----- > > So, how can I say (pseudocode): > > match from any person actually a user or in my aliases table action > "local_mail" > > ... which would fail for nonexistent addresses, which is GOOD, and then > successfully move down to the catchall match ? >