coppertone 0.0.1 → 0.0.2
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 +4 -4
- data/.travis.yml +1 -0
- data/LICENSE +1 -1
- data/Rakefile +2 -2
- data/coppertone.gemspec +1 -0
- data/lib/coppertone/directive.rb +14 -7
- data/lib/coppertone/error.rb +24 -5
- data/lib/coppertone/ip_address_wrapper.rb +9 -6
- data/lib/coppertone/macro_context.rb +7 -13
- data/lib/coppertone/macro_string/macro_expand.rb +3 -2
- data/lib/coppertone/macro_string.rb +12 -5
- data/lib/coppertone/mechanism/a.rb +5 -1
- data/lib/coppertone/mechanism/all.rb +11 -4
- data/lib/coppertone/mechanism/cidr_parser.rb +1 -1
- data/lib/coppertone/mechanism/domain_spec_mechanism.rb +9 -0
- data/lib/coppertone/mechanism/domain_spec_optional.rb +1 -0
- data/lib/coppertone/mechanism/domain_spec_required.rb +1 -0
- data/lib/coppertone/mechanism/domain_spec_with_dual_cidr.rb +1 -0
- data/lib/coppertone/mechanism/exists.rb +5 -1
- data/lib/coppertone/mechanism/include.rb +17 -5
- data/lib/coppertone/mechanism/ip4.rb +5 -1
- data/lib/coppertone/mechanism/ip6.rb +5 -1
- data/lib/coppertone/mechanism/ip_mechanism.rb +9 -1
- data/lib/coppertone/mechanism/mx.rb +5 -1
- data/lib/coppertone/mechanism/ptr.rb +5 -1
- data/lib/coppertone/mechanism.rb +25 -2
- data/lib/coppertone/modifier/base.rb +1 -0
- data/lib/coppertone/modifier/exp.rb +6 -2
- data/lib/coppertone/modifier/redirect.rb +5 -1
- data/lib/coppertone/modifier/unknown.rb +6 -1
- data/lib/coppertone/modifier.rb +11 -2
- data/lib/coppertone/null_macro_context.rb +23 -0
- data/lib/coppertone/qualifier.rb +8 -0
- data/lib/coppertone/record.rb +33 -41
- data/lib/coppertone/record_finder.rb +3 -1
- data/lib/coppertone/record_term_parser.rb +30 -0
- data/lib/coppertone/request.rb +3 -1
- data/lib/coppertone/request_context.rb +1 -2
- data/lib/coppertone/result.rb +6 -2
- data/lib/coppertone/utils/validated_domain_finder.rb +6 -4
- data/lib/coppertone/version.rb +1 -1
- data/lib/coppertone.rb +3 -1
- data/spec/directive_spec.rb +13 -0
- data/spec/ip_address_wrapper_spec.rb +3 -0
- data/spec/macro_string_spec.rb +20 -0
- data/spec/mechanism/a_spec.rb +22 -7
- data/spec/mechanism/all_spec.rb +8 -0
- data/spec/mechanism/exists_spec.rb +14 -6
- data/spec/mechanism/include_spec.rb +13 -1
- data/spec/mechanism/ip4_spec.rb +15 -5
- data/spec/mechanism/ip6_spec.rb +18 -11
- data/spec/mechanism/mx_spec.rb +56 -0
- data/spec/mechanism/ptr_spec.rb +7 -0
- data/spec/modifier/exp_spec.rb +10 -0
- data/spec/modifier/redirect_spec.rb +10 -0
- data/spec/open_spf/ALL_mechanism_syntax_spec.rb +6 -6
- data/spec/open_spf/A_mechanism_syntax_spec.rb +30 -30
- data/spec/open_spf/EXISTS_mechanism_syntax_spec.rb +8 -8
- data/spec/open_spf/IP4_mechanism_syntax_spec.rb +10 -10
- data/spec/open_spf/IP6_mechanism_syntax_spec.rb +10 -10
- data/spec/open_spf/Include_mechanism_semantics_and_syntax_spec.rb +10 -10
- data/spec/open_spf/Initial_processing_spec.rb +21 -14
- data/spec/open_spf/MX_mechanism_syntax_spec.rb +22 -22
- data/spec/open_spf/Macro_expansion_rules_spec.rb +26 -30
- data/spec/open_spf/PTR_mechanism_syntax_spec.rb +7 -7
- data/spec/open_spf/Processing_limits_spec.rb +12 -12
- data/spec/open_spf/Record_evaluation_spec.rb +13 -13
- data/spec/open_spf/Record_lookup_spec.rb +8 -8
- data/spec/open_spf/Selecting_records_spec.rb +11 -11
- data/spec/open_spf/Semantics_of_exp_and_other_modifiers_spec.rb +27 -25
- data/spec/open_spf/Test_cases_from_implementation_bugs_spec.rb +2 -2
- data/spec/record_spec.rb +34 -13
- data/spec/request_context_spec.rb +1 -1
- data/spec/rfc7208-tests.yml +10 -0
- metadata +59 -48
- data/lib/coppertone/dns/error.rb +0 -9
- data/lib/coppertone/dns/mock_client.rb +0 -106
- data/lib/coppertone/dns/resolv_client.rb +0 -110
- data/lib/coppertone/dns.rb +0 -3
- data/lib/resolv/dns/resource/in/spf.rb +0 -15
- data/spec/dns/resolv_client_spec.rb +0 -307
data/spec/mechanism/ip6_spec.rb
CHANGED
@@ -16,30 +16,30 @@ describe Coppertone::Mechanism::IP6 do
|
|
16
16
|
|
17
17
|
it 'should fail if called with an invalid IP' do
|
18
18
|
expect do
|
19
|
-
Coppertone::Mechanism::IP6.new('not_an_ip')
|
19
|
+
Coppertone::Mechanism::IP6.new(':not_an_ip')
|
20
20
|
end.to raise_error(Coppertone::InvalidMechanismError)
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'should not fail if called with an IP v4' do
|
24
|
-
mech = Coppertone::Mechanism::IP6.new('1.2.3.4')
|
24
|
+
mech = Coppertone::Mechanism::IP6.new(':1.2.3.4')
|
25
25
|
expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4'))
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'should work if called with an IP6' do
|
29
|
-
mech = Coppertone::Mechanism::IP6.new('fe80::202:b3ff:fe1e:8329')
|
29
|
+
mech = Coppertone::Mechanism::IP6.new(':fe80::202:b3ff:fe1e:8329')
|
30
30
|
expect(mech.ip_network)
|
31
31
|
.to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329'))
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'should work if called with an IP6 with a pfxlen' do
|
35
|
-
mech = Coppertone::Mechanism::IP6.new('fe80::202:b3ff:fe1e:8329/64')
|
35
|
+
mech = Coppertone::Mechanism::IP6.new(':fe80::202:b3ff:fe1e:8329/64')
|
36
36
|
expect(mech.ip_network)
|
37
37
|
.to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329/64'))
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'should fail if called with an invalid pfxlen' do
|
41
41
|
expect do
|
42
|
-
Coppertone::Mechanism::IP6.new('fe80::202:b3ff:fe1e:8329/384')
|
42
|
+
Coppertone::Mechanism::IP6.new(':fe80::202:b3ff:fe1e:8329/384')
|
43
43
|
end.to raise_error(Coppertone::InvalidMechanismError)
|
44
44
|
end
|
45
45
|
|
@@ -60,23 +60,23 @@ describe Coppertone::Mechanism::IP6 do
|
|
60
60
|
|
61
61
|
it 'should fail if called with an invalid IP' do
|
62
62
|
expect do
|
63
|
-
Coppertone::Mechanism::IP6.create('not_an_ip')
|
63
|
+
Coppertone::Mechanism::IP6.create(':not_an_ip')
|
64
64
|
end.to raise_error(Coppertone::InvalidMechanismError)
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'should not fail if called with an IP v4' do
|
68
|
-
mech = Coppertone::Mechanism::IP6.create('1.2.3.4')
|
68
|
+
mech = Coppertone::Mechanism::IP6.create(':1.2.3.4')
|
69
69
|
expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4'))
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'should work if called with an IP6' do
|
73
|
-
mech = Coppertone::Mechanism::IP6.create('fe80::202:b3ff:fe1e:8329')
|
73
|
+
mech = Coppertone::Mechanism::IP6.create(':fe80::202:b3ff:fe1e:8329')
|
74
74
|
expect(mech.ip_network)
|
75
75
|
.to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329'))
|
76
76
|
end
|
77
77
|
|
78
78
|
it 'should work if called with an IP6 with a pfxlen' do
|
79
|
-
mech = Coppertone::Mechanism::IP6.create('fe80::202:b3ff:fe1e:8329/64')
|
79
|
+
mech = Coppertone::Mechanism::IP6.create(':fe80::202:b3ff:fe1e:8329/64')
|
80
80
|
expect(mech.ip_network)
|
81
81
|
.to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329/64'))
|
82
82
|
end
|
@@ -92,13 +92,20 @@ describe Coppertone::Mechanism::IP6 do
|
|
92
92
|
end
|
93
93
|
|
94
94
|
it 'should return true if the client IP is in the network' do
|
95
|
-
mech = Coppertone::Mechanism::IP6.create('fe80:0:0:0:202:b3ff:fe1e:8300/120')
|
95
|
+
mech = Coppertone::Mechanism::IP6.create(':fe80:0:0:0:202:b3ff:fe1e:8300/120')
|
96
96
|
expect(mech.match?(macro_context, double)).to eq(true)
|
97
97
|
end
|
98
98
|
|
99
99
|
it 'should return false if the client IP is not in the network' do
|
100
|
-
mech = Coppertone::Mechanism::IP6.create('fe80:0:0:0:202:b3ff:fe1e:8300/126')
|
100
|
+
mech = Coppertone::Mechanism::IP6.create(':fe80:0:0:0:202:b3ff:fe1e:8300/126')
|
101
101
|
expect(mech.match?(macro_context, double)).to eq(false)
|
102
102
|
end
|
103
103
|
end
|
104
|
+
|
105
|
+
context 'dns_lookup_term?' do
|
106
|
+
it 'should be false' do
|
107
|
+
expect(Coppertone::Mechanism::IP6).not_to be_dns_lookup_term
|
108
|
+
expect(Coppertone::Mechanism::IP6.create(':fe80::202:b3ff:fe1e:8329')).not_to be_dns_lookup_term
|
109
|
+
end
|
110
|
+
end
|
104
111
|
end
|
data/spec/mechanism/mx_spec.rb
CHANGED
@@ -8,6 +8,7 @@ describe Coppertone::Mechanism::MX do
|
|
8
8
|
expect(mech.domain_spec).to be_nil
|
9
9
|
expect(mech.ip_v4_cidr_length).to eq(32)
|
10
10
|
expect(mech.ip_v6_cidr_length).to eq(128)
|
11
|
+
expect(mech.to_s).to eq('mx')
|
11
12
|
end
|
12
13
|
|
13
14
|
it 'should not fail if called with a blank argument' do
|
@@ -16,6 +17,7 @@ describe Coppertone::Mechanism::MX do
|
|
16
17
|
expect(mech.domain_spec).to be_nil
|
17
18
|
expect(mech.ip_v4_cidr_length).to eq(32)
|
18
19
|
expect(mech.ip_v6_cidr_length).to eq(128)
|
20
|
+
expect(mech.to_s).to eq('mx')
|
19
21
|
end
|
20
22
|
|
21
23
|
it 'should fail if called with an invalid macrostring' do
|
@@ -23,6 +25,53 @@ describe Coppertone::Mechanism::MX do
|
|
23
25
|
Coppertone::Mechanism::MX.new('abc%:def')
|
24
26
|
end.to raise_error(Coppertone::InvalidMechanismError)
|
25
27
|
end
|
28
|
+
|
29
|
+
it 'should process the domain spec if it includes a IP v4 CIDR' do
|
30
|
+
mech = Coppertone::Mechanism::MX.new('/24')
|
31
|
+
expect(mech).not_to be_nil
|
32
|
+
expect(mech.domain_spec).to be_nil
|
33
|
+
expect(mech.ip_v4_cidr_length).to eq(24)
|
34
|
+
expect(mech.ip_v6_cidr_length).to eq(128)
|
35
|
+
expect(mech.to_s).to eq('mx/24')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should process the domain spec if it includes a IP v6 CIDR' do
|
39
|
+
mech = Coppertone::Mechanism::MX.new('//96')
|
40
|
+
expect(mech).not_to be_nil
|
41
|
+
expect(mech.domain_spec).to be_nil
|
42
|
+
expect(mech.ip_v4_cidr_length).to eq(32)
|
43
|
+
expect(mech.ip_v6_cidr_length).to eq(96)
|
44
|
+
expect(mech.to_s).to eq('mx//96')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should process the domain spec if it includes an IP v4 CIDR and an IP v6 CIDR' do
|
48
|
+
mech = Coppertone::Mechanism::MX.new('/28//96')
|
49
|
+
expect(mech).not_to be_nil
|
50
|
+
expect(mech.domain_spec).to be_nil
|
51
|
+
expect(mech.ip_v4_cidr_length).to eq(28)
|
52
|
+
expect(mech.ip_v6_cidr_length).to eq(96)
|
53
|
+
expect(mech.to_s).to eq('mx/28//96')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should not fail if called with a blank argument' do
|
57
|
+
mech = Coppertone::Mechanism::MX.new(':mx.example.com')
|
58
|
+
expect(mech).not_to be_nil
|
59
|
+
expect(mech.domain_spec)
|
60
|
+
.to eq(Coppertone::DomainSpec.new('mx.example.com'))
|
61
|
+
expect(mech.ip_v4_cidr_length).to eq(32)
|
62
|
+
expect(mech.ip_v6_cidr_length).to eq(128)
|
63
|
+
expect(mech.to_s).to eq('mx:mx.example.com')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should not fail if called with a blank argument' do
|
67
|
+
mech = Coppertone::Mechanism::MX.new(':mx.example.com/28//96')
|
68
|
+
expect(mech).not_to be_nil
|
69
|
+
expect(mech.domain_spec)
|
70
|
+
.to eq(Coppertone::DomainSpec.new('mx.example.com'))
|
71
|
+
expect(mech.ip_v4_cidr_length).to eq(28)
|
72
|
+
expect(mech.ip_v6_cidr_length).to eq(96)
|
73
|
+
expect(mech.to_s).to eq('mx:mx.example.com/28//96')
|
74
|
+
end
|
26
75
|
end
|
27
76
|
|
28
77
|
context '#create' do
|
@@ -48,4 +97,11 @@ describe Coppertone::Mechanism::MX do
|
|
48
97
|
end.to raise_error(Coppertone::InvalidMechanismError)
|
49
98
|
end
|
50
99
|
end
|
100
|
+
|
101
|
+
context 'dns_lookup_term?' do
|
102
|
+
it 'should be true' do
|
103
|
+
expect(Coppertone::Mechanism::MX).to be_dns_lookup_term
|
104
|
+
expect(Coppertone::Mechanism::MX.new(':example.com')).to be_dns_lookup_term
|
105
|
+
end
|
106
|
+
end
|
51
107
|
end
|
data/spec/mechanism/ptr_spec.rb
CHANGED
@@ -40,4 +40,11 @@ describe Coppertone::Mechanism::Ptr do
|
|
40
40
|
end.to raise_error(Coppertone::InvalidMechanismError)
|
41
41
|
end
|
42
42
|
end
|
43
|
+
|
44
|
+
context 'dns_lookup_term?' do
|
45
|
+
it 'should be true' do
|
46
|
+
expect(Coppertone::Mechanism::Ptr).to be_dns_lookup_term
|
47
|
+
expect(Coppertone::Mechanism::Ptr.new(':example.com')).to be_dns_lookup_term
|
48
|
+
end
|
49
|
+
end
|
43
50
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Coppertone::Modifier::Redirect do
|
4
|
+
context 'to_s' do
|
5
|
+
it 'should result in the expected string' do
|
6
|
+
expect(Coppertone::Modifier::Redirect.new('test.example.com').to_s)
|
7
|
+
.to eq('redirect=test.example.com')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -5,34 +5,34 @@ describe 'ALL mechanism syntax' do
|
|
5
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
6
|
end
|
7
7
|
|
8
|
-
let(:dns_client) {
|
8
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
9
|
let(:options) { { dns_client: dns_client } }
|
10
10
|
|
11
11
|
it 'all = "all" ' do
|
12
12
|
# At least one implementation got this wrong
|
13
13
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
14
|
-
expect(
|
14
|
+
expect([:permerror]).to include(result.code)
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'all = "all" ' do
|
18
18
|
# At least one implementation got this wrong
|
19
19
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
20
|
-
expect(
|
20
|
+
expect([:permerror]).to include(result.code)
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'all = "all" ' do
|
24
24
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
|
25
|
-
expect(
|
25
|
+
expect([:permerror]).to include(result.code)
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'all = "all" ' do
|
29
29
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
30
|
-
expect(
|
30
|
+
expect([:neutral]).to include(result.code)
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'all = "all" ' do
|
34
34
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
|
35
|
-
expect(
|
35
|
+
expect([:pass]).to include(result.code)
|
36
36
|
end
|
37
37
|
|
38
38
|
end
|
@@ -5,155 +5,155 @@ describe 'A mechanism syntax' do
|
|
5
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
6
|
end
|
7
7
|
|
8
|
-
let(:dns_client) {
|
8
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
9
|
let(:options) { { dns_client: dns_client } }
|
10
10
|
|
11
11
|
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
12
12
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6.example.com', 'mail.example.com', options)
|
13
|
-
expect(
|
13
|
+
expect([:fail]).to include(result.code)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
17
17
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6a.example.com', 'mail.example.com', options)
|
18
|
-
expect(
|
18
|
+
expect([:permerror]).to include(result.code)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
22
22
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e7.example.com', 'mail.example.com', options)
|
23
|
-
expect(
|
23
|
+
expect([:permerror]).to include(result.code)
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
27
27
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8.example.com', 'mail.example.com', options)
|
28
|
-
expect(
|
28
|
+
expect([:pass]).to include(result.code)
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
32
32
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8e.example.com', 'mail.example.com', options)
|
33
|
-
expect(
|
33
|
+
expect([:permerror]).to include(result.code)
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
37
37
|
result = Coppertone::SpfService.authenticate_email('2001:db8:1234::cafe:babe', 'foo@e8.example.com', 'mail.example.com', options)
|
38
|
-
expect(
|
38
|
+
expect([:pass]).to include(result.code)
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
42
42
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8b.example.com', 'mail.example.com', options)
|
43
|
-
expect(
|
43
|
+
expect([:fail]).to include(result.code)
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'A = "a" [ ":" domain-spec ] [ dual-cidr-length ] dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] ' do
|
47
47
|
result = Coppertone::SpfService.authenticate_email('2001:db8:1234::cafe:babe', 'foo@e8a.example.com', 'mail.example.com', options)
|
48
|
-
expect(
|
48
|
+
expect([:fail]).to include(result.code)
|
49
49
|
end
|
50
50
|
|
51
51
|
it 'A matches any returned IP.' do
|
52
52
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e10.example.com', 'mail.example.com', options)
|
53
|
-
expect(
|
53
|
+
expect([:pass]).to include(result.code)
|
54
54
|
end
|
55
55
|
|
56
56
|
it 'A matches any returned IP.' do
|
57
57
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e10.example.com', 'mail.example.com', options)
|
58
|
-
expect(
|
58
|
+
expect([:pass]).to include(result.code)
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'domain-spec must pass basic syntax checks; a : may appear in domain-spec, but not in top-label' do
|
62
62
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e9.example.com', 'mail.example.com', options)
|
63
|
-
expect(
|
63
|
+
expect([:permerror]).to include(result.code)
|
64
64
|
end
|
65
65
|
|
66
66
|
it 'If no ips are returned, A mechanism does not match, even with /0.' do
|
67
67
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
68
|
-
expect(
|
68
|
+
expect([:fail]).to include(result.code)
|
69
69
|
end
|
70
70
|
|
71
71
|
it 'Matches if any A records are present in DNS.' do
|
72
72
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
73
|
-
expect(
|
73
|
+
expect([:pass]).to include(result.code)
|
74
74
|
end
|
75
75
|
|
76
76
|
it 'Matches if any A records are present in DNS.' do
|
77
77
|
result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@e2.example.com', 'mail.example.com', options)
|
78
|
-
expect(
|
78
|
+
expect([:fail]).to include(result.code)
|
79
79
|
end
|
80
80
|
|
81
81
|
it 'Would match if any AAAA records are present in DNS, but not for an IP4 connection.' do
|
82
82
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2a.example.com', 'mail.example.com', options)
|
83
|
-
expect(
|
83
|
+
expect([:fail]).to include(result.code)
|
84
84
|
end
|
85
85
|
|
86
86
|
it 'Would match if any AAAA records are present in DNS, but not for an IP4 connection.' do
|
87
87
|
result = Coppertone::SpfService.authenticate_email('::FFFF:1.2.3.4', 'foo@e2a.example.com', 'mail.example.com', options)
|
88
|
-
expect(
|
88
|
+
expect([:fail]).to include(result.code)
|
89
89
|
end
|
90
90
|
|
91
91
|
it 'Matches if any AAAA records are present in DNS.' do
|
92
92
|
result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@e2a.example.com', 'mail.example.com', options)
|
93
|
-
expect(
|
93
|
+
expect([:pass]).to include(result.code)
|
94
94
|
end
|
95
95
|
|
96
96
|
it 'Simple IP6 Address match with dual stack.' do
|
97
97
|
result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@ipv6.example.com', 'mail.example.com', options)
|
98
|
-
expect(
|
98
|
+
expect([:pass]).to include(result.code)
|
99
99
|
end
|
100
100
|
|
101
101
|
it 'No match if no AAAA records are present in DNS.' do
|
102
102
|
result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@e2b.example.com', 'mail.example.com', options)
|
103
|
-
expect(
|
103
|
+
expect([:fail]).to include(result.code)
|
104
104
|
end
|
105
105
|
|
106
106
|
it 'Null octets not allowed in toplabel' do
|
107
107
|
result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'foo@e3.example.com', 'mail.example.com', options)
|
108
|
-
expect(
|
108
|
+
expect([:permerror]).to include(result.code)
|
109
109
|
end
|
110
110
|
|
111
111
|
it 'toplabel may not be all numeric' do
|
112
112
|
# A common publishing mistake is using ip4 addresses with A mechanism. This should receive special diagnostic attention in the permerror.
|
113
113
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
114
|
-
expect(
|
114
|
+
expect([:permerror]).to include(result.code)
|
115
115
|
end
|
116
116
|
|
117
117
|
it 'toplabel may not be all numeric' do
|
118
118
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
|
119
|
-
expect(
|
119
|
+
expect([:permerror]).to include(result.code)
|
120
120
|
end
|
121
121
|
|
122
122
|
it 'toplabel may contain dashes' do
|
123
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
124
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e14.example.com', 'mail.example.com', options)
|
125
|
-
expect(
|
125
|
+
expect([:pass]).to include(result.code)
|
126
126
|
end
|
127
127
|
|
128
128
|
it 'toplabel may not begin with a dash' do
|
129
129
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e12.example.com', 'mail.example.com', options)
|
130
|
-
expect(
|
130
|
+
expect([:permerror]).to include(result.code)
|
131
131
|
end
|
132
132
|
|
133
133
|
it 'domain-spec may not consist of only a toplabel.' do
|
134
134
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5a.example.com', 'mail.example.com', options)
|
135
|
-
expect(
|
135
|
+
expect([:permerror]).to include(result.code)
|
136
136
|
end
|
137
137
|
|
138
138
|
it 'domain-spec may not consist of only a toplabel.' do
|
139
139
|
# "A trailing dot doesn\'t help."
|
140
140
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5b.example.com', 'mail.example.com', options)
|
141
|
-
expect(
|
141
|
+
expect([:permerror]).to include(result.code)
|
142
142
|
end
|
143
143
|
|
144
144
|
it 'domain-spec may contain any visible char except %' do
|
145
145
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e11.example.com', 'mail.example.com', options)
|
146
|
-
expect(
|
146
|
+
expect([:pass]).to include(result.code)
|
147
147
|
end
|
148
148
|
|
149
149
|
it 'domain-spec may contain any visible char except %' do
|
150
150
|
result = Coppertone::SpfService.authenticate_email('::FFFF:1.2.3.4', 'foo@e11.example.com', 'mail.example.com', options)
|
151
|
-
expect(
|
151
|
+
expect([:pass]).to include(result.code)
|
152
152
|
end
|
153
153
|
|
154
154
|
it 'domain-spec cannot be empty.' do
|
155
155
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e13.example.com', 'mail.example.com', options)
|
156
|
-
expect(
|
156
|
+
expect([:permerror]).to include(result.code)
|
157
157
|
end
|
158
158
|
|
159
159
|
end
|
@@ -5,42 +5,42 @@ describe 'EXISTS mechanism syntax' do
|
|
5
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
6
|
end
|
7
7
|
|
8
|
-
let(:dns_client) {
|
8
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
9
|
let(:options) { { dns_client: dns_client } }
|
10
10
|
|
11
11
|
it 'domain-spec cannot be empty.' do
|
12
12
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
13
|
-
expect(
|
13
|
+
expect([:permerror]).to include(result.code)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'exists = "exists" ":" domain-spec' do
|
17
17
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
18
|
-
expect(
|
18
|
+
expect([:permerror]).to include(result.code)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'exists = "exists" ":" domain-spec' do
|
22
22
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
|
23
|
-
expect(
|
23
|
+
expect([:permerror]).to include(result.code)
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'mechanism matches if any DNS A RR exists' do
|
27
27
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
28
|
-
expect(
|
28
|
+
expect([:pass]).to include(result.code)
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'The lookup type is A even when the connection is ip6' do
|
32
32
|
result = Coppertone::SpfService.authenticate_email('CAFE:BABE::3', 'foo@e4.example.com', 'mail.example.com', options)
|
33
|
-
expect(
|
33
|
+
expect([:pass]).to include(result.code)
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'The lookup type is A even when the connection is ip6' do
|
37
37
|
result = Coppertone::SpfService.authenticate_email('CAFE:BABE::3', 'foo@e5.example.com', 'mail.example.com', options)
|
38
|
-
expect(
|
38
|
+
expect([:fail]).to include(result.code)
|
39
39
|
end
|
40
40
|
|
41
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
42
|
result = Coppertone::SpfService.authenticate_email('CAFE:BABE::3', 'foo@e6.example.com', 'mail.example.com', options)
|
43
|
-
expect(
|
43
|
+
expect([:temperror]).to include(result.code)
|
44
44
|
end
|
45
45
|
|
46
46
|
end
|
@@ -5,55 +5,55 @@ describe 'IP4 mechanism syntax' do
|
|
5
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
6
|
end
|
7
7
|
|
8
|
-
let(:dns_client) {
|
8
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
9
|
let(:options) { { dns_client: dns_client } }
|
10
10
|
|
11
11
|
it 'ip4-cidr-length = "/" 1*DIGIT' do
|
12
12
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
13
|
-
expect(
|
13
|
+
expect([:pass]).to include(result.code)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'ip4-cidr-length = "/" 1*DIGIT' do
|
17
17
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
18
|
-
expect(
|
18
|
+
expect([:pass]).to include(result.code)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'Invalid CIDR should get permerror.' do
|
22
22
|
# The RFC4408 was silent on ip4 CIDR > 32 or ip6 CIDR > 128, but RFC7208 is explicit. Invalid CIDR is prohibited.
|
23
23
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
|
24
|
-
expect(
|
24
|
+
expect([:permerror]).to include(result.code)
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'Invalid CIDR should get permerror.' do
|
28
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
29
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
30
|
-
expect(
|
30
|
+
expect([:permerror]).to include(result.code)
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'IP4 = "ip4" ":" ip4-network [ ip4-cidr-length ]' do
|
34
34
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
|
35
|
-
expect(
|
35
|
+
expect([:permerror]).to include(result.code)
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'IP4 = "ip4" ":" ip4-network [ ip4-cidr-length ]' do
|
39
39
|
# This has actually been published in SPF records.
|
40
40
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8.example.com', 'mail.example.com', options)
|
41
|
-
expect(
|
41
|
+
expect([:permerror]).to include(result.code)
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'It is not permitted to omit parts of the IP address instead of using CIDR notations.' do
|
45
45
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e9.example.com', 'mail.example.com', options)
|
46
|
-
expect(
|
46
|
+
expect([:permerror]).to include(result.code)
|
47
47
|
end
|
48
48
|
|
49
49
|
it 'dual-cidr-length not permitted on ip4' do
|
50
50
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6.example.com', 'mail.example.com', options)
|
51
|
-
expect(
|
51
|
+
expect([:permerror]).to include(result.code)
|
52
52
|
end
|
53
53
|
|
54
54
|
it 'IP4 mapped IP6 connections MUST be treated as IP4' do
|
55
55
|
result = Coppertone::SpfService.authenticate_email('::FFFF:1.2.3.4', 'foo@e7.example.com', 'mail.example.com', options)
|
56
|
-
expect(
|
56
|
+
expect([:fail]).to include(result.code)
|
57
57
|
end
|
58
58
|
|
59
59
|
end
|
@@ -5,56 +5,56 @@ describe 'IP6 mechanism syntax' do
|
|
5
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
6
|
end
|
7
7
|
|
8
|
-
let(:dns_client) {
|
8
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
9
|
let(:options) { { dns_client: dns_client } }
|
10
10
|
|
11
11
|
it 'IP6 = "ip6" ":" ip6-network [ ip6-cidr-length ]' do
|
12
12
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
13
|
-
expect(
|
13
|
+
expect([:permerror]).to include(result.code)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'IP4 connections do not match ip6.' do
|
17
17
|
# There was controversy over IPv4 mapped connections. RFC7208 clearly states IPv4 mapped addresses only match ip4: mechanisms.
|
18
18
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
19
|
-
expect(
|
19
|
+
expect([:neutral]).to include(result.code)
|
20
20
|
end
|
21
21
|
|
22
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
23
|
# There was controversy over ip4 mapped connections. RFC7208 clearly requires such connections to be considered as ip4 only.
|
24
24
|
result = Coppertone::SpfService.authenticate_email('::FFFF:1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
25
|
-
expect(
|
25
|
+
expect([:neutral]).to include(result.code)
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'Match any IP6' do
|
29
29
|
result = Coppertone::SpfService.authenticate_email('DEAF:BABE::CAB:FEE', 'foo@e2.example.com', 'mail.example.com', options)
|
30
|
-
expect(
|
30
|
+
expect([:pass]).to include(result.code)
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'Invalid CIDR' do
|
34
34
|
# IP4 only implementations MUST fully syntax check all mechanisms, even if they otherwise ignore them.
|
35
35
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
|
36
|
-
expect(
|
36
|
+
expect([:permerror]).to include(result.code)
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'dual-cidr syntax not used for ip6' do
|
40
40
|
# IP4 only implementations MUST fully syntax check all mechanisms, even if they otherwise ignore them.
|
41
41
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
|
42
|
-
expect(
|
42
|
+
expect([:permerror]).to include(result.code)
|
43
43
|
end
|
44
44
|
|
45
45
|
it 'make sure ip4 cidr restriction are not used for ip6' do
|
46
46
|
result = Coppertone::SpfService.authenticate_email('CAFE:BABE:8000::', 'foo@e5.example.com', 'mail.example.com', options)
|
47
|
-
expect(
|
47
|
+
expect([:pass]).to include(result.code)
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'make sure ip4 cidr restriction are not used for ip6' do
|
51
51
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
|
52
|
-
expect(
|
52
|
+
expect([:neutral]).to include(result.code)
|
53
53
|
end
|
54
54
|
|
55
55
|
it '' do
|
56
56
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6.example.com', 'mail.example.com', options)
|
57
|
-
expect(
|
57
|
+
expect([:permerror]).to include(result.code)
|
58
58
|
end
|
59
59
|
|
60
60
|
end
|