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")