author | Alberto Bertogli
<albertito@blitiri.com.ar> 2021-09-03 10:09:54 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2021-10-10 10:28:40 UTC |
parent | 805c97b5e162fc3b59cf6eb4bd639c1cf0ed22a1 |
spf.go | +60 | -51 |
spf_test.go | +78 | -78 |
diff --git a/spf.go b/spf.go index fb7cd83..23aee29 100644 --- a/spf.go +++ b/spf.go @@ -28,6 +28,7 @@ package spf // import "blitiri.com.ar/go/spf" import ( "context" + "errors" "fmt" "net" "net/url" @@ -78,23 +79,31 @@ var qualToResult = map[byte]Result{ '?': Neutral, } +// Errors returned by the library. Note that the errors returned in different +// situations may change over time, and new ones may be added. Be careful +// about over-relying on these. var ( - errLookupLimitReached = fmt.Errorf("lookup limit reached") - errUnknownField = fmt.Errorf("unknown field") - errInvalidIP = fmt.Errorf("invalid ipX value") - errInvalidMask = fmt.Errorf("invalid mask") - errInvalidMacro = fmt.Errorf("invalid macro") - errInvalidDomain = fmt.Errorf("invalid domain") - errNoResult = fmt.Errorf("no DNS record found") - errMultipleRecords = fmt.Errorf("multiple matching DNS records") - errTooManyMXRecords = fmt.Errorf("too many MX records") - - errMatchedAll = fmt.Errorf("matched 'all'") - errMatchedA = fmt.Errorf("matched 'a'") - errMatchedIP = fmt.Errorf("matched 'ip'") - errMatchedMX = fmt.Errorf("matched 'mx'") - errMatchedPTR = fmt.Errorf("matched 'ptr'") - errMatchedExists = fmt.Errorf("matched 'exists'") + // Errors related to an invalid SPF record. + ErrUnknownField = errors.New("unknown field") + ErrInvalidIP = errors.New("invalid ipX value") + ErrInvalidMask = errors.New("invalid mask") + ErrInvalidMacro = errors.New("invalid macro") + ErrInvalidDomain = errors.New("invalid domain") + + // Errors related to DNS lookups. + // Note that the library functions may also return net.DNSError. + ErrNoResult = errors.New("no DNS record found") + ErrLookupLimitReached = errors.New("lookup limit reached") + ErrTooManyMXRecords = errors.New("too many MX records") + ErrMultipleRecords = errors.New("multiple matching DNS records") + + // Errors returned on a successful match. + ErrMatchedAll = errors.New("matched all") + ErrMatchedA = errors.New("matched a") + ErrMatchedIP = errors.New("matched ip") + ErrMatchedMX = errors.New("matched mx") + ErrMatchedPTR = errors.New("matched ptr") + ErrMatchedExists = errors.New("matched exists") ) // Default value for the maximum number of DNS lookups while resolving SPF. @@ -276,7 +285,7 @@ func (r *resolution) Check(domain string) (Result, error) { r.trace("dns temp error: %v", err) return TempError, err } - if err == errMultipleRecords { + if err == ErrMultipleRecords { r.trace("multiple dns records") return PermError, err } @@ -290,7 +299,7 @@ func (r *resolution) Check(domain string) (Result, error) { if txt == "" { // No record => None. // https://tools.ietf.org/html/rfc7208#section-4.5 - return None, errNoResult + return None, ErrNoResult } fields := strings.Split(txt, " ") @@ -308,7 +317,7 @@ func (r *resolution) Check(domain string) (Result, error) { if len(redirects) > 1 { // At most a single redirect is allowed. // https://tools.ietf.org/html/rfc7208#section-6 - return PermError, errInvalidDomain + return PermError, ErrInvalidDomain } fields = append(newfields, redirects...) @@ -328,7 +337,7 @@ func (r *resolution) Check(domain string) (Result, error) { // https://tools.ietf.org/html/rfc7208#section-4.6.4 if r.count > r.maxcount { r.trace("lookup limit reached") - return PermError, errLookupLimitReached + return PermError, ErrLookupLimitReached } // See if we have a qualifier, defaulting to + (pass). @@ -347,7 +356,7 @@ func (r *resolution) Check(domain string) (Result, error) { if lfield == "all" { // https://tools.ietf.org/html/rfc7208#section-5.1 r.trace("%v matched all", result) - return result, errMatchedAll + return result, ErrMatchedAll } else if strings.HasPrefix(lfield, "include:") { if ok, res, err := r.includeField(result, field, domain); ok { r.trace("include ok, %v %v", res, err) @@ -387,7 +396,7 @@ func (r *resolution) Check(domain string) (Result, error) { } else { // http://www.openspf.org/SPF_Record_Syntax r.trace("permerror, unknown field") - return PermError, errUnknownField + return PermError, ErrUnknownField } } @@ -434,7 +443,7 @@ func (r *resolution) getDNSRecord(domain string) (string, error) { } else if l == 1 { return records[0], nil } - return "", errMultipleRecords + return "", ErrMultipleRecords } func isTemporary(err error) bool { @@ -448,18 +457,18 @@ func (r *resolution) ipField(res Result, field string) (bool, Result, error) { if strings.Contains(fip, "/") { _, ipnet, err := net.ParseCIDR(fip) if err != nil { - return true, PermError, errInvalidMask + return true, PermError, ErrInvalidMask } if ipnet.Contains(r.ip) { - return true, res, errMatchedIP + return true, res, ErrMatchedIP } } else { ip := net.ParseIP(fip) if ip == nil { - return true, PermError, errInvalidIP + return true, PermError, ErrInvalidIP } if ip.Equal(r.ip) { - return true, res, errMatchedIP + return true, res, ErrMatchedIP } } @@ -476,11 +485,11 @@ func (r *resolution) ptrField(res Result, field, domain string) (bool, Result, e } ptrDomain, err := r.expandMacros(ptrDomain, domain) if err != nil { - return true, PermError, errInvalidMacro + return true, PermError, ErrInvalidMacro } if ptrDomain == "" { - return true, PermError, errInvalidDomain + return true, PermError, ErrInvalidDomain } if r.ipNames == nil { @@ -499,7 +508,7 @@ func (r *resolution) ptrField(res Result, field, domain string) (bool, Result, e // have some A/AAAA. // https://tools.ietf.org/html/rfc7208#section-5.5 if r.count > 10 { - return false, "", errLookupLimitReached + return false, "", ErrLookupLimitReached } r.count++ addrs, err := r.resolver.LookupIPAddr(r.ctx, n) @@ -520,7 +529,7 @@ func (r *resolution) ptrField(res Result, field, domain string) (bool, Result, e ptrDomain = strings.ToLower(ptrDomain) for _, n := range r.ipNames { if strings.HasSuffix(n, ptrDomain+".") { - return true, res, errMatchedPTR + return true, res, ErrMatchedPTR } } @@ -534,11 +543,11 @@ func (r *resolution) existsField(res Result, field, domain string) (bool, Result eDomain := field[7:] eDomain, err := r.expandMacros(eDomain, domain) if err != nil { - return true, PermError, errInvalidMacro + return true, PermError, ErrInvalidMacro } if eDomain == "" { - return true, PermError, errInvalidDomain + return true, PermError, ErrInvalidDomain } r.count++ @@ -554,7 +563,7 @@ func (r *resolution) existsField(res Result, field, domain string) (bool, Result // Exists only counts if there are IPv4 matches. for _, ip := range ips { if ip.IP.To4() != nil { - return true, res, errMatchedExists + return true, res, ErrMatchedExists } } return false, "", nil @@ -566,7 +575,7 @@ func (r *resolution) includeField(res Result, field, domain string) (bool, Resul incdomain := field[len("include:"):] incdomain, err := r.expandMacros(incdomain, domain) if err != nil { - return true, PermError, errInvalidMacro + return true, PermError, ErrInvalidMacro } ir, err := r.Check(incdomain) switch ir { @@ -582,7 +591,7 @@ func (r *resolution) includeField(res Result, field, domain string) (bool, Resul return true, PermError, err } - return false, "", fmt.Errorf("This should never be reached") + return false, "", fmt.Errorf("this should never be reached") } type dualMasks struct { @@ -620,7 +629,7 @@ func domainAndMask(re *regexp.Regexp, field, domain string) (string, dualMasks, i, err := strconv.Atoi(groups[4]) mask4 := net.CIDRMask(i, 32) if err != nil || mask4 == nil { - return "", masks, errInvalidMask + return "", masks, ErrInvalidMask } masks.v4 = mask4 } @@ -628,7 +637,7 @@ func domainAndMask(re *regexp.Regexp, field, domain string) (string, dualMasks, i, err := strconv.Atoi(groups[6]) mask6 := net.CIDRMask(i, 128) if err != nil || mask6 == nil { - return "", masks, errInvalidMask + return "", masks, ErrInvalidMask } masks.v6 = mask6 } @@ -637,7 +646,7 @@ func domainAndMask(re *regexp.Regexp, field, domain string) (string, dualMasks, // Test to catch malformed entries: if there's a /, there must be at least // one mask. if strings.Contains(field, "/") && masks.v4 == nil && masks.v6 == nil { - return "", masks, errInvalidMask + return "", masks, ErrInvalidMask } return domain, masks, nil @@ -653,7 +662,7 @@ func (r *resolution) aField(res Result, field, domain string) (bool, Result, err } aDomain, err = r.expandMacros(aDomain, domain) if err != nil { - return true, PermError, errInvalidMacro + return true, PermError, ErrInvalidMacro } r.count++ @@ -668,7 +677,7 @@ func (r *resolution) aField(res Result, field, domain string) (bool, Result, err for _, ip := range ips { if ipMatch(r.ip, ip.IP, masks) { r.trace("a matched %v, %v, %v", r.ip, ip.IP, masks) - return true, res, errMatchedA + return true, res, ErrMatchedA } } @@ -685,7 +694,7 @@ func (r *resolution) mxField(res Result, field, domain string) (bool, Result, er } mxDomain, err = r.expandMacros(mxDomain, domain) if err != nil { - return true, PermError, errInvalidMacro + return true, PermError, ErrInvalidMacro } r.count++ @@ -701,7 +710,7 @@ func (r *resolution) mxField(res Result, field, domain string) (bool, Result, er // There's an explicit maximum of 10 MX records per match. // https://tools.ietf.org/html/rfc7208#section-4.6.4 if len(mxs) > 10 { - return true, PermError, errTooManyMXRecords + return true, PermError, ErrTooManyMXRecords } mxips := []net.IP{} @@ -722,7 +731,7 @@ func (r *resolution) mxField(res Result, field, domain string) (bool, Result, er for _, ip := range mxips { if ipMatch(r.ip, ip, masks) { r.trace("mx matched %v, %v, %v", r.ip, ip, masks) - return true, res, errMatchedMX + return true, res, ErrMatchedMX } } @@ -734,11 +743,11 @@ func (r *resolution) redirectField(field, domain string) (Result, error) { rDomain := field[len("redirect="):] rDomain, err := r.expandMacros(rDomain, domain) if err != nil { - return PermError, errInvalidMacro + return PermError, ErrInvalidMacro } if rDomain == "" { - return PermError, errInvalidDomain + return PermError, ErrInvalidDomain } // https://tools.ietf.org/html/rfc7208#section-6.1 @@ -764,7 +773,7 @@ func (r *resolution) expandMacros(s, domain string) (string, error) { // doesn't, prevent them from sneaking through. if strings.Contains(s, "/") { r.trace("macro contains /") - return "", errInvalidDomain + return "", ErrInvalidDomain } // Bypass the complex logic if there are no macros present. @@ -800,7 +809,7 @@ func (r *resolution) expandMacros(s, domain string) (string, error) { inMacroDefinition = true continue } - return "", errInvalidMacro + return "", ErrInvalidMacro } if inMacroDefinition { if c != '}' { @@ -815,7 +824,7 @@ func (r *resolution) expandMacros(s, domain string) (string, error) { r.trace("macro %q: %q", macroS, groups) macroS = "" if groups == nil { - return "", errInvalidMacro + return "", ErrInvalidMacro } letter := groups[1] @@ -825,7 +834,7 @@ func (r *resolution) expandMacros(s, domain string) (string, error) { // valid. digits, err = strconv.Atoi(groups[2]) if err != nil || digits <= 0 { - return "", errInvalidMacro + return "", ErrInvalidMacro } } reverse := groups[3] == "r" || groups[3] == "R" @@ -867,7 +876,7 @@ func (r *resolution) expandMacros(s, domain string) (string, error) { default: // c, r, t are allowed in exp only, and we don't expand macros // in exp so they are just as invalid as the rest. - return "", errInvalidMacro + return "", ErrInvalidMacro } // Split str using the given separators. diff --git a/spf_test.go b/spf_test.go index c44b12e..5e4a595 100644 --- a/spf_test.go +++ b/spf_test.go @@ -36,48 +36,48 @@ func TestBasic(t *testing.T) { res Result err error }{ - {"", None, errNoResult}, - {"blah", None, errNoResult}, + {"", None, ErrNoResult}, + {"blah", None, ErrNoResult}, {"v=spf1", Neutral, nil}, {"v=spf1 ", Neutral, nil}, - {"v=spf1 -", PermError, errUnknownField}, - {"v=spf1 all", Pass, errMatchedAll}, - {"v=spf1 exp=blah +all", Pass, errMatchedAll}, - {"v=spf1 +all", Pass, errMatchedAll}, - {"v=spf1 -all ", Fail, errMatchedAll}, - {"v=spf1 ~all", SoftFail, errMatchedAll}, - {"v=spf1 ?all", Neutral, errMatchedAll}, - {"v=spf1 a ~all", SoftFail, errMatchedAll}, + {"v=spf1 -", PermError, ErrUnknownField}, + {"v=spf1 all", Pass, ErrMatchedAll}, + {"v=spf1 exp=blah +all", Pass, ErrMatchedAll}, + {"v=spf1 +all", Pass, ErrMatchedAll}, + {"v=spf1 -all ", Fail, ErrMatchedAll}, + {"v=spf1 ~all", SoftFail, ErrMatchedAll}, + {"v=spf1 ?all", Neutral, ErrMatchedAll}, + {"v=spf1 a ~all", SoftFail, ErrMatchedAll}, {"v=spf1 a/24", Neutral, nil}, - {"v=spf1 a:d1110/24", Pass, errMatchedA}, - {"v=spf1 a:d1110/montoto", PermError, errInvalidMask}, - {"v=spf1 a:d1110/99", PermError, errInvalidMask}, + {"v=spf1 a:d1110/24", Pass, ErrMatchedA}, + {"v=spf1 a:d1110/montoto", PermError, ErrInvalidMask}, + {"v=spf1 a:d1110/99", PermError, ErrInvalidMask}, {"v=spf1 a:d1110/32", Neutral, nil}, {"v=spf1 a:d1110", Neutral, nil}, - {"v=spf1 a:d1111", Pass, errMatchedA}, + {"v=spf1 a:d1111", Pass, ErrMatchedA}, {"v=spf1 a:nothing/24", Neutral, nil}, {"v=spf1 mx", Neutral, nil}, {"v=spf1 mx/24", Neutral, nil}, - {"v=spf1 mx:a/montoto ~all", PermError, errInvalidMask}, - {"v=spf1 mx:d1110/24 ~all", Pass, errMatchedMX}, - {"v=spf1 mx:d1110/24//100 ~all", Pass, errMatchedMX}, - {"v=spf1 mx:d1110/24//129 ~all", PermError, errInvalidMask}, - {"v=spf1 mx:d1110/24/100 ~all", PermError, errInvalidMask}, - {"v=spf1 mx:d1110/99 ~all", PermError, errInvalidMask}, - {"v=spf1 ip4:1.2.3.4 ~all", SoftFail, errMatchedAll}, - {"v=spf1 ip6:12 ~all", PermError, errInvalidIP}, - {"v=spf1 ip4:1.1.1.1 -all", Pass, errMatchedIP}, - {"v=spf1 ip4:1.1.1.1/24 -all", Pass, errMatchedIP}, - {"v=spf1 ip4:1.1.1.1/lala -all", PermError, errInvalidMask}, - {"v=spf1 ip4:1.1.1.1/33 -all", PermError, errInvalidMask}, - {"v=spf1 include:doesnotexist", PermError, errNoResult}, - {"v=spf1 ptr -all", Pass, errMatchedPTR}, - {"v=spf1 ptr:d1111 -all", Pass, errMatchedPTR}, - {"v=spf1 ptr:lalala -all", Pass, errMatchedPTR}, - {"v=spf1 ptr:doesnotexist -all", Fail, errMatchedAll}, - {"v=spf1 blah", PermError, errUnknownField}, - {"v=spf1 exists:d1111 -all", Pass, errMatchedExists}, - {"v=spf1 redirect=", PermError, errInvalidDomain}, + {"v=spf1 mx:a/montoto ~all", PermError, ErrInvalidMask}, + {"v=spf1 mx:d1110/24 ~all", Pass, ErrMatchedMX}, + {"v=spf1 mx:d1110/24//100 ~all", Pass, ErrMatchedMX}, + {"v=spf1 mx:d1110/24//129 ~all", PermError, ErrInvalidMask}, + {"v=spf1 mx:d1110/24/100 ~all", PermError, ErrInvalidMask}, + {"v=spf1 mx:d1110/99 ~all", PermError, ErrInvalidMask}, + {"v=spf1 ip4:1.2.3.4 ~all", SoftFail, ErrMatchedAll}, + {"v=spf1 ip6:12 ~all", PermError, ErrInvalidIP}, + {"v=spf1 ip4:1.1.1.1 -all", Pass, ErrMatchedIP}, + {"v=spf1 ip4:1.1.1.1/24 -all", Pass, ErrMatchedIP}, + {"v=spf1 ip4:1.1.1.1/lala -all", PermError, ErrInvalidMask}, + {"v=spf1 ip4:1.1.1.1/33 -all", PermError, ErrInvalidMask}, + {"v=spf1 include:doesnotexist", PermError, ErrNoResult}, + {"v=spf1 ptr -all", Pass, ErrMatchedPTR}, + {"v=spf1 ptr:d1111 -all", Pass, ErrMatchedPTR}, + {"v=spf1 ptr:lalala -all", Pass, ErrMatchedPTR}, + {"v=spf1 ptr:doesnotexist -all", Fail, ErrMatchedAll}, + {"v=spf1 blah", PermError, ErrUnknownField}, + {"v=spf1 exists:d1111 -all", Pass, ErrMatchedExists}, + {"v=spf1 redirect=", PermError, ErrInvalidDomain}, } dns.Ip["d1111"] = []net.IP{ip1111} @@ -111,24 +111,24 @@ func TestIPv6(t *testing.T) { res Result err error }{ - {"v=spf1 all", Pass, errMatchedAll}, - {"v=spf1 a ~all", SoftFail, errMatchedAll}, + {"v=spf1 all", Pass, ErrMatchedAll}, + {"v=spf1 a ~all", SoftFail, ErrMatchedAll}, {"v=spf1 a/24", Neutral, nil}, - {"v=spf1 a:d6660//24", Pass, errMatchedA}, - {"v=spf1 a:d6660/24//100", Pass, errMatchedA}, + {"v=spf1 a:d6660//24", Pass, ErrMatchedA}, + {"v=spf1 a:d6660/24//100", Pass, ErrMatchedA}, {"v=spf1 a:d6660", Neutral, nil}, - {"v=spf1 a:d6666", Pass, errMatchedA}, + {"v=spf1 a:d6666", Pass, ErrMatchedA}, {"v=spf1 a:nothing//24", Neutral, nil}, - {"v=spf1 mx:d6660//24 ~all", Pass, errMatchedMX}, - {"v=spf1 mx:d6660/24//100 ~all", Pass, errMatchedMX}, - {"v=spf1 mx:d6660/24/100 ~all", PermError, errInvalidMask}, - {"v=spf1 ip6:2001:db8::68 ~all", Pass, errMatchedIP}, - {"v=spf1 ip6:2001:db8::1/24 ~all", Pass, errMatchedIP}, - {"v=spf1 ip6:2001:db8::1/100 ~all", Pass, errMatchedIP}, - {"v=spf1 ptr -all", Pass, errMatchedPTR}, - {"v=spf1 ptr:d6666 -all", Pass, errMatchedPTR}, - {"v=spf1 ptr:sonlas6 -all", Pass, errMatchedPTR}, - {"v=spf1 ptr:sonlas7 -all", Fail, errMatchedAll}, + {"v=spf1 mx:d6660//24 ~all", Pass, ErrMatchedMX}, + {"v=spf1 mx:d6660/24//100 ~all", Pass, ErrMatchedMX}, + {"v=spf1 mx:d6660/24/100 ~all", PermError, ErrInvalidMask}, + {"v=spf1 ip6:2001:db8::68 ~all", Pass, ErrMatchedIP}, + {"v=spf1 ip6:2001:db8::1/24 ~all", Pass, ErrMatchedIP}, + {"v=spf1 ip6:2001:db8::1/100 ~all", Pass, ErrMatchedIP}, + {"v=spf1 ptr -all", Pass, ErrMatchedPTR}, + {"v=spf1 ptr:d6666 -all", Pass, ErrMatchedPTR}, + {"v=spf1 ptr:sonlas6 -all", Pass, ErrMatchedPTR}, + {"v=spf1 ptr:sonlas7 -all", Fail, ErrMatchedAll}, } dns.Ip["d6666"] = []net.IP{ip6666} @@ -165,12 +165,12 @@ func TestInclude(t *testing.T) { res Result err error }{ - {"", PermError, errNoResult}, - {"v=spf1 all", Pass, errMatchedAll}, + {"", PermError, ErrNoResult}, + {"v=spf1 all", Pass, ErrMatchedAll}, // domain2 did not pass, so continued and matched parent's ip4. - {"v=spf1", Pass, errMatchedIP}, - {"v=spf1 -all", Pass, errMatchedIP}, + {"v=spf1", Pass, ErrMatchedIP}, + {"v=spf1 -all", Pass, ErrMatchedIP}, } for _, c := range cases { @@ -189,7 +189,7 @@ func TestRecursionLimit(t *testing.T) { defaultTrace = t.Logf res, err := CheckHost(ip1111, "domain") - if res != PermError || err != errLookupLimitReached { + if res != PermError || err != ErrLookupLimitReached { t.Errorf("expected permerror, got %v (%v)", res, err) } } @@ -220,7 +220,7 @@ func TestInvalidRedirect(t *testing.T) { } res, err = CheckHost(ip1111, "domain") - if res != PermError || err != errNoResult { + if res != PermError || err != ErrNoResult { t.Errorf("expected permerror, got %v (%v)", res, err) } } @@ -234,13 +234,13 @@ func TestRedirectOrder(t *testing.T) { dns.Txt["domain"] = []string{"v=spf1 redirect=faildom"} res, err := CheckHost(ip1111, "domain") - if res != Fail || err != errMatchedAll { + if res != Fail || err != ErrMatchedAll { t.Errorf("expected fail, got %v (%v)", res, err) } dns.Txt["domain"] = []string{"v=spf1 redirect=faildom all"} res, err = CheckHost(ip1111, "domain") - if res != Pass || err != errMatchedAll { + if res != Pass || err != ErrMatchedAll { t.Errorf("expected pass, got %v (%v)", res, err) } } @@ -339,18 +339,18 @@ func TestMacros(t *testing.T) { res Result err error }{ - {"v=spf1 ptr:%{fff} -all", PermError, errInvalidMacro}, - {"v=spf1 mx:%{fff} -all", PermError, errInvalidMacro}, - {"v=spf1 redirect=%{fff}", PermError, errInvalidMacro}, - {"v=spf1 a:%{o0}", PermError, errInvalidMacro}, - {"v=spf1 +a:sss-%{s}-sss", Pass, errMatchedA}, - {"v=spf1 +a:ooo-%{o}-ooo", Pass, errMatchedA}, - {"v=spf1 +a:OOO-%{O}-OOO", Pass, errMatchedA}, - {"v=spf1 +a:ppp-%{p}-ppp", Pass, errMatchedA}, - {"v=spf1 +a:vvv-%{v}-vvv", Pass, errMatchedA}, - {"v=spf1 a:%{x}", PermError, errInvalidMacro}, - {"v=spf1 +a:ooo-%{o7}-ooo", Pass, errMatchedA}, - {"v=spf1 exists:%{ir}.vvv -all", Pass, errMatchedExists}, + {"v=spf1 ptr:%{fff} -all", PermError, ErrInvalidMacro}, + {"v=spf1 mx:%{fff} -all", PermError, ErrInvalidMacro}, + {"v=spf1 redirect=%{fff}", PermError, ErrInvalidMacro}, + {"v=spf1 a:%{o0}", PermError, ErrInvalidMacro}, + {"v=spf1 +a:sss-%{s}-sss", Pass, ErrMatchedA}, + {"v=spf1 +a:ooo-%{o}-ooo", Pass, ErrMatchedA}, + {"v=spf1 +a:OOO-%{O}-OOO", Pass, ErrMatchedA}, + {"v=spf1 +a:ppp-%{p}-ppp", Pass, ErrMatchedA}, + {"v=spf1 +a:vvv-%{v}-vvv", Pass, ErrMatchedA}, + {"v=spf1 a:%{x}", PermError, ErrInvalidMacro}, + {"v=spf1 +a:ooo-%{o7}-ooo", Pass, ErrMatchedA}, + {"v=spf1 exists:%{ir}.vvv -all", Pass, ErrMatchedExists}, } dns.Ip["sss-user@domain-sss"] = []net.IP{ip6666} @@ -386,12 +386,12 @@ func TestMacrosV4(t *testing.T) { res Result err error }{ - {"v=spf1 +a:sr-%{sr}-sr", Pass, errMatchedA}, - {"v=spf1 +a:sra-%{sr.}-sra", Pass, errMatchedA}, - {"v=spf1 +a:o7-%{o7}-o7", Pass, errMatchedA}, - {"v=spf1 +a:o1-%{o1}-o1", Pass, errMatchedA}, - {"v=spf1 +a:o1r-%{o1r}-o1r", Pass, errMatchedA}, - {"v=spf1 +a:vvv-%{v}-vvv", Pass, errMatchedA}, + {"v=spf1 +a:sr-%{sr}-sr", Pass, ErrMatchedA}, + {"v=spf1 +a:sra-%{sr.}-sra", Pass, ErrMatchedA}, + {"v=spf1 +a:o7-%{o7}-o7", Pass, ErrMatchedA}, + {"v=spf1 +a:o1-%{o1}-o1", Pass, ErrMatchedA}, + {"v=spf1 +a:o1r-%{o1r}-o1r", Pass, ErrMatchedA}, + {"v=spf1 +a:vvv-%{v}-vvv", Pass, ErrMatchedA}, } dns.Ip["sr-com.user@domain-sr"] = []net.IP{ip1111} @@ -466,9 +466,9 @@ func TestInvalidMacro(t *testing.T) { } out, err := r.expandMacros(macro, "sender.com") - if out != "" || err != errInvalidMacro { + if out != "" || err != ErrInvalidMacro { t.Errorf(`[%s]:expected ""/%v, got %q/%v`, - macro, errInvalidMacro, out, err) + macro, ErrInvalidMacro, out, err) } } } @@ -514,7 +514,7 @@ func TestOverrideLookupLimit(t *testing.T) { // Set the limit to 3, which is not enough. res, err = CheckHostWithSender(ip1111, "helo", "user@domain1", OverrideLookupLimit(3)) - if res != PermError || err != errLookupLimitReached { + if res != PermError || err != ErrLookupLimitReached { t.Errorf("expected permerror/lookup limit reached, got %q / %q", res, err) }