6	"github.com/mjl-/mox/dns"
 
9func TestAuthResultsPack(t *testing.T) {
 
10	dom, err := dns.ParseDomain("møx.example")
 
12		t.Fatalf("parsing domain: %v", err)
 
14	authRes := AuthResults{
 
15		Hostname: dom.XName(true),
 
16		Comment:  dom.ASCIIExtra(true),
 
17		Methods: []AuthMethod{
 
18			{"dkim", "", "pass", "", "", []AuthProp{{"header", "d", dom.XName(true), true, dom.ASCIIExtra(true)}}},
 
22	const exp = "Authentication-Results: (xn--mx-lka.example) møx.example;\r\n\tdkim=pass header.d=møx.example (xn--mx-lka.example)\r\n"
 
24		t.Fatalf("got %q, expected %q", s, exp)
 
28func TestAuthResultsParse(t *testing.T) {
 
29	ar, err := ParseAuthResults("(xn--mx-lka.example) møx.example;\r\n\tdkim=pass header.d=møx.example (xn--mx-lka.example)\r\n")
 
30	tcheck(t, err, "parsing auth results header")
 
31	tcompare(t, ar, AuthResults{
 
32		Hostname: "møx.example",
 
33		Methods: []AuthMethod{
 
38					{Type: "header", Property: "d", Value: "møx.example"},
 
44	const localhost = `localhost;
 
45	auth=pass smtp.mailfrom=mox+qvpVtG6ZQg-vJmN_beaGyQ@localhost
 
47	ar, err = ParseAuthResults(localhost)
 
48	tcheck(t, err, "parsing auth results header")
 
49	tcompare(t, ar, AuthResults{
 
50		Hostname: "localhost",
 
51		Methods: []AuthMethod{
 
56					{Type: "smtp", Property: "mailfrom", IsAddrLike: true, Value: "mox+qvpVtG6ZQg-vJmN_beaGyQ@localhost"},
 
62	const other = `komijn.test.xmox.nl;
 
63	iprev=pass (without dnssec) policy.iprev=198.2.145.102;
 
64	dkim=pass (2048 bit rsa, without dnssec) header.d=mandrillapp.com
 
65	header.s=mte1 header.a=rsa-sha256 header.b="CfNW8cht1/v3";
 
66	dkim=pass (2048 bit rsa, without dnssec) header.d=letsencrypt.org
 
67	header.s=mte1 header.a=rsa-sha256 header.b=F9lCi4OC77su
 
68	header.i=expiry@letsencrypt.org;
 
69	spf=pass (without dnssec) smtp.mailfrom=delivery.letsencrypt.org;
 
70	dmarc=pass (without dnssec) header.from=letsencrypt.org
 
73	ar, err = ParseAuthResults(other)
 
74	tcheck(t, err, "parsing auth results header")
 
75	tcompare(t, ar, AuthResults{
 
76		Hostname: "komijn.test.xmox.nl",
 
77		Methods: []AuthMethod{
 
82					{Type: "policy", Property: "iprev", Value: "198.2.145.102"},
 
89					{Type: "header", Property: "d", Value: "mandrillapp.com"},
 
90					{Type: "header", Property: "s", Value: "mte1"},
 
91					{Type: "header", Property: "a", Value: "rsa-sha256"},
 
92					{Type: "header", Property: "b", Value: "CfNW8cht1/v3"},
 
99					{Type: "header", Property: "d", Value: "letsencrypt.org"},
 
100					{Type: "header", Property: "s", Value: "mte1"},
 
101					{Type: "header", Property: "a", Value: "rsa-sha256"},
 
102					{Type: "header", Property: "b", Value: "F9lCi4OC77su"},
 
103					{Type: "header", Property: "i", IsAddrLike: true, Value: "expiry@letsencrypt.org"},
 
110					{Type: "smtp", Property: "mailfrom", Value: "delivery.letsencrypt.org"},
 
117					{Type: "header", Property: "from", Value: "letsencrypt.org"},
 
123	const google = `mx.google.com;
 
124       dkim=pass header.i=@test.xmox.nl header.s=2022b header.b="Z9k/yZIA";
 
125       spf=pass (google.com: domain of mjl@test.xmox.nl designates 2a02:2770::21a:4aff:feba:bde0 as permitted sender) smtp.mailfrom=mjl@test.xmox.nl;
 
126       dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=test.xmox.nl
 
129	ar, err = ParseAuthResults(google)
 
130	tcheck(t, err, "parsing auth results header")
 
131	tcompare(t, ar, AuthResults{
 
132		Hostname: "mx.google.com",
 
133		Methods: []AuthMethod{
 
138					{Type: "header", Property: "i", IsAddrLike: true, Value: "@test.xmox.nl"},
 
139					{Type: "header", Property: "s", Value: "2022b"},
 
140					{Type: "header", Property: "b", Value: "Z9k/yZIA"},
 
147					{Type: "smtp", Property: "mailfrom", IsAddrLike: true, Value: "mjl@test.xmox.nl"},
 
154					{Type: "header", Property: "from", Value: "test.xmox.nl"},
 
160	const yahoo = `atlas220.free.mail.bf1.yahoo.com;
 
161 dkim=perm_fail header.i=@ueber.net header.s=2023a;
 
162 dkim=pass header.i=@ueber.net header.s=2023b;
 
163 spf=pass smtp.mailfrom=ueber.net;
 
164 dmarc=pass(p=REJECT) header.from=ueber.net;
 
166	ar, err = ParseAuthResults(yahoo)
 
167	tcheck(t, err, "parsing auth results header")
 
168	tcompare(t, ar, AuthResults{
 
169		Hostname: "atlas220.free.mail.bf1.yahoo.com",
 
170		Methods: []AuthMethod{
 
175					{Type: "header", Property: "i", IsAddrLike: true, Value: "@ueber.net"},
 
176					{Type: "header", Property: "s", Value: "2023a"},
 
183					{Type: "header", Property: "i", IsAddrLike: true, Value: "@ueber.net"},
 
184					{Type: "header", Property: "s", Value: "2023b"},
 
191					{Type: "smtp", Property: "mailfrom", Value: "ueber.net"},
 
198					{Type: "header", Property: "from", Value: "ueber.net"},
 
204	const proton0 = `mail.protonmail.ch; dkim=pass (Good
 
205    ed25519-sha256 signature) header.d=ueber.net header.i=mechiel@ueber.net
 
206    header.a=ed25519-sha256; dkim=pass (Good 2048 bit rsa-sha256 signature)
 
207    header.d=ueber.net header.i=mechiel@ueber.net header.a=rsa-sha256
 
209	ar, err = ParseAuthResults(proton0)
 
210	tcheck(t, err, "parsing auth results header")
 
211	tcompare(t, ar, AuthResults{
 
212		Hostname: "mail.protonmail.ch",
 
213		Methods: []AuthMethod{
 
218					{Type: "header", Property: "d", Value: "ueber.net"},
 
219					{Type: "header", Property: "i", IsAddrLike: true, Value: "mechiel@ueber.net"},
 
220					{Type: "header", Property: "a", Value: "ed25519-sha256"},
 
227					{Type: "header", Property: "d", Value: "ueber.net"},
 
228					{Type: "header", Property: "i", IsAddrLike: true, Value: "mechiel@ueber.net"},
 
229					{Type: "header", Property: "a", Value: "rsa-sha256"},
 
235	const proton1 = `mail.protonmail.ch; dmarc=pass (p=reject dis=none)
 
236 header.from=ueber.net
 
238	ar, err = ParseAuthResults(proton1)
 
239	tcheck(t, err, "parsing auth results header")
 
240	tcompare(t, ar, AuthResults{
 
241		Hostname: "mail.protonmail.ch",
 
242		Methods: []AuthMethod{
 
247					{Type: "header", Property: "from", Value: "ueber.net"},
 
252	const proton2 = `mail.protonmail.ch; spf=pass smtp.mailfrom=ueber.net
 
254	ar, err = ParseAuthResults(proton2)
 
255	tcheck(t, err, "parsing auth results header")
 
256	tcompare(t, ar, AuthResults{
 
257		Hostname: "mail.protonmail.ch",
 
258		Methods: []AuthMethod{
 
263					{Type: "smtp", Property: "mailfrom", Value: "ueber.net"},
 
268	const proton3 = `mail.protonmail.ch; arc=none smtp.remote-ip=84.22.96.237
 
270	ar, err = ParseAuthResults(proton3)
 
271	tcheck(t, err, "parsing auth results header")
 
272	tcompare(t, ar, AuthResults{
 
273		Hostname: "mail.protonmail.ch",
 
274		Methods: []AuthMethod{
 
279					{Type: "smtp", Property: "remote-ip", Value: "84.22.96.237"},
 
284	const proton4 = `mail.protonmail.ch; dkim=permerror (0-bit key) header.d=ueber.net
 
285 header.i=mechiel@ueber.net header.b="a4SMWyJ7"; dkim=pass (2048-bit key)
 
286 header.d=ueber.net header.i=mechiel@ueber.net header.b="mQickWQ7"
 
288	ar, err = ParseAuthResults(proton4)
 
289	tcheck(t, err, "parsing auth results header")
 
290	tcompare(t, ar, AuthResults{
 
291		Hostname: "mail.protonmail.ch",
 
292		Methods: []AuthMethod{
 
297					{Type: "header", Property: "d", Value: "ueber.net"},
 
298					{Type: "header", Property: "i", IsAddrLike: true, Value: "mechiel@ueber.net"},
 
299					{Type: "header", Property: "b", Value: "a4SMWyJ7"},
 
306					{Type: "header", Property: "d", Value: "ueber.net"},
 
307					{Type: "header", Property: "i", IsAddrLike: true, Value: "mechiel@ueber.net"},
 
308					{Type: "header", Property: "b", Value: "mQickWQ7"},
 
314	const dkimReason = `host.example;
 
315 dkim=none reason="no dkim signatures"
 
317	ar, err = ParseAuthResults(dkimReason)
 
318	tcheck(t, err, "parsing auth results header")
 
319	tcompare(t, ar, AuthResults{
 
320		Hostname: "host.example",
 
321		Methods: []AuthMethod{
 
325				Reason: "no dkim signatures",
 
330	// Outlook adds an invalid line, missing required hostname at the start. And their
 
331	// dmarc "action=none" is invalid. Nothing to be done.
 
332	const outlook = `x; spf=pass (sender IP is 84.22.96.237)
 
333 smtp.mailfrom=ueber.net; dkim=pass (signature was verified)
 
334 header.d=ueber.net;dmarc=pass action=none header.from=ueber.net;compauth=pass
 
337	_, err = ParseAuthResults(outlook)
 
339		t.Fatalf("missing error while parsing authresults header from outlook")