1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2026-01-09 17:55:57 +00:00

chasquid: Fail at RCPT TO time if a user does not exist

It's more convenient and in line with standard practice to fail RCPT TO if the
user does not exist.

This involves making the server and client aware of aliases, but it doesn't
end up being very convoluted, and simplifies other code.
This commit is contained in:
Alberto Bertogli
2016-09-25 21:46:32 +01:00
parent ce379dea3e
commit 0995eac474
8 changed files with 163 additions and 20 deletions

View File

@@ -120,6 +120,19 @@ func (v *Resolver) Resolve(addr string) ([]Recipient, error) {
return v.resolve(0, addr)
}
// Exists check that the address exists in the database.
// It returns the cleaned address, and a boolean indicating the result.
// The clean address can be used to look it up in other databases, even if it
// doesn't exist.
func (v *Resolver) Exists(addr string) (string, bool) {
v.mu.Lock()
defer v.mu.Unlock()
addr = v.cleanIfLocal(addr)
_, ok := v.aliases[addr]
return addr, ok
}
func (v *Resolver) resolve(rcount int, addr string) ([]Recipient, error) {
if rcount >= recursionLimit {
return nil, ErrRecursionLimitExceeded

View File

@@ -25,6 +25,22 @@ func (cases Cases) check(t *testing.T, r *Resolver) {
}
}
func mustExist(t *testing.T, r *Resolver, addrs ...string) {
for _, addr := range addrs {
if _, ok := r.Exists(addr); !ok {
t.Errorf("address %q does not exist, it should", addr)
}
}
}
func mustNotExist(t *testing.T, r *Resolver, addrs ...string) {
for _, addr := range addrs {
if _, ok := r.Exists(addr); ok {
t.Errorf("address %q exists, it should not", addr)
}
}
}
func TestBasic(t *testing.T) {
resolver := NewResolver()
resolver.aliases = map[string][]Recipient{
@@ -39,6 +55,9 @@ func TestBasic(t *testing.T) {
{"x@y", []Recipient{{"x@y", EMAIL}}},
}
cases.check(t, resolver)
mustExist(t, resolver, "a@b", "e@f", "cmd")
mustNotExist(t, resolver, "x@y")
}
func TestAddrRewrite(t *testing.T) {
@@ -81,6 +100,48 @@ func TestAddrRewrite(t *testing.T) {
cases.check(t, resolver)
}
func TestExistsRewrite(t *testing.T) {
resolver := NewResolver()
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 = "-+"
mustExist(t, resolver, "abc@def", "a.bc+blah@def", "ño.ño@def")
mustNotExist(t, resolver, "abc@d.ef", "nothere@def")
cases := []struct {
addr string
expectAddr string
expectExists bool
}{
{"abc@def", "abc@def", true},
{"abc+blah@def", "abc@def", true},
{"a.b~c@def", "abc@def", true},
{"a.bc+blah@def", "abc@def", true},
{"a.bc@unknown", "a.bc@unknown", false},
{"x.yz@def", "xyz@def", false},
{"x.yz@d.ef", "x.yz@d.ef", false},
}
for _, c := range cases {
addr, exists := resolver.Exists(c.addr)
if addr != c.expectAddr {
t.Errorf("%q: expected addr %q, got %q",
c.addr, c.expectAddr, addr)
}
if exists != c.expectExists {
t.Errorf("%q: expected exists %v, got %v",
c.addr, c.expectExists, exists)
}
}
}
func TestTooMuchRecursion(t *testing.T) {
resolver := Resolver{}
resolver.aliases = map[string][]Recipient{

View File

@@ -202,6 +202,14 @@ func (db *DB) RemoveUser(name string) bool {
return present
}
// HasUser returns true if the user is present, False otherwise.
func (db *DB) HasUser(name string) bool {
db.mu.Lock()
_, present := db.db.Users[name]
db.mu.Unlock()
return present
}
///////////////////////////////////////////////////////////
// Encryption schemes
//

View File

@@ -283,3 +283,29 @@ func TestRemoveUser(t *testing.T) {
t.Errorf("removal of unknown user succeeded")
}
}
func TestHasUser(t *testing.T) {
fname := mustCreateDB(t, "")
defer removeIfSuccessful(t, fname)
db := mustLoad(t, fname)
if ok := db.HasUser("unknown"); ok {
t.Errorf("unknown user exists")
}
if err := db.AddUser("user", "passwd"); err != nil {
t.Fatalf("error adding user: %v", err)
}
if ok := db.HasUser("unknown"); ok {
t.Errorf("unknown user exists")
}
if ok := db.HasUser("user"); !ok {
t.Errorf("known user does not exist")
}
if ok := db.HasUser("user"); !ok {
t.Errorf("known user does not exist")
}
}