1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package dmarc2021import (22 "bufio"23 "strings"24 "testing"2526 "github.com/emersion/go-message/textproto"27 "github.com/emersion/go-msgauth/authres"28 "github.com/emersion/go-msgauth/dmarc"29)3031func TestEvaluateAlignment(t *testing.T) {32 type tCase struct {33 fromDomain string34 record *Record35 results []authres.Result3637 output authres.ResultValue38 }39 test := func(i int, c tCase) {40 out := EvaluateAlignment(c.fromDomain, c.record, c.results)41 t.Logf("%d - %+v", i, out)42 if out.Authres.Value != c.output {43 t.Errorf("%d: Wrong eval result, want '%s', got '%s' (%+v)", i, c.output, out.Authres.Value, out)44 }45 }4647 cases := []tCase{48 { // 049 fromDomain: "example.org",50 record: &Record{},5152 output: authres.ResultNone,53 },54 { // 155 fromDomain: "example.org",56 record: &Record{},57 results: []authres.Result{58 &authres.SPFResult{59 Value: authres.ResultFail,60 From: "example.org",61 Helo: "mx.example.org",62 },63 &authres.DKIMResult{64 Value: authres.ResultNone,65 Domain: "example.org",66 },67 },68 output: authres.ResultFail,69 },70 { // 271 fromDomain: "example.org",72 record: &Record{},73 results: []authres.Result{74 &authres.SPFResult{75 Value: authres.ResultPass,76 From: "example.org",77 Helo: "mx.example.org",78 },79 &authres.DKIMResult{80 Value: authres.ResultNone,81 Domain: "example.org",82 },83 },84 output: authres.ResultPass,85 },86 { // 387 fromDomain: "example.org",88 record: &Record{},89 results: []authres.Result{90 &authres.SPFResult{91 Value: authres.ResultFail,92 From: "example.org",93 Helo: "mx.example.org",94 },95 &authres.DKIMResult{96 Value: authres.ResultNone,97 Domain: "example.org",98 },99 },100 output: authres.ResultFail,101 },102 { // 4103 fromDomain: "example.org",104 record: &Record{},105 results: []authres.Result{106 &authres.SPFResult{107 Value: authres.ResultPass,108 From: "example.com",109 Helo: "mx.example.com",110 },111 &authres.DKIMResult{112 Value: authres.ResultNone,113 Domain: "example.org",114 },115 },116 output: authres.ResultFail,117 },118 { // 5119 fromDomain: "example.com",120 record: &Record{},121 results: []authres.Result{122 &authres.SPFResult{123 Value: authres.ResultPass,124 From: "cbg.bounces.example.com",125 Helo: "mx.example.com",126 },127 &authres.DKIMResult{128 Value: authres.ResultNone,129 Domain: "example.org",130 },131 },132 output: authres.ResultPass,133 },134 { // 6135 fromDomain: "example.com",136 record: &Record{137 SPFAlignment: dmarc.AlignmentStrict,138 },139 results: []authres.Result{140 &authres.SPFResult{141 Value: authres.ResultPass,142 From: "cbg.bounces.example.com",143 Helo: "mx.example.com",144 },145 &authres.DKIMResult{146 Value: authres.ResultNone,147 Domain: "example.org",148 },149 },150 output: authres.ResultFail,151 },152 { // 7153 fromDomain: "example.org",154 record: &Record{},155 results: []authres.Result{156 &authres.DKIMResult{157 Value: authres.ResultFail,158 Domain: "example.org",159 },160 &authres.SPFResult{161 Value: authres.ResultNone,162 From: "example.org",163 Helo: "mx.example.org",164 },165 },166 output: authres.ResultFail,167 },168 { // 8169 fromDomain: "example.org",170 record: &Record{},171 results: []authres.Result{172 &authres.DKIMResult{173 Value: authres.ResultPass,174 Domain: "example.org",175 },176 &authres.SPFResult{177 Value: authres.ResultNone,178 From: "example.org",179 Helo: "mx.example.org",180 },181 },182 output: authres.ResultPass,183 },184 { // 9185 fromDomain: "example.com",186 record: &Record{},187 results: []authres.Result{188 &authres.SPFResult{189 Value: authres.ResultPass,190 From: "cbg.bounces.example.com",191 Helo: "mx.example.com",192 },193 &authres.DKIMResult{194 Value: authres.ResultPass,195 Domain: "example.com",196 },197 },198 output: authres.ResultPass,199 },200 { // 10201 fromDomain: "example.com",202 record: &Record{203 SPFAlignment: dmarc.AlignmentRelaxed,204 },205 results: []authres.Result{206 &authres.SPFResult{207 Value: authres.ResultPass,208 From: "cbg.bounces.example.com",209 Helo: "mx.example.com",210 },211 &authres.DKIMResult{212 Value: authres.ResultFail,213 Domain: "example.com",214 },215 },216 output: authres.ResultPass,217 },218 { // 11219 fromDomain: "example.com",220 record: &Record{221 SPFAlignment: dmarc.AlignmentStrict,222 },223 results: []authres.Result{224 &authres.SPFResult{225 Value: authres.ResultPass,226 From: "cbg.bounces.example.com",227 Helo: "mx.example.com",228 },229 &authres.DKIMResult{230 Value: authres.ResultPass,231 Domain: "example.com",232 },233 },234 output: authres.ResultPass,235 },236 { // 12237 fromDomain: "example.com",238 record: &Record{239 SPFAlignment: dmarc.AlignmentStrict,240 DKIMAlignment: dmarc.AlignmentStrict,241 },242 results: []authres.Result{243 &authres.SPFResult{244 Value: authres.ResultPass,245 From: "cbg.bounces.example.com",246 Helo: "mx.example.com",247 },248 &authres.DKIMResult{249 Value: authres.ResultFail,250 Domain: "cbg.example.com",251 },252 },253 output: authres.ResultFail,254 },255 { // 13256 fromDomain: "example.org",257 record: &Record{},258 results: []authres.Result{259 &authres.DKIMResult{260 Value: authres.ResultFail,261 Domain: "example.org",262 },263 &authres.DKIMResult{264 Value: authres.ResultPass,265 Domain: "example.net",266 },267 &authres.DKIMResult{268 Value: authres.ResultPass,269 Domain: "example.org",270 },271 &authres.DKIMResult{272 Value: authres.ResultFail,273 Domain: "example.com",274 },275 &authres.SPFResult{276 Value: authres.ResultNone,277 From: "example.org",278 Helo: "mx.example.org",279 },280 },281 output: authres.ResultPass,282 },283 { // 14284 fromDomain: "example.com",285 record: &Record{},286 results: []authres.Result{287 &authres.SPFResult{288 Value: authres.ResultPass,289 From: "",290 Helo: "mx.example.com",291 },292 &authres.DKIMResult{293 Value: authres.ResultNone,294 Domain: "example.org",295 },296 },297 output: authres.ResultPass,298 },299 { // 15300 fromDomain: "example.com",301 record: &Record{302 SPFAlignment: dmarc.AlignmentStrict,303 },304 results: []authres.Result{305 &authres.SPFResult{306 Value: authres.ResultPass,307 From: "",308 Helo: "mx.example.com",309 },310 &authres.DKIMResult{311 Value: authres.ResultNone,312 Domain: "example.org",313 },314 },315 output: authres.ResultFail,316 },317 { // 16318 fromDomain: "example.com",319 record: &Record{},320 results: []authres.Result{321 &authres.SPFResult{322 Value: authres.ResultTempError,323 From: "",324 Helo: "mx.example.com",325 },326 &authres.DKIMResult{327 Value: authres.ResultNone,328 Domain: "example.org",329 },330 },331 output: authres.ResultTempError,332 },333 { // 17334 fromDomain: "example.com",335 record: &Record{},336 results: []authres.Result{337 &authres.DKIMResult{338 Value: authres.ResultTempError,339 Domain: "example.com",340 },341 &authres.SPFResult{342 Value: authres.ResultNone,343 From: "example.org",344 Helo: "mx.example.org",345 },346 },347 output: authres.ResultTempError,348 },349 { // 18350 fromDomain: "example.com",351 record: &Record{},352 results: []authres.Result{353 &authres.SPFResult{354 Value: authres.ResultTempError,355 From: "",356 Helo: "mx.example.com",357 },358 &authres.DKIMResult{359 Value: authres.ResultPass,360 Domain: "example.com",361 },362 },363 output: authres.ResultPass,364 },365 { // 19366 fromDomain: "example.com",367 record: &Record{},368 results: []authres.Result{369 &authres.SPFResult{370 Value: authres.ResultPass,371 From: "",372 Helo: "mx.example.com",373 },374 &authres.DKIMResult{375 Value: authres.ResultTempError,376 Domain: "example.com",377 },378 },379 output: authres.ResultPass,380 },381 { // 20382 fromDomain: "example.org",383 record: &Record{},384 results: []authres.Result{385 &authres.DKIMResult{386 Value: authres.ResultPass,387 Domain: "example.org",388 },389 &authres.DKIMResult{390 Value: authres.ResultTempError,391 Domain: "example.org",392 },393 &authres.SPFResult{394 Value: authres.ResultNone,395 From: "example.org",396 Helo: "mx.example.org",397 },398 },399 output: authres.ResultPass,400 },401 { // 21402 fromDomain: "example.org",403 record: &Record{},404 results: []authres.Result{405 &authres.DKIMResult{406 Value: authres.ResultFail,407 Domain: "example.org",408 },409 &authres.DKIMResult{410 Value: authres.ResultTempError,411 Domain: "example.org",412 },413 &authres.SPFResult{414 Value: authres.ResultNone,415 From: "example.org",416 Helo: "mx.example.org",417 },418 },419 output: authres.ResultTempError,420 },421 { // 22422 fromDomain: "example.org",423 record: &Record{},424 results: []authres.Result{425 &authres.DKIMResult{426 Value: authres.ResultNone,427 Domain: "example.org",428 },429 &authres.SPFResult{430 Value: authres.ResultNone,431 From: "example.org",432 Helo: "mx.example.org",433 },434 },435 output: authres.ResultFail,436 },437 { // 23438 fromDomain: "sub.example.org",439 record: &Record{},440 results: []authres.Result{441 &authres.DKIMResult{442 Value: authres.ResultPass,443 Domain: "mx.example.org",444 },445 &authres.SPFResult{446 Value: authres.ResultNone,447 From: "example.org",448 Helo: "mx.example.org",449 },450 },451 output: authres.ResultPass,452 },453 }454 for i, case_ := range cases {455 test(i, case_)456 }457}458459func TestExtractDomains(t *testing.T) {460 type tCase struct {461 hdr string462463 fromDomain string464 }465 test := func(i int, c tCase) {466 hdr, err := textproto.ReadHeader(bufio.NewReader(strings.NewReader(c.hdr + "\n\n")))467 if err != nil {468 panic(err)469 }470471 domain, err := ExtractFromDomain(hdr)472 if c.fromDomain == "" && err == nil {473 t.Errorf("%d: expected failure, got fromDomain = %s", i, domain)474 return475 }476 if c.fromDomain != "" && err != nil {477 t.Errorf("%d: unexpected error: %v", i, err)478 return479 }480 if domain != c.fromDomain {481 t.Errorf("%d: want fromDomain = %v but got %s", i, c.fromDomain, domain)482 }483 }484485 cases := []tCase{486 {487 hdr: `From: <test@example.org>`,488 fromDomain: "example.org",489 },490 {491 hdr: `From: <test@foo.example.org>`,492 fromDomain: "foo.example.org",493 },494 {495 hdr: `From: <test@foo.example.org>, <test@bar.example.org>`,496 },497 {498 hdr: `From: <test@foo.example.org>,499From: <test@bar.example.org>`,500 },501 {502 hdr: `From: <test@>`,503 },504 {505 hdr: `From: `,506 },507 {508 hdr: `From: foo`,509 },510 }511 for i, case_ := range cases {512 test(i, case_)513 }514}