mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-17 14:37:02 +00:00
aliases: Exists does not need to return the "clean" address
The aliases.Resolver.Exists function currently returns the "clean" address (with the drop characters and suffixes removed), which is relied upon in its only caller. That, however, makes the logic more difficult to follow, hiding some of the address manipulation behind what should be a read-only check. So this patch reorganizes that code a little bit, removing the "cleaning" of the address as part of Exists, and making it explicit when needed instead. This patch does not have any user-visible change in behaviour, it is just internal reorganization. This is in preparation for further patches which will improve the handling of some aliases corner cases.
This commit is contained in:
@@ -151,28 +151,25 @@ func (v *Resolver) Resolve(tr *trace.Trace, addr string) ([]Recipient, error) {
|
|||||||
return v.resolve(0, addr, tr)
|
return v.resolve(0, addr, tr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exists check that the address exists in the database.
|
// Exists check that the address exists in the database. It must only be
|
||||||
// It returns the cleaned address, and a boolean indicating the result.
|
// called for local addresses.
|
||||||
// The clean address can be used to look it up in other databases, even if it
|
func (v *Resolver) Exists(tr *trace.Trace, addr string) bool {
|
||||||
// doesn't exist. It must only be called for local addresses.
|
|
||||||
func (v *Resolver) Exists(tr *trace.Trace, addr string) (string, bool) {
|
|
||||||
tr = tr.NewChild("Alias.Exists", addr)
|
tr = tr.NewChild("Alias.Exists", addr)
|
||||||
defer tr.Finish()
|
defer tr.Finish()
|
||||||
|
|
||||||
addr = v.cleanIfLocal(addr)
|
addr = v.RemoveDropsAndSuffix(addr)
|
||||||
|
|
||||||
rcpts, _ := v.lookup(addr, tr)
|
rcpts, _ := v.lookup(addr, tr)
|
||||||
if len(rcpts) > 0 {
|
if len(rcpts) > 0 {
|
||||||
return addr, true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
domain := envelope.DomainOf(addr)
|
domain := envelope.DomainOf(addr)
|
||||||
catchAll, _ := v.lookup("*@"+domain, tr)
|
catchAll, _ := v.lookup("*@"+domain, tr)
|
||||||
if len(catchAll) > 0 {
|
if len(catchAll) > 0 {
|
||||||
return addr, true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return addr, false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Resolver) lookup(addr string, tr *trace.Trace) ([]Recipient, error) {
|
func (v *Resolver) lookup(addr string, tr *trace.Trace) ([]Recipient, error) {
|
||||||
@@ -209,7 +206,7 @@ func (v *Resolver) resolve(rcount int, addr string, tr *trace.Trace) ([]Recipien
|
|||||||
// Drop suffixes and chars to get the "clean" address before resolving.
|
// Drop suffixes and chars to get the "clean" address before resolving.
|
||||||
// This also means that we will return the clean version if there's no
|
// This also means that we will return the clean version if there's no
|
||||||
// match, which our callers can rely upon.
|
// match, which our callers can rely upon.
|
||||||
addr = v.cleanIfLocal(addr)
|
addr = v.RemoveDropsAndSuffix(addr)
|
||||||
|
|
||||||
// Lookup in the aliases database.
|
// Lookup in the aliases database.
|
||||||
rcpts, err := v.lookup(addr, tr)
|
rcpts, err := v.lookup(addr, tr)
|
||||||
@@ -278,17 +275,8 @@ func (v *Resolver) resolve(rcount int, addr string, tr *trace.Trace) ([]Recipien
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Resolver) cleanIfLocal(addr string) string {
|
func (v *Resolver) RemoveDropsAndSuffix(addr string) string {
|
||||||
user, domain := envelope.Split(addr)
|
user, domain := envelope.Split(addr)
|
||||||
|
|
||||||
v.mu.Lock()
|
|
||||||
isLocal := v.domains[domain]
|
|
||||||
v.mu.Unlock()
|
|
||||||
|
|
||||||
if !isLocal {
|
|
||||||
return addr
|
|
||||||
}
|
|
||||||
|
|
||||||
user = removeAllAfter(user, v.SuffixSep)
|
user = removeAllAfter(user, v.SuffixSep)
|
||||||
user = removeChars(user, v.DropChars)
|
user = removeChars(user, v.DropChars)
|
||||||
user, _ = normalize.User(user)
|
user, _ = normalize.User(user)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func mustExist(t *testing.T, r *Resolver, addrs ...string) {
|
|||||||
tr := trace.New("test", "mustExist")
|
tr := trace.New("test", "mustExist")
|
||||||
defer tr.Finish()
|
defer tr.Finish()
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
if _, ok := r.Exists(tr, addr); !ok {
|
if ok := r.Exists(tr, addr); !ok {
|
||||||
t.Errorf("address %q does not exist, it should", addr)
|
t.Errorf("address %q does not exist, it should", addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ func mustNotExist(t *testing.T, r *Resolver, addrs ...string) {
|
|||||||
tr := trace.New("test", "mustNotExist")
|
tr := trace.New("test", "mustNotExist")
|
||||||
defer tr.Finish()
|
defer tr.Finish()
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
if _, ok := r.Exists(tr, addr); ok {
|
if ok := r.Exists(tr, addr); ok {
|
||||||
t.Errorf("address %q exists, it should not", addr)
|
t.Errorf("address %q exists, it should not", addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,9 +86,9 @@ func TestBasic(t *testing.T) {
|
|||||||
resolver.AddDomain("localA")
|
resolver.AddDomain("localA")
|
||||||
resolver.AddDomain("localB")
|
resolver.AddDomain("localB")
|
||||||
resolver.aliases = map[string][]Recipient{
|
resolver.aliases = map[string][]Recipient{
|
||||||
"a@localA": {{"c@d", EMAIL}, {"e@localB", EMAIL}},
|
"a@localA": {{"c@d", EMAIL}, {"e@localB", EMAIL}},
|
||||||
"e@localB": {{"cmd", PIPE}},
|
"e@localB": {{"cmd", PIPE}},
|
||||||
"cmd": {{"x@y", EMAIL}}, // it's a trap!
|
"cmd@localA": {{"x@y", EMAIL}},
|
||||||
}
|
}
|
||||||
|
|
||||||
cases := Cases{
|
cases := Cases{
|
||||||
@@ -98,7 +98,7 @@ func TestBasic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cases.check(t, resolver)
|
cases.check(t, resolver)
|
||||||
|
|
||||||
mustExist(t, resolver, "a@localA", "e@localB", "cmd")
|
mustExist(t, resolver, "a@localA", "e@localB", "cmd@localA")
|
||||||
mustNotExist(t, resolver, "x@y")
|
mustNotExist(t, resolver, "x@y")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ func TestAddrRewrite(t *testing.T) {
|
|||||||
cases.check(t, resolver)
|
cases.check(t, resolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExistsRewrite(t *testing.T) {
|
func TestExists(t *testing.T) {
|
||||||
resolver := NewResolver(allUsersExist)
|
resolver := NewResolver(allUsersExist)
|
||||||
resolver.AddDomain("def")
|
resolver.AddDomain("def")
|
||||||
resolver.AddDomain("p-q.com")
|
resolver.AddDomain("p-q.com")
|
||||||
@@ -207,35 +207,53 @@ func TestExistsRewrite(t *testing.T) {
|
|||||||
resolver.DropChars = ".~"
|
resolver.DropChars = ".~"
|
||||||
resolver.SuffixSep = "-+"
|
resolver.SuffixSep = "-+"
|
||||||
|
|
||||||
mustExist(t, resolver, "abc@def", "a.bc+blah@def", "ño.ño@def")
|
mustExist(t, resolver,
|
||||||
mustNotExist(t, resolver, "abc@d.ef", "nothere@def")
|
"abc@def",
|
||||||
|
"abc+blah@def",
|
||||||
|
"a.bc+blah@def",
|
||||||
|
"a.b~c@def",
|
||||||
|
"ñoño@def",
|
||||||
|
"ño.ño@def",
|
||||||
|
"recu@def",
|
||||||
|
"re.cu@def")
|
||||||
|
mustNotExist(t, resolver,
|
||||||
|
"abc@d.ef",
|
||||||
|
"nothere@def",
|
||||||
|
"ex@def",
|
||||||
|
"a.bc@unknown",
|
||||||
|
"x.yz@def",
|
||||||
|
"x.yz@d.ef",
|
||||||
|
"abc@d.ef")
|
||||||
|
}
|
||||||
|
|
||||||
tr := trace.New("test", "TestExistsRewrite")
|
func TestRemoveDropsAndSuffix(t *testing.T) {
|
||||||
defer tr.Finish()
|
resolver := NewResolver(allUsersExist)
|
||||||
|
resolver.AddDomain("def")
|
||||||
|
resolver.AddDomain("p-q.com")
|
||||||
|
resolver.aliases = map[string][]Recipient{
|
||||||
|
"abc@def": {{"x@y", EMAIL}},
|
||||||
|
"ñoño@def": {{"x@y", EMAIL}},
|
||||||
|
"recu@def": {{"ab+cd@p-q.com", EMAIL}},
|
||||||
|
}
|
||||||
|
resolver.DropChars = ".~"
|
||||||
|
resolver.SuffixSep = "-+"
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
addr string
|
addr string
|
||||||
expectAddr string
|
want string
|
||||||
expectExists bool
|
|
||||||
}{
|
}{
|
||||||
{"abc@def", "abc@def", true},
|
{"abc@def", "abc@def"},
|
||||||
{"abc+blah@def", "abc@def", true},
|
{"abc+blah@def", "abc@def"},
|
||||||
{"a.b~c@def", "abc@def", true},
|
{"a.b~c@def", "abc@def"},
|
||||||
{"a.bc+blah@def", "abc@def", true},
|
{"a.bc+blah@def", "abc@def"},
|
||||||
|
{"x.yz@def", "xyz@def"},
|
||||||
{"a.bc@unknown", "a.bc@unknown", false},
|
{"x.yz@d.ef", "xyz@d.ef"},
|
||||||
{"x.yz@def", "xyz@def", false},
|
|
||||||
{"x.yz@d.ef", "x.yz@d.ef", false},
|
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
addr, exists := resolver.Exists(tr, c.addr)
|
addr := resolver.RemoveDropsAndSuffix(c.addr)
|
||||||
if addr != c.expectAddr {
|
if addr != c.want {
|
||||||
t.Errorf("%q: expected addr %q, got %q",
|
t.Errorf("RemoveDropsAndSuffix(%q): want %q, got %q",
|
||||||
c.addr, c.expectAddr, addr)
|
c.addr, c.want, addr)
|
||||||
}
|
|
||||||
if exists != c.expectExists {
|
|
||||||
t.Errorf("%q: expected exists %v, got %v",
|
|
||||||
c.addr, c.expectExists, exists)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -591,7 +591,7 @@ func (c *Conn) RCPT(params string) (code int, msg string) {
|
|||||||
return 550, "5.1.3 Destination address is invalid"
|
return 550, "5.1.3 Destination address is invalid"
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, err := c.userExists(addr)
|
ok, err := c.localUserExists(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.tr.Errorf("error checking if user %q exists: %v", addr, err)
|
c.tr.Errorf("error checking if user %q exists: %v", addr, err)
|
||||||
maillog.Rejected(c.remoteAddr, c.mailFrom, []string{addr},
|
maillog.Rejected(c.remoteAddr, c.mailFrom, []string{addr},
|
||||||
@@ -1096,17 +1096,14 @@ func (c *Conn) resetEnvelope() {
|
|||||||
c.spfError = nil
|
c.spfError = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) userExists(addr string) (bool, error) {
|
func (c *Conn) localUserExists(addr string) (bool, error) {
|
||||||
var ok bool
|
if c.aliasesR.Exists(c.tr, addr) {
|
||||||
addr, ok = c.aliasesR.Exists(c.tr, addr)
|
|
||||||
if ok {
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note we used the address returned by the aliases resolver, which has
|
// Remove the drop chars and suffixes, if any, so the database lookup is
|
||||||
// cleaned it up. This means that a check for "us.er@domain" will have us
|
// on a "clean" address.
|
||||||
// look up "user" in our databases if the domain is local, which is what
|
addr = c.aliasesR.RemoveDropsAndSuffix(addr)
|
||||||
// we want.
|
|
||||||
user, domain := envelope.Split(addr)
|
user, domain := envelope.Split(addr)
|
||||||
return c.authr.Exists(c.tr, user, domain)
|
return c.authr.Exists(c.tr, user, domain)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user