coppertone 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/.travis.yml +7 -0
- data/Gemfile +7 -0
- data/LICENSE +201 -0
- data/README.md +58 -0
- data/Rakefile +140 -0
- data/coppertone.gemspec +27 -0
- data/lib/coppertone/class_builder.rb +20 -0
- data/lib/coppertone/directive.rb +38 -0
- data/lib/coppertone/dns/error.rb +9 -0
- data/lib/coppertone/dns/mock_client.rb +106 -0
- data/lib/coppertone/dns/resolv_client.rb +110 -0
- data/lib/coppertone/dns.rb +3 -0
- data/lib/coppertone/domain_spec.rb +45 -0
- data/lib/coppertone/error.rb +29 -0
- data/lib/coppertone/ip_address_wrapper.rb +75 -0
- data/lib/coppertone/macro_context.rb +67 -0
- data/lib/coppertone/macro_string/macro_expand.rb +84 -0
- data/lib/coppertone/macro_string/macro_literal.rb +24 -0
- data/lib/coppertone/macro_string/macro_parser.rb +62 -0
- data/lib/coppertone/macro_string/macro_static_expand.rb +52 -0
- data/lib/coppertone/macro_string.rb +31 -0
- data/lib/coppertone/mechanism/a.rb +16 -0
- data/lib/coppertone/mechanism/all.rb +24 -0
- data/lib/coppertone/mechanism/cidr_parser.rb +14 -0
- data/lib/coppertone/mechanism/domain_spec_mechanism.rb +18 -0
- data/lib/coppertone/mechanism/domain_spec_optional.rb +46 -0
- data/lib/coppertone/mechanism/domain_spec_required.rb +37 -0
- data/lib/coppertone/mechanism/domain_spec_with_dual_cidr.rb +114 -0
- data/lib/coppertone/mechanism/exists.rb +14 -0
- data/lib/coppertone/mechanism/include.rb +18 -0
- data/lib/coppertone/mechanism/include_matcher.rb +34 -0
- data/lib/coppertone/mechanism/ip4.rb +13 -0
- data/lib/coppertone/mechanism/ip6.rb +13 -0
- data/lib/coppertone/mechanism/ip_mechanism.rb +48 -0
- data/lib/coppertone/mechanism/mx.rb +40 -0
- data/lib/coppertone/mechanism/ptr.rb +17 -0
- data/lib/coppertone/mechanism.rb +32 -0
- data/lib/coppertone/modifier/base.rb +24 -0
- data/lib/coppertone/modifier/exp.rb +34 -0
- data/lib/coppertone/modifier/redirect.rb +17 -0
- data/lib/coppertone/modifier/unknown.rb +16 -0
- data/lib/coppertone/modifier.rb +30 -0
- data/lib/coppertone/qualifier.rb +45 -0
- data/lib/coppertone/record.rb +86 -0
- data/lib/coppertone/record_evaluator.rb +63 -0
- data/lib/coppertone/record_finder.rb +34 -0
- data/lib/coppertone/request.rb +68 -0
- data/lib/coppertone/request_context.rb +67 -0
- data/lib/coppertone/request_count_limiter.rb +36 -0
- data/lib/coppertone/result.rb +50 -0
- data/lib/coppertone/sender_identity.rb +39 -0
- data/lib/coppertone/spf_service.rb +9 -0
- data/lib/coppertone/term.rb +13 -0
- data/lib/coppertone/utils/domain_utils.rb +59 -0
- data/lib/coppertone/utils/host_utils.rb +22 -0
- data/lib/coppertone/utils/ip_in_domain_checker.rb +53 -0
- data/lib/coppertone/utils/validated_domain_finder.rb +40 -0
- data/lib/coppertone/utils.rb +4 -0
- data/lib/coppertone/version.rb +3 -0
- data/lib/coppertone.rb +48 -0
- data/lib/resolv/dns/resource/in/spf.rb +15 -0
- data/spec/directive_spec.rb +41 -0
- data/spec/dns/resolv_client_spec.rb +307 -0
- data/spec/domain_spec_spec.rb +35 -0
- data/spec/ip_address_wrapper_spec.rb +67 -0
- data/spec/macro_context_spec.rb +69 -0
- data/spec/macro_string/macro_expand_spec.rb +79 -0
- data/spec/macro_string/macro_literal_spec.rb +27 -0
- data/spec/macro_string/macro_static_expand_spec.rb +67 -0
- data/spec/macro_string_spec.rb +20 -0
- data/spec/mechanism/a_spec.rb +198 -0
- data/spec/mechanism/all_spec.rb +22 -0
- data/spec/mechanism/exists_spec.rb +91 -0
- data/spec/mechanism/include_spec.rb +43 -0
- data/spec/mechanism/ip4_spec.rb +110 -0
- data/spec/mechanism/ip6_spec.rb +104 -0
- data/spec/mechanism/mx_spec.rb +51 -0
- data/spec/mechanism/ptr_spec.rb +43 -0
- data/spec/mechanism_spec.rb +4 -0
- data/spec/modifier_spec.rb +4 -0
- data/spec/open_spf/ALL_mechanism_syntax_spec.rb +38 -0
- data/spec/open_spf/A_mechanism_syntax_spec.rb +159 -0
- data/spec/open_spf/EXISTS_mechanism_syntax_spec.rb +46 -0
- data/spec/open_spf/IP4_mechanism_syntax_spec.rb +59 -0
- data/spec/open_spf/IP6_mechanism_syntax_spec.rb +60 -0
- data/spec/open_spf/Include_mechanism_semantics_and_syntax_spec.rb +56 -0
- data/spec/open_spf/Initial_processing_spec.rb +77 -0
- data/spec/open_spf/MX_mechanism_syntax_spec.rb +119 -0
- data/spec/open_spf/Macro_expansion_rules_spec.rb +154 -0
- data/spec/open_spf/PTR_mechanism_syntax_spec.rb +42 -0
- data/spec/open_spf/Processing_limits_spec.rb +72 -0
- data/spec/open_spf/Record_evaluation_spec.rb +75 -0
- data/spec/open_spf/Record_lookup_spec.rb +48 -0
- data/spec/open_spf/Selecting_records_spec.rb +61 -0
- data/spec/open_spf/Semantics_of_exp_and_other_modifiers_spec.rb +167 -0
- data/spec/open_spf/Test_cases_from_implementation_bugs_spec.rb +17 -0
- data/spec/qualifier_spec.rb +54 -0
- data/spec/record_evaluator_spec.rb +4 -0
- data/spec/record_finder_spec.rb +4 -0
- data/spec/record_spec.rb +100 -0
- data/spec/request_context_spec.rb +43 -0
- data/spec/request_count_limiter_spec.rb +28 -0
- data/spec/result_spec.rb +4 -0
- data/spec/rfc7208-tests.yml +2548 -0
- data/spec/sender_identity_spec.rb +69 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/term_spec.rb +38 -0
- data/spec/utils/domain_utils_spec.rb +60 -0
- data/spec/utils/host_utils_spec.rb +32 -0
- data/spec/utils/ip_in_domain_checker_spec.rb +4 -0
- data/spec/utils/validated_domain_finder_spec.rb +4 -0
- metadata +306 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Coppertone::Mechanism::MX do
|
|
4
|
+
context '#new' do
|
|
5
|
+
it 'should not fail if called with a nil argument' do
|
|
6
|
+
mech = Coppertone::Mechanism::MX.new(nil)
|
|
7
|
+
expect(mech).not_to be_nil
|
|
8
|
+
expect(mech.domain_spec).to be_nil
|
|
9
|
+
expect(mech.ip_v4_cidr_length).to eq(32)
|
|
10
|
+
expect(mech.ip_v6_cidr_length).to eq(128)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'should not fail if called with a blank argument' do
|
|
14
|
+
mech = Coppertone::Mechanism::MX.new('')
|
|
15
|
+
expect(mech).not_to be_nil
|
|
16
|
+
expect(mech.domain_spec).to be_nil
|
|
17
|
+
expect(mech.ip_v4_cidr_length).to eq(32)
|
|
18
|
+
expect(mech.ip_v6_cidr_length).to eq(128)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'should fail if called with an invalid macrostring' do
|
|
22
|
+
expect do
|
|
23
|
+
Coppertone::Mechanism::MX.new('abc%:def')
|
|
24
|
+
end.to raise_error(Coppertone::InvalidMechanismError)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context '#create' do
|
|
29
|
+
it 'should fail if called with a nil argument' do
|
|
30
|
+
mech = Coppertone::Mechanism::MX.create(nil)
|
|
31
|
+
expect(mech).not_to be_nil
|
|
32
|
+
expect(mech.domain_spec).to be_nil
|
|
33
|
+
expect(mech.ip_v4_cidr_length).to eq(32)
|
|
34
|
+
expect(mech.ip_v6_cidr_length).to eq(128)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'should fail if called with a blank argument' do
|
|
38
|
+
mech = Coppertone::Mechanism::MX.create('')
|
|
39
|
+
expect(mech).not_to be_nil
|
|
40
|
+
expect(mech.domain_spec).to be_nil
|
|
41
|
+
expect(mech.ip_v4_cidr_length).to eq(32)
|
|
42
|
+
expect(mech.ip_v6_cidr_length).to eq(128)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'should fail if called with an argument invalid macrostring' do
|
|
46
|
+
expect do
|
|
47
|
+
Coppertone::Mechanism::MX.create('abc%:def')
|
|
48
|
+
end.to raise_error(Coppertone::InvalidMechanismError)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Coppertone::Mechanism::Ptr do
|
|
4
|
+
context '#new' do
|
|
5
|
+
it 'should not fail if called with a nil argument' do
|
|
6
|
+
mech = Coppertone::Mechanism::Ptr.new(nil)
|
|
7
|
+
expect(mech).not_to be_nil
|
|
8
|
+
expect(mech.domain_spec).to be_nil
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'should not fail if called with a blank argument' do
|
|
12
|
+
mech = Coppertone::Mechanism::Ptr.new('')
|
|
13
|
+
expect(mech).not_to be_nil
|
|
14
|
+
expect(mech.domain_spec).to be_nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should fail if called with an invalid macrostring' do
|
|
18
|
+
expect do
|
|
19
|
+
Coppertone::Mechanism::Ptr.new('abc%:def')
|
|
20
|
+
end.to raise_error(Coppertone::InvalidMechanismError)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
context '#create' do
|
|
25
|
+
it 'should fail if called with a nil argument' do
|
|
26
|
+
mech = Coppertone::Mechanism::Ptr.create(nil)
|
|
27
|
+
expect(mech).not_to be_nil
|
|
28
|
+
expect(mech.domain_spec).to be_nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'should fail if called with a blank argument' do
|
|
32
|
+
mech = Coppertone::Mechanism::Ptr.create('')
|
|
33
|
+
expect(mech).not_to be_nil
|
|
34
|
+
expect(mech.domain_spec).to be_nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'should fail if called with an argument invalid macrostring' do
|
|
38
|
+
expect do
|
|
39
|
+
Coppertone::Mechanism::Ptr.create('abc%:def')
|
|
40
|
+
end.to raise_error(Coppertone::InvalidMechanismError)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'ALL mechanism syntax' do
|
|
4
|
+
let(:zonefile) do
|
|
5
|
+
{ 'mail.example.com' => [{ 'A' => '1.2.3.4' }], 'e1.example.com' => [{ 'TXT' => 'v=spf1 -all.' }], 'e2.example.com' => [{ 'TXT' => 'v=spf1 -all:foobar' }], 'e3.example.com' => [{ 'TXT' => 'v=spf1 -all/8' }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 ?all' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 all -all' }] }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:dns_client) { Coppertone::DNS::MockClient.new(zonefile) }
|
|
9
|
+
let(:options) { { dns_client: dns_client } }
|
|
10
|
+
|
|
11
|
+
it 'all = "all" ' do
|
|
12
|
+
# At least one implementation got this wrong
|
|
13
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
|
14
|
+
expect(%i(permerror)).to include(result.code)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'all = "all" ' do
|
|
18
|
+
# At least one implementation got this wrong
|
|
19
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
|
20
|
+
expect(%i(permerror)).to include(result.code)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'all = "all" ' do
|
|
24
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
|
|
25
|
+
expect(%i(permerror)).to include(result.code)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'all = "all" ' do
|
|
29
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
|
30
|
+
expect(%i(neutral)).to include(result.code)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'all = "all" ' do
|
|
34
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
|
|
35
|
+
expect(%i(pass)).to include(result.code)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'A mechanism syntax' do
|
|
4
|
+
let(:zonefile) do
|
|
5
|
+
{ 'mail.example.com' => [{ 'A' => '1.2.3.4' }], 'e1.example.com' => [{ 'TXT' => 'v=spf1 a/0 -all' }], 'e2.example.com' => [{ 'A' => '1.1.1.1' }, { 'AAAA' => '1234::2' }, { 'TXT' => 'v=spf1 a/0 -all' }], 'e2a.example.com' => [{ 'AAAA' => '1234::1' }, { 'TXT' => 'v=spf1 a//0 -all' }], 'e2b.example.com' => [{ 'A' => '1.1.1.1' }, { 'TXT' => 'v=spf1 a//0 -all' }], 'ipv6.example.com' => [{ 'AAAA' => '1234::1' }, { 'A' => '1.1.1.1' }, { 'TXT' => 'v=spf1 a -all' }], 'e3.example.com' => [{ 'TXT' => "v=spf1 a:foo.example.com\u0000" }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 a:111.222.33.44' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 a:abc.123' }], 'e5a.example.com' => [{ 'TXT' => 'v=spf1 a:museum' }], 'e5b.example.com' => [{ 'TXT' => 'v=spf1 a:museum.' }], 'e6.example.com' => [{ 'TXT' => 'v=spf1 a//33 -all' }], 'e6a.example.com' => [{ 'TXT' => 'v=spf1 a/33 -all' }], 'e7.example.com' => [{ 'TXT' => 'v=spf1 a//129 -all' }], 'e8.example.com' => [{ 'A' => '1.2.3.5' }, { 'AAAA' => '2001:db8:1234::dead:beef' }, { 'TXT' => 'v=spf1 a/24//64 -all' }], 'e8e.example.com' => [{ 'A' => '1.2.3.5' }, { 'AAAA' => '2001:db8:1234::dead:beef' }, { 'TXT' => 'v=spf1 a/24/64 -all' }], 'e8a.example.com' => [{ 'A' => '1.2.3.5' }, { 'AAAA' => '2001:db8:1234::dead:beef' }, { 'TXT' => 'v=spf1 a/24 -all' }], 'e8b.example.com' => [{ 'A' => '1.2.3.5' }, { 'AAAA' => '2001:db8:1234::dead:beef' }, { 'TXT' => 'v=spf1 a//64 -all' }], 'e9.example.com' => [{ 'TXT' => 'v=spf1 a:example.com:8080' }], 'e10.example.com' => [{ 'TXT' => 'v=spf1 a:foo.example.com/24' }], 'foo.example.com' => [{ 'A' => '1.1.1.1' }, { 'A' => '1.2.3.5' }], 'e11.example.com' => [{ 'TXT' => 'v=spf1 a:foo:bar/baz.example.com' }], 'foo:bar/baz.example.com' => [{ 'A' => '1.2.3.4' }], 'e12.example.com' => [{ 'TXT' => 'v=spf1 a:example.-com' }], 'e13.example.com' => [{ 'TXT' => 'v=spf1 a:' }], 'e14.example.com' => [{ 'TXT' => 'v=spf1 a:foo.example.xn--zckzah -all' }], 'foo.example.xn--zckzah' => [{ 'A' => '1.2.3.4' }] }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:dns_client) { Coppertone::DNS::MockClient.new(zonefile) }
|
|
9
|
+
let(:options) { { dns_client: dns_client } }
|
|
10
|
+
|
|
11
|
+
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
|
12
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6.example.com', 'mail.example.com', options)
|
|
13
|
+
expect(%i(fail)).to include(result.code)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
|
17
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6a.example.com', 'mail.example.com', options)
|
|
18
|
+
expect(%i(permerror)).to include(result.code)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
|
22
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e7.example.com', 'mail.example.com', options)
|
|
23
|
+
expect(%i(permerror)).to include(result.code)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
|
27
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8.example.com', 'mail.example.com', options)
|
|
28
|
+
expect(%i(pass)).to include(result.code)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
|
32
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8e.example.com', 'mail.example.com', options)
|
|
33
|
+
expect(%i(permerror)).to include(result.code)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
|
37
|
+
result = Coppertone::SpfService.authenticate_email('2001:db8:1234::cafe:babe', 'foo@e8.example.com', 'mail.example.com', options)
|
|
38
|
+
expect(%i(pass)).to include(result.code)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
|
42
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8b.example.com', 'mail.example.com', options)
|
|
43
|
+
expect(%i(fail)).to include(result.code)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
|
47
|
+
result = Coppertone::SpfService.authenticate_email('2001:db8:1234::cafe:babe', 'foo@e8a.example.com', 'mail.example.com', options)
|
|
48
|
+
expect(%i(fail)).to include(result.code)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'A matches any returned IP.' do
|
|
52
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e10.example.com', 'mail.example.com', options)
|
|
53
|
+
expect(%i(pass)).to include(result.code)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'A matches any returned IP.' do
|
|
57
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e10.example.com', 'mail.example.com', options)
|
|
58
|
+
expect(%i(pass)).to include(result.code)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'domain-spec must pass basic syntax checks; a : may appear in domain-spec, but not in top-label' do
|
|
62
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e9.example.com', 'mail.example.com', options)
|
|
63
|
+
expect(%i(permerror)).to include(result.code)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'If no ips are returned, A mechanism does not match, even with /0.' do
|
|
67
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
|
68
|
+
expect(%i(fail)).to include(result.code)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'Matches if any A records are present in DNS.' do
|
|
72
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
|
73
|
+
expect(%i(pass)).to include(result.code)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'Matches if any A records are present in DNS.' do
|
|
77
|
+
result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@e2.example.com', 'mail.example.com', options)
|
|
78
|
+
expect(%i(fail)).to include(result.code)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'Would match if any AAAA records are present in DNS, but not for an IP4 connection.' do
|
|
82
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2a.example.com', 'mail.example.com', options)
|
|
83
|
+
expect(%i(fail)).to include(result.code)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'Would match if any AAAA records are present in DNS, but not for an IP4 connection.' do
|
|
87
|
+
result = Coppertone::SpfService.authenticate_email('::FFFF:1.2.3.4', 'foo@e2a.example.com', 'mail.example.com', options)
|
|
88
|
+
expect(%i(fail)).to include(result.code)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'Matches if any AAAA records are present in DNS.' do
|
|
92
|
+
result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@e2a.example.com', 'mail.example.com', options)
|
|
93
|
+
expect(%i(pass)).to include(result.code)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'Simple IP6 Address match with dual stack.' do
|
|
97
|
+
result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@ipv6.example.com', 'mail.example.com', options)
|
|
98
|
+
expect(%i(pass)).to include(result.code)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'No match if no AAAA records are present in DNS.' do
|
|
102
|
+
result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@e2b.example.com', 'mail.example.com', options)
|
|
103
|
+
expect(%i(fail)).to include(result.code)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'Null octets not allowed in toplabel' do
|
|
107
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'foo@e3.example.com', 'mail.example.com', options)
|
|
108
|
+
expect(%i(permerror)).to include(result.code)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'toplabel may not be all numeric' do
|
|
112
|
+
# A common publishing mistake is using ip4 addresses with A mechanism. This should receive special diagnostic attention in the permerror.
|
|
113
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
|
114
|
+
expect(%i(permerror)).to include(result.code)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'toplabel may not be all numeric' do
|
|
118
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
|
|
119
|
+
expect(%i(permerror)).to include(result.code)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'toplabel may contain dashes' do
|
|
123
|
+
# Going from the "toplabel" grammar definition, an implementation using regular expressions in incrementally parsing SPF records might erroneously try to match a TLD such as ".xn--zckzah" (cf. IDN TLDs!) to \'( *alphanum ALPHA *alphanum )\' first before trying the alternative \'( 1*alphanum "-" *( alphanum / "-" ) alphanum )\', essentially causing a non-greedy, and thus, incomplete match. Make sure a greedy match is performed!
|
|
124
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e14.example.com', 'mail.example.com', options)
|
|
125
|
+
expect(%i(pass)).to include(result.code)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'toplabel may not begin with a dash' do
|
|
129
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e12.example.com', 'mail.example.com', options)
|
|
130
|
+
expect(%i(permerror)).to include(result.code)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'domain-spec may not consist of only a toplabel.' do
|
|
134
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5a.example.com', 'mail.example.com', options)
|
|
135
|
+
expect(%i(permerror)).to include(result.code)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'domain-spec may not consist of only a toplabel.' do
|
|
139
|
+
# "A trailing dot doesn\'t help."
|
|
140
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5b.example.com', 'mail.example.com', options)
|
|
141
|
+
expect(%i(permerror)).to include(result.code)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it 'domain-spec may contain any visible char except %' do
|
|
145
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e11.example.com', 'mail.example.com', options)
|
|
146
|
+
expect(%i(pass)).to include(result.code)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it 'domain-spec may contain any visible char except %' do
|
|
150
|
+
result = Coppertone::SpfService.authenticate_email('::FFFF:1.2.3.4', 'foo@e11.example.com', 'mail.example.com', options)
|
|
151
|
+
expect(%i(pass)).to include(result.code)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'domain-spec cannot be empty.' do
|
|
155
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e13.example.com', 'mail.example.com', options)
|
|
156
|
+
expect(%i(permerror)).to include(result.code)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'EXISTS mechanism syntax' do
|
|
4
|
+
let(:zonefile) do
|
|
5
|
+
{ 'mail.example.com' => [{ 'A' => '1.2.3.4' }], 'mail6.example.com' => [{ 'AAAA' => 'CAFE:BABE::4' }], 'err.example.com' => ['TIMEOUT'], 'e1.example.com' => [{ 'TXT' => 'v=spf1 exists:' }], 'e2.example.com' => [{ 'TXT' => 'v=spf1 exists' }], 'e3.example.com' => [{ 'TXT' => 'v=spf1 exists:mail.example.com/24' }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 exists:mail.example.com' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 exists:mail6.example.com -all' }], 'e6.example.com' => [{ 'TXT' => 'v=spf1 exists:err.example.com -all' }] }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:dns_client) { Coppertone::DNS::MockClient.new(zonefile) }
|
|
9
|
+
let(:options) { { dns_client: dns_client } }
|
|
10
|
+
|
|
11
|
+
it 'domain-spec cannot be empty.' do
|
|
12
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
|
13
|
+
expect(%i(permerror)).to include(result.code)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'exists = "exists" ":" domain-spec' do
|
|
17
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
|
18
|
+
expect(%i(permerror)).to include(result.code)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'exists = "exists" ":" domain-spec' do
|
|
22
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
|
|
23
|
+
expect(%i(permerror)).to include(result.code)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'mechanism matches if any DNS A RR exists' do
|
|
27
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
|
28
|
+
expect(%i(pass)).to include(result.code)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'The lookup type is A even when the connection is ip6' do
|
|
32
|
+
result = Coppertone::SpfService.authenticate_email('CAFE:BABE::3', 'foo@e4.example.com', 'mail.example.com', options)
|
|
33
|
+
expect(%i(pass)).to include(result.code)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'The lookup type is A even when the connection is ip6' do
|
|
37
|
+
result = Coppertone::SpfService.authenticate_email('CAFE:BABE::3', 'foo@e5.example.com', 'mail.example.com', options)
|
|
38
|
+
expect(%i(fail)).to include(result.code)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'Result for DNS error clarified in RFC7208: MTAs or other processors SHOULD impose a limit on the maximum amount of elapsed time to evaluate check_host(). Such a limit SHOULD allow at least 20 seconds. If such a limit is exceeded, the result of authorization SHOULD be "temperror".' do
|
|
42
|
+
result = Coppertone::SpfService.authenticate_email('CAFE:BABE::3', 'foo@e6.example.com', 'mail.example.com', options)
|
|
43
|
+
expect(%i(temperror)).to include(result.code)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'IP4 mechanism syntax' do
|
|
4
|
+
let(:zonefile) do
|
|
5
|
+
{ 'mail.example.com' => [{ 'A' => '1.2.3.4' }], 'e1.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.1.1.1/0 -all' }], 'e2.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.4/32 -all' }], 'e3.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.4/33 -all' }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.4/032 -all' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 ip4' }], 'e6.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.4//32' }], 'e7.example.com' => [{ 'TXT' => 'v=spf1 -ip4:1.2.3.4 ip6:::FFFF:1.2.3.4' }], 'e8.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.4:8080' }], 'e9.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3' }] }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:dns_client) { Coppertone::DNS::MockClient.new(zonefile) }
|
|
9
|
+
let(:options) { { dns_client: dns_client } }
|
|
10
|
+
|
|
11
|
+
it 'ip4-cidr-length = "/" 1*DIGIT' do
|
|
12
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
|
13
|
+
expect(%i(pass)).to include(result.code)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'ip4-cidr-length = "/" 1*DIGIT' do
|
|
17
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
|
18
|
+
expect(%i(pass)).to include(result.code)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'Invalid CIDR should get permerror.' do
|
|
22
|
+
# The RFC4408 was silent on ip4 CIDR > 32 or ip6 CIDR > 128, but RFC7208 is explicit. Invalid CIDR is prohibited.
|
|
23
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
|
|
24
|
+
expect(%i(permerror)).to include(result.code)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'Invalid CIDR should get permerror.' do
|
|
28
|
+
# Leading zeros are not explicitly prohibited by the RFC. However, since the RFC explicity prohibits leading zeros in ip4-network, our interpretation is that CIDR should be also.
|
|
29
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
|
30
|
+
expect(%i(permerror)).to include(result.code)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'IP4 = "ip4" ":" ip4-network [ ip4-cidr-length ]' do
|
|
34
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
|
|
35
|
+
expect(%i(permerror)).to include(result.code)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'IP4 = "ip4" ":" ip4-network [ ip4-cidr-length ]' do
|
|
39
|
+
# This has actually been published in SPF records.
|
|
40
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8.example.com', 'mail.example.com', options)
|
|
41
|
+
expect(%i(permerror)).to include(result.code)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'It is not permitted to omit parts of the IP address instead of using CIDR notations.' do
|
|
45
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e9.example.com', 'mail.example.com', options)
|
|
46
|
+
expect(%i(permerror)).to include(result.code)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'dual-cidr-length not permitted on ip4' do
|
|
50
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6.example.com', 'mail.example.com', options)
|
|
51
|
+
expect(%i(permerror)).to include(result.code)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'IP4 mapped IP6 connections MUST be treated as IP4' do
|
|
55
|
+
result = Coppertone::SpfService.authenticate_email('::FFFF:1.2.3.4', 'foo@e7.example.com', 'mail.example.com', options)
|
|
56
|
+
expect(%i(fail)).to include(result.code)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'IP6 mechanism syntax' do
|
|
4
|
+
let(:zonefile) do
|
|
5
|
+
{ 'mail.example.com' => [{ 'A' => '1.2.3.4' }], 'e1.example.com' => [{ 'TXT' => 'v=spf1 -all ip6' }], 'e2.example.com' => [{ 'TXT' => 'v=spf1 ip6:::1.1.1.1/0' }], 'e3.example.com' => [{ 'TXT' => 'v=spf1 ip6:::1.1.1.1/129' }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 ip6:::1.1.1.1//33' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 ip6:CAFE:BABE:8000::/33' }], 'e6.example.com' => [{ 'TXT' => 'v=spf1 ip6::CAFE::BABE' }] }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:dns_client) { Coppertone::DNS::MockClient.new(zonefile) }
|
|
9
|
+
let(:options) { { dns_client: dns_client } }
|
|
10
|
+
|
|
11
|
+
it 'IP6 = "ip6" ":" ip6-network [ ip6-cidr-length ]' do
|
|
12
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
|
13
|
+
expect(%i(permerror)).to include(result.code)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'IP4 connections do not match ip6.' do
|
|
17
|
+
# There was controversy over IPv4 mapped connections. RFC7208 clearly states IPv4 mapped addresses only match ip4: mechanisms.
|
|
18
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
|
19
|
+
expect(%i(neutral)).to include(result.code)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'Even if the SMTP connection is via IPv6, an IPv4-mapped IPv6 IP address (see RFC 3513, Section 2.5.5) MUST still be considered an IPv4 address.' do
|
|
23
|
+
# There was controversy over ip4 mapped connections. RFC7208 clearly requires such connections to be considered as ip4 only.
|
|
24
|
+
result = Coppertone::SpfService.authenticate_email('::FFFF:1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
|
25
|
+
expect(%i(neutral)).to include(result.code)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'Match any IP6' do
|
|
29
|
+
result = Coppertone::SpfService.authenticate_email('DEAF:BABE::CAB:FEE', 'foo@e2.example.com', 'mail.example.com', options)
|
|
30
|
+
expect(%i(pass)).to include(result.code)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'Invalid CIDR' do
|
|
34
|
+
# IP4 only implementations MUST fully syntax check all mechanisms, even if they otherwise ignore them.
|
|
35
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
|
|
36
|
+
expect(%i(permerror)).to include(result.code)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'dual-cidr syntax not used for ip6' do
|
|
40
|
+
# IP4 only implementations MUST fully syntax check all mechanisms, even if they otherwise ignore them.
|
|
41
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
|
42
|
+
expect(%i(permerror)).to include(result.code)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'make sure ip4 cidr restriction are not used for ip6' do
|
|
46
|
+
result = Coppertone::SpfService.authenticate_email('CAFE:BABE:8000::', 'foo@e5.example.com', 'mail.example.com', options)
|
|
47
|
+
expect(%i(pass)).to include(result.code)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'make sure ip4 cidr restriction are not used for ip6' do
|
|
51
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
|
|
52
|
+
expect(%i(neutral)).to include(result.code)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it '' do
|
|
56
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6.example.com', 'mail.example.com', options)
|
|
57
|
+
expect(%i(permerror)).to include(result.code)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Include mechanism semantics and syntax' do
|
|
4
|
+
let(:zonefile) do
|
|
5
|
+
{ 'mail.example.com' => [{ 'A' => '1.2.3.4' }], 'ip5.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.5 -all' }], 'ip6.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.6 ~all' }], 'ip7.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.7 ?all' }], 'ip8.example.com' => ['TIMEOUT'], 'erehwon.example.com' => [{ 'TXT' => 'v=spfl am not an SPF record' }], 'e1.example.com' => [{ 'TXT' => 'v=spf1 include:ip5.example.com ~all' }], 'e2.example.com' => [{ 'TXT' => 'v=spf1 include:ip6.example.com all' }], 'e3.example.com' => [{ 'TXT' => 'v=spf1 include:ip7.example.com -all' }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 include:ip8.example.com -all' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 include:e6.example.com -all' }], 'e6.example.com' => [{ 'TXT' => 'v=spf1 include +all' }], 'e7.example.com' => [{ 'TXT' => 'v=spf1 include:erehwon.example.com -all' }], 'e8.example.com' => [{ 'TXT' => 'v=spf1 include: -all' }], 'e9.example.com' => [{ 'TXT' => 'v=spf1 include:ip5.example.com/24 -all' }] }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:dns_client) { Coppertone::DNS::MockClient.new(zonefile) }
|
|
9
|
+
let(:options) { { dns_client: dns_client } }
|
|
10
|
+
|
|
11
|
+
it 'recursive check_host() result of fail causes include to not match.' do
|
|
12
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
|
13
|
+
expect(%i(softfail)).to include(result.code)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'recursive check_host() result of softfail causes include to not match.' do
|
|
17
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
|
18
|
+
expect(%i(pass)).to include(result.code)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'recursive check_host() result of neutral causes include to not match.' do
|
|
22
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
|
|
23
|
+
expect(%i(fail)).to include(result.code)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'recursive check_host() result of temperror causes include to temperror' do
|
|
27
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
|
28
|
+
expect(%i(temperror)).to include(result.code)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'recursive check_host() result of permerror causes include to permerror' do
|
|
32
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
|
|
33
|
+
expect(%i(permerror)).to include(result.code)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'include = "include" ":" domain-spec' do
|
|
37
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6.example.com', 'mail.example.com', options)
|
|
38
|
+
expect(%i(permerror)).to include(result.code)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'include = "include" ":" domain-spec' do
|
|
42
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e9.example.com', 'mail.example.com', options)
|
|
43
|
+
expect(%i(permerror)).to include(result.code)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'recursive check_host() result of none causes include to permerror' do
|
|
47
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e7.example.com', 'mail.example.com', options)
|
|
48
|
+
expect(%i(permerror)).to include(result.code)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'domain-spec cannot be empty.' do
|
|
52
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8.example.com', 'mail.example.com', options)
|
|
53
|
+
expect(%i(permerror)).to include(result.code)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Initial processing' do
|
|
4
|
+
let(:zonefile) do
|
|
5
|
+
{ 'example.com' => ['TIMEOUT'], 'example.net' => [{ 'TXT' => 'v=spf1 -all exp=exp.example.net' }], 'a.example.net' => [{ 'TXT' => 'v=spf1 -all exp=exp.example.net' }], 'exp.example.net' => [{ 'TXT' => '%{l}' }], 'a12345678901234567890123456789012345678901234567890123456789012.example.com' => [{ 'TXT' => 'v=spf1 -all' }], 'hosed.example.com' => [{ 'TXT' => 'v=spf1 a:garbage.example.net -all' }], 'hosed2.example.com' => [{ 'TXT' => "v=spf1 \u0080a:example.net -all" }], 'hosed3.example.com' => [{ 'TXT' => "v=spf1 a:example.net \u0096all" }], 'nothosed.example.com' => [{ 'TXT' => 'v=spf1 a:example.net -all' }, { 'TXT' => "\u0096" }], 'ctrl.example.com' => [{ 'TXT' => "v=spf1 a:ctrl.example.com\rptr -all" }, { 'A' => '192.0.2.3' }] }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:dns_client) { Coppertone::DNS::MockClient.new(zonefile) }
|
|
9
|
+
let(:options) { { dns_client: dns_client } }
|
|
10
|
+
|
|
11
|
+
it 'DNS labels limited to 63 chars.' do
|
|
12
|
+
# For initial processing, a long label results in None, not TempError
|
|
13
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'lyme.eater@A123456789012345678901234567890123456789012345678901234567890123.example.com', 'mail.example.net', options)
|
|
14
|
+
expect(%i(none)).to include(result.code)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'DNS labels limited to 63 chars.' do
|
|
18
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'lyme.eater@A12345678901234567890123456789012345678901234567890123456789012.example.com', 'mail.example.net', options)
|
|
19
|
+
expect(%i(fail)).to include(result.code)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'emptylabel' do
|
|
23
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'lyme.eater@A...example.com', 'mail.example.net', options)
|
|
24
|
+
expect(%i(none)).to include(result.code)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'helo-not-fqdn' do
|
|
28
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.5', '', 'A2345678', options)
|
|
29
|
+
expect(%i(none)).to include(result.code)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'helo-domain-literal' do
|
|
33
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.5', '', '[1.2.3.5]', options)
|
|
34
|
+
expect(%i(none)).to include(result.code)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'nolocalpart' do
|
|
38
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', '@example.net', 'mail.example.net', options)
|
|
39
|
+
expect(%i(fail)).to include(result.code)
|
|
40
|
+
expect(result.explanation).to eq('postmaster')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'domain-literal' do
|
|
44
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'foo@[1.2.3.5]', 'OEMCOMPUTER', options)
|
|
45
|
+
expect(%i(none)).to include(result.code)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'SPF policies are restricted to 7-bit ascii.' do
|
|
49
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foobar@hosed.example.com', 'hosed', options)
|
|
50
|
+
expect(%i(permerror)).to include(result.code)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'SPF policies are restricted to 7-bit ascii.' do
|
|
54
|
+
# Checking a possibly different code path for non-ascii chars.
|
|
55
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foobar@hosed2.example.com', 'hosed', options)
|
|
56
|
+
expect(%i(permerror)).to include(result.code)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'SPF policies are restricted to 7-bit ascii.' do
|
|
60
|
+
# Checking yet another code path for non-ascii chars.
|
|
61
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foobar@hosed3.example.com', 'hosed', options)
|
|
62
|
+
expect(%i(permerror)).to include(result.code)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'Non-ascii content in non-SPF related records.' do
|
|
66
|
+
# Non-SPF related TXT records are none of our business.
|
|
67
|
+
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foobar@nothosed.example.com', 'hosed', options)
|
|
68
|
+
expect(%i(fail)).to include(result.code)
|
|
69
|
+
expect(result.explanation).to eq('DEFAULT')
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'Mechanisms are separated by spaces only, not any control char.' do
|
|
73
|
+
result = Coppertone::SpfService.authenticate_email('192.0.2.3', 'foobar@ctrl.example.com', 'hosed', options)
|
|
74
|
+
expect(%i(permerror)).to include(result.code)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|