maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1/*
  2Maddy Mail Server - Composable all-in-one email server.
  3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
  4
  5This program is free software: you can redistribute it and/or modify
  6it under the terms of the GNU General Public License as published by
  7the Free Software Foundation, either version 3 of the License, or
  8(at your option) any later version.
  9
 10This program is distributed in the hope that it will be useful,
 11but WITHOUT ANY WARRANTY; without even the implied warranty of
 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13GNU General Public License for more details.
 14
 15You should have received a copy of the GNU General Public License
 16along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17*/
 18
 19package dnsbl
 20
 21import (
 22	"context"
 23	"errors"
 24	"net"
 25	"testing"
 26
 27	"github.com/foxcpp/go-mockdns"
 28	"github.com/foxcpp/maddy/internal/testutils"
 29)
 30
 31func TestCheckList(t *testing.T) {
 32	test := func(zones map[string]mockdns.Zone, cfg List, ip net.IP, ehlo, mailFrom string, expectedErr error) {
 33		mod := &DNSBL{
 34			resolver: &mockdns.Resolver{Zones: zones},
 35			log:      testutils.Logger(t, "dnsbl"),
 36		}
 37		err := mod.checkList(context.Background(), cfg, ip, ehlo, mailFrom)
 38		if !errors.Is(err, expectedErr) {
 39			t.Errorf("expected err to be '%#v', got '%#v'", expectedErr, err)
 40		}
 41	}
 42
 43	test(nil, List{Zone: "example.org"}, net.IPv4(1, 2, 3, 4),
 44		"example.com", "foo@example.com", nil)
 45	test(map[string]mockdns.Zone{
 46		"4.3.2.1.example.org.": {
 47			A: []string{"127.0.0.1"},
 48		},
 49	}, List{Zone: "example.org", ClientIPv4: true}, net.IPv4(1, 2, 3, 4),
 50		"mx.example.com", "foo@example.com", ListedErr{
 51			Identity: "1.2.3.4",
 52			List:     "example.org",
 53			Reason:   "127.0.0.1",
 54		},
 55	)
 56	test(map[string]mockdns.Zone{
 57		"mx.example.com.example.org.": {
 58			A: []string{"127.0.0.1"},
 59		},
 60	}, List{Zone: "example.org"}, net.IPv4(1, 2, 3, 4),
 61		"mx.example.com", "foo@example.com", nil,
 62	)
 63	test(map[string]mockdns.Zone{
 64		"mx.example.com.example.org.": {
 65			A: []string{"127.0.0.1"},
 66		},
 67	}, List{Zone: "example.org", EHLO: true}, net.IPv4(1, 2, 3, 4),
 68		"mx.example.com", "foo@example.com", ListedErr{
 69			Identity: "mx.example.com",
 70			List:     "example.org",
 71			Reason:   "127.0.0.1",
 72		},
 73	)
 74	test(map[string]mockdns.Zone{
 75		"[1.2.3.4].example.org.": {
 76			A: []string{"127.0.0.1"},
 77		},
 78	}, List{Zone: "example.org", EHLO: true}, net.IPv4(1, 2, 3, 4),
 79		"[1.2.3.4]", "foo@example.com", nil,
 80	)
 81	test(map[string]mockdns.Zone{
 82		"[IPv6:beef::1].example.org.": {
 83			A: []string{"127.0.0.1"},
 84		},
 85	}, List{Zone: "example.org", EHLO: true}, net.IPv4(1, 2, 3, 4),
 86		"[IPv6:beef::1]", "foo@example.com", nil,
 87	)
 88	test(map[string]mockdns.Zone{
 89		"example.com.example.org.": {
 90			A: []string{"127.0.0.1"},
 91		},
 92	}, List{Zone: "example.org"}, net.IPv4(1, 2, 3, 4),
 93		"mx.example.com", "foo@example.com", nil,
 94	)
 95	test(map[string]mockdns.Zone{
 96		"postmaster.example.org.": {
 97			A: []string{"127.0.0.1"},
 98		},
 99	}, List{Zone: "example.org", MAILFROM: true}, net.IPv4(1, 2, 3, 4),
100		"mx.example.com", "postmaster", nil,
101	)
102	test(map[string]mockdns.Zone{
103		".example.org.": {
104			A: []string{"127.0.0.1"},
105		},
106	}, List{Zone: "example.org", MAILFROM: true}, net.IPv4(1, 2, 3, 4),
107		"mx.example.com", "", nil,
108	)
109	test(map[string]mockdns.Zone{
110		"example.com.example.org.": {
111			A: []string{"127.0.0.1"},
112		},
113	}, List{Zone: "example.org", MAILFROM: true}, net.IPv4(1, 2, 3, 4),
114		"mx.example.com", "foo@example.com", ListedErr{
115			Identity: "example.com",
116			List:     "example.org",
117			Reason:   "127.0.0.1",
118		},
119	)
120}
121
122func TestCheckLists(t *testing.T) {
123	test := func(zones map[string]mockdns.Zone, bls []List, ip net.IP, ehlo, mailFrom string, reject, quarantine bool) {
124		mod := &DNSBL{
125			bls:             bls,
126			resolver:        &mockdns.Resolver{Zones: zones},
127			log:             testutils.Logger(t, "dnsbl"),
128			quarantineThres: 1,
129			rejectThres:     2,
130		}
131		result := mod.checkLists(context.Background(), ip, ehlo, mailFrom)
132
133		if result.Reject && !reject {
134			t.Errorf("Expected message to not be rejected")
135		}
136		if !result.Reject && reject {
137			t.Errorf("Expected message to be rejected")
138		}
139		if result.Quarantine && !quarantine {
140			t.Errorf("Expected message to not be quarantined")
141		}
142		if !result.Quarantine && quarantine {
143			t.Errorf("Expected message to be quarantined")
144		}
145	}
146
147	// Score 2 >= 2, reject
148	test(map[string]mockdns.Zone{
149		"4.3.2.1.example.org.": {
150			A: []string{"127.0.0.1"},
151		},
152	}, []List{
153		{
154			Zone:       "example.org",
155			ClientIPv4: true,
156			ScoreAdj:   2,
157		},
158	}, net.IPv4(1, 2, 3, 4),
159		"mx.example.com", "foo@example.com", true, false,
160	)
161
162	// Score 1 >= 1, quarantine
163	test(map[string]mockdns.Zone{
164		"4.3.2.1.example.org.": {
165			A: []string{"127.0.0.1"},
166		},
167	}, []List{
168		{
169			Zone:       "example.org",
170			ClientIPv4: true,
171			ScoreAdj:   1,
172		},
173	}, net.IPv4(1, 2, 3, 4),
174		"mx.example.com", "foo@example.com", false, true,
175	)
176
177	// Score 0, no action
178	test(map[string]mockdns.Zone{
179		"4.3.2.1.example.org.": {
180			A: []string{"127.0.0.1"},
181		},
182		"4.3.2.1.example.net.": {
183			A: []string{"127.0.0.1"},
184		},
185	},
186		[]List{
187			{Zone: "example.org", ClientIPv4: true, ScoreAdj: 1},
188			{Zone: "example.net", ClientIPv4: true, ScoreAdj: -1},
189		},
190		net.IPv4(1, 2, 3, 4),
191		"mx.example.com", "foo@example.com",
192		false, false,
193	)
194
195	// DNS error, hard-fail (reject)
196	test(map[string]mockdns.Zone{
197		"4.3.2.2.example.org.": {
198			Err: &net.DNSError{
199				Err:         "i/o timeout",
200				IsTimeout:   true,
201				IsTemporary: true,
202			},
203		},
204	},
205		[]List{
206			{Zone: "example.org", ClientIPv4: true, ScoreAdj: 1},
207			{Zone: "example.net", ClientIPv4: true, ScoreAdj: 2},
208		},
209		net.IPv4(2, 2, 3, 4),
210		"mx.example.com", "foo@example.com",
211		true, false,
212	)
213}