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
| @@ -5,52 +5,52 @@ describe 'Include mechanism semantics and syntax' do | |
| 5 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 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 'recursive check_host() result of fail causes include to not match.' do
         | 
| 12 12 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
         | 
| 13 | 
            -
                expect( | 
| 13 | 
            +
                expect([:softfail]).to include(result.code)
         | 
| 14 14 | 
             
              end
         | 
| 15 15 |  | 
| 16 16 | 
             
              it 'recursive check_host() result of softfail causes include to not match.' 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 'recursive check_host() result of neutral causes include to not match.' do
         | 
| 22 22 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
         | 
| 23 | 
            -
                expect( | 
| 23 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 24 24 | 
             
              end
         | 
| 25 25 |  | 
| 26 26 | 
             
              it 'recursive check_host() result of temperror causes include to temperror' do
         | 
| 27 27 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
         | 
| 28 | 
            -
                expect( | 
| 28 | 
            +
                expect([:temperror]).to include(result.code)
         | 
| 29 29 | 
             
              end
         | 
| 30 30 |  | 
| 31 31 | 
             
              it 'recursive check_host() result of permerror causes include to permerror' do
         | 
| 32 32 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
         | 
| 33 | 
            -
                expect( | 
| 33 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 34 34 | 
             
              end
         | 
| 35 35 |  | 
| 36 36 | 
             
              it 'include          = "include"  ":" domain-spec' do
         | 
| 37 37 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6.example.com', 'mail.example.com', options)
         | 
| 38 | 
            -
                expect( | 
| 38 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 39 39 | 
             
              end
         | 
| 40 40 |  | 
| 41 41 | 
             
              it 'include          = "include"  ":" domain-spec' do
         | 
| 42 42 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e9.example.com', 'mail.example.com', options)
         | 
| 43 | 
            -
                expect( | 
| 43 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 44 44 | 
             
              end
         | 
| 45 45 |  | 
| 46 46 | 
             
              it 'recursive check_host() result of none causes include to permerror' do
         | 
| 47 47 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e7.example.com', 'mail.example.com', options)
         | 
| 48 | 
            -
                expect( | 
| 48 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 49 49 | 
             
              end
         | 
| 50 50 |  | 
| 51 51 | 
             
              it 'domain-spec cannot be empty.' do
         | 
| 52 52 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8.example.com', 'mail.example.com', options)
         | 
| 53 | 
            -
                expect( | 
| 53 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 54 54 | 
             
              end
         | 
| 55 55 |  | 
| 56 56 | 
             
            end
         | 
| @@ -1,77 +1,84 @@ | |
| 1 | 
            +
            # -- encoding : utf-8 --
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'spec_helper'
         | 
| 2 4 |  | 
| 3 5 | 
             
            describe 'Initial processing' do
         | 
| 4 6 | 
             
              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' }] }
         | 
| 7 | 
            +
                { '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' }], 'fine.example.com' => [{ 'TXT' => 'v=spf1 a  -all' }] }
         | 
| 6 8 | 
             
              end
         | 
| 7 9 |  | 
| 8 | 
            -
              let(:dns_client) {  | 
| 10 | 
            +
              let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
         | 
| 9 11 | 
             
              let(:options) { { dns_client: dns_client } }
         | 
| 10 12 |  | 
| 11 13 | 
             
              it 'DNS labels limited to 63 chars.' do
         | 
| 12 14 | 
             
                # For initial processing, a long label results in None, not TempError
         | 
| 13 15 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'lyme.eater@A123456789012345678901234567890123456789012345678901234567890123.example.com', 'mail.example.net', options)
         | 
| 14 | 
            -
                expect( | 
| 16 | 
            +
                expect([:none]).to include(result.code)
         | 
| 15 17 | 
             
              end
         | 
| 16 18 |  | 
| 17 19 | 
             
              it 'DNS labels limited to 63 chars.' do
         | 
| 18 20 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'lyme.eater@A12345678901234567890123456789012345678901234567890123456789012.example.com', 'mail.example.net', options)
         | 
| 19 | 
            -
                expect( | 
| 21 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 20 22 | 
             
              end
         | 
| 21 23 |  | 
| 22 24 | 
             
              it 'emptylabel' do
         | 
| 23 25 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'lyme.eater@A...example.com', 'mail.example.net', options)
         | 
| 24 | 
            -
                expect( | 
| 26 | 
            +
                expect([:none]).to include(result.code)
         | 
| 25 27 | 
             
              end
         | 
| 26 28 |  | 
| 27 29 | 
             
              it 'helo-not-fqdn' do
         | 
| 28 30 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.5', '', 'A2345678', options)
         | 
| 29 | 
            -
                expect( | 
| 31 | 
            +
                expect([:none]).to include(result.code)
         | 
| 30 32 | 
             
              end
         | 
| 31 33 |  | 
| 32 34 | 
             
              it 'helo-domain-literal' do
         | 
| 33 35 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.5', '', '[1.2.3.5]', options)
         | 
| 34 | 
            -
                expect( | 
| 36 | 
            +
                expect([:none]).to include(result.code)
         | 
| 35 37 | 
             
              end
         | 
| 36 38 |  | 
| 37 39 | 
             
              it 'nolocalpart' do
         | 
| 38 40 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', '@example.net', 'mail.example.net', options)
         | 
| 39 | 
            -
                expect( | 
| 41 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 40 42 | 
             
                expect(result.explanation).to eq('postmaster')
         | 
| 41 43 | 
             
              end
         | 
| 42 44 |  | 
| 43 45 | 
             
              it 'domain-literal' do
         | 
| 44 46 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'foo@[1.2.3.5]', 'OEMCOMPUTER', options)
         | 
| 45 | 
            -
                expect( | 
| 47 | 
            +
                expect([:none]).to include(result.code)
         | 
| 46 48 | 
             
              end
         | 
| 47 49 |  | 
| 48 50 | 
             
              it 'SPF policies are restricted to 7-bit ascii.' do
         | 
| 49 51 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foobar@hosed.example.com', 'hosed', options)
         | 
| 50 | 
            -
                expect( | 
| 52 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 51 53 | 
             
              end
         | 
| 52 54 |  | 
| 53 55 | 
             
              it 'SPF policies are restricted to 7-bit ascii.' do
         | 
| 54 56 | 
             
                # Checking a possibly different code path for non-ascii chars.
         | 
| 55 57 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foobar@hosed2.example.com', 'hosed', options)
         | 
| 56 | 
            -
                expect( | 
| 58 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 57 59 | 
             
              end
         | 
| 58 60 |  | 
| 59 61 | 
             
              it 'SPF policies are restricted to 7-bit ascii.' do
         | 
| 60 62 | 
             
                # Checking yet another code path for non-ascii chars.
         | 
| 61 63 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foobar@hosed3.example.com', 'hosed', options)
         | 
| 62 | 
            -
                expect( | 
| 64 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 63 65 | 
             
              end
         | 
| 64 66 |  | 
| 65 67 | 
             
              it 'Non-ascii content in non-SPF related records.' do
         | 
| 66 68 | 
             
                # Non-SPF related TXT records are none of our business.
         | 
| 67 69 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foobar@nothosed.example.com', 'hosed', options)
         | 
| 68 | 
            -
                expect( | 
| 70 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 69 71 | 
             
                expect(result.explanation).to eq('DEFAULT')
         | 
| 70 72 | 
             
              end
         | 
| 71 73 |  | 
| 72 74 | 
             
              it 'Mechanisms are separated by spaces only, not any control char.' do
         | 
| 73 75 | 
             
                result = Coppertone::SpfService.authenticate_email('192.0.2.3', 'foobar@ctrl.example.com', 'hosed', options)
         | 
| 74 | 
            -
                expect( | 
| 76 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              it 'ABNF for term separation is one or more spaces, not just one.' do
         | 
| 80 | 
            +
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'actually@fine.example.com', 'hosed', options)
         | 
| 81 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 75 82 | 
             
              end
         | 
| 76 83 |  | 
| 77 84 | 
             
            end
         | 
| @@ -5,115 +5,115 @@ describe 'MX mechanism syntax' do | |
| 5 5 | 
             
                { 'mail.example.com' => [{ 'A' => '1.2.3.4' }, { 'MX' => [0, ''] }, { 'TXT' => 'v=spf1 mx' }], 'e1.example.com' => [{ 'TXT' => 'v=spf1 mx/0 -all' }, { 'MX' => [0, 'e1.example.com'] }], 'e2.example.com' => [{ 'A' => '1.1.1.1' }, { 'AAAA' => '1234::2' }, { 'MX' => [0, 'e2.example.com'] }, { 'TXT' => 'v=spf1 mx/0 -all' }], 'e2a.example.com' => [{ 'AAAA' => '1234::1' }, { 'MX' => [0, 'e2a.example.com'] }, { 'TXT' => 'v=spf1 mx//0 -all' }], 'e2b.example.com' => [{ 'A' => '1.1.1.1' }, { 'MX' => [0, 'e2b.example.com'] }, { 'TXT' => 'v=spf1 mx//0 -all' }], 'e3.example.com' => [{ 'TXT' => "v=spf1 mx:foo.example.com\u0000" }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 mx' }, { 'A' => '1.2.3.4' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 mx:abc.123' }], 'e6.example.com' => [{ 'TXT' => 'v=spf1 mx//33 -all' }], 'e6a.example.com' => [{ 'TXT' => 'v=spf1 mx/33 -all' }], 'e7.example.com' => [{ 'TXT' => 'v=spf1 mx//129 -all' }], 'e9.example.com' => [{ 'TXT' => 'v=spf1 mx:example.com:8080' }], 'e10.example.com' => [{ 'TXT' => 'v=spf1 mx:foo.example.com/24' }], 'foo.example.com' => [{ 'MX' => [0, 'foo1.example.com'] }], 'foo1.example.com' => [{ 'A' => '1.1.1.1' }, { 'A' => '1.2.3.5' }], 'e11.example.com' => [{ 'TXT' => 'v=spf1 mx:foo:bar/baz.example.com' }], 'foo:bar/baz.example.com' => [{ 'MX' => [0, 'foo:bar/baz.example.com'] }, { 'A' => '1.2.3.4' }], 'e12.example.com' => [{ 'TXT' => 'v=spf1 mx:example.-com' }], 'e13.example.com' => [{ 'TXT' => 'v=spf1 mx: -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 'MX                = "mx"      [ ":" 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 'MX                = "mx"      [ ":" 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 'MX                = "mx"      [ ":" 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 'MX matches any returned IP.' do
         | 
| 27 27 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e10.example.com', 'mail.example.com', options)
         | 
| 28 | 
            -
                expect( | 
| 28 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 29 29 | 
             
              end
         | 
| 30 30 |  | 
| 31 31 | 
             
              it 'MX matches any returned IP.' do
         | 
| 32 32 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e10.example.com', 'mail.example.com', options)
         | 
| 33 | 
            -
                expect( | 
| 33 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 34 34 | 
             
              end
         | 
| 35 35 |  | 
| 36 36 | 
             
              it 'domain-spec must pass basic syntax checks' do
         | 
| 37 37 | 
             
                # A \':\' may appear in domain-spec, but not in top-label.
         | 
| 38 38 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e9.example.com', 'mail.example.com', options)
         | 
| 39 | 
            -
                expect( | 
| 39 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 40 40 | 
             
              end
         | 
| 41 41 |  | 
| 42 42 | 
             
              it 'If no ips are returned, MX mechanism does not match, even with /0.' do
         | 
| 43 43 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
         | 
| 44 | 
            -
                expect( | 
| 44 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 45 45 | 
             
              end
         | 
| 46 46 |  | 
| 47 47 | 
             
              it 'Matches if any A records for any MX records are present in DNS.' do
         | 
| 48 48 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
         | 
| 49 | 
            -
                expect( | 
| 49 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 50 50 | 
             
              end
         | 
| 51 51 |  | 
| 52 52 | 
             
              it 'cidr4 doesnt apply to IP6 connections.' do
         | 
| 53 53 | 
             
                # The IP6 CIDR starts with a double slash.
         | 
| 54 54 | 
             
                result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@e2.example.com', 'mail.example.com', options)
         | 
| 55 | 
            -
                expect( | 
| 55 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 56 56 | 
             
              end
         | 
| 57 57 |  | 
| 58 58 | 
             
              it 'Would match if any AAAA records for MX records are present in DNS, but not for an IP4 connection.' do
         | 
| 59 59 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2a.example.com', 'mail.example.com', options)
         | 
| 60 | 
            -
                expect( | 
| 60 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 61 61 | 
             
              end
         | 
| 62 62 |  | 
| 63 63 | 
             
              it 'Would match if any AAAA records for MX records are present in DNS, but not for an IP4 connection.' do
         | 
| 64 64 | 
             
                result = Coppertone::SpfService.authenticate_email('::FFFF:1.2.3.4', 'foo@e2a.example.com', 'mail.example.com', options)
         | 
| 65 | 
            -
                expect( | 
| 65 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 66 66 | 
             
              end
         | 
| 67 67 |  | 
| 68 68 | 
             
              it 'Matches if any AAAA records for any MX records are present in DNS.' do
         | 
| 69 69 | 
             
                result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@e2a.example.com', 'mail.example.com', options)
         | 
| 70 | 
            -
                expect( | 
| 70 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 71 71 | 
             
              end
         | 
| 72 72 |  | 
| 73 73 | 
             
              it 'No match if no AAAA records for any MX records are present in DNS.' do
         | 
| 74 74 | 
             
                result = Coppertone::SpfService.authenticate_email('1234::1', 'foo@e2b.example.com', 'mail.example.com', options)
         | 
| 75 | 
            -
                expect( | 
| 75 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 76 76 | 
             
              end
         | 
| 77 77 |  | 
| 78 78 | 
             
              it 'Null not allowed in top-label.' do
         | 
| 79 79 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'foo@e3.example.com', 'mail.example.com', options)
         | 
| 80 | 
            -
                expect( | 
| 80 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 81 81 | 
             
              end
         | 
| 82 82 |  | 
| 83 83 | 
             
              it 'Top-label may not be all numeric' do
         | 
| 84 84 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
         | 
| 85 | 
            -
                expect( | 
| 85 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 86 86 | 
             
              end
         | 
| 87 87 |  | 
| 88 88 | 
             
              it 'Domain-spec may contain any visible char except %' do
         | 
| 89 89 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e11.example.com', 'mail.example.com', options)
         | 
| 90 | 
            -
                expect( | 
| 90 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 91 91 | 
             
              end
         | 
| 92 92 |  | 
| 93 93 | 
             
              it 'Domain-spec may contain any visible char except %' do
         | 
| 94 94 | 
             
                result = Coppertone::SpfService.authenticate_email('::FFFF:1.2.3.4', 'foo@e11.example.com', 'mail.example.com', options)
         | 
| 95 | 
            -
                expect( | 
| 95 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 96 96 | 
             
              end
         | 
| 97 97 |  | 
| 98 98 | 
             
              it 'Toplabel may not begin with -' do
         | 
| 99 99 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e12.example.com', 'mail.example.com', options)
         | 
| 100 | 
            -
                expect( | 
| 100 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 101 101 | 
             
              end
         | 
| 102 102 |  | 
| 103 103 | 
             
              it 'test null MX' do
         | 
| 104 104 | 
             
                # Some implementations have had trouble with null MX
         | 
| 105 105 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', '', 'mail.example.com', options)
         | 
| 106 | 
            -
                expect( | 
| 106 | 
            +
                expect([:neutral]).to include(result.code)
         | 
| 107 107 | 
             
              end
         | 
| 108 108 |  | 
| 109 109 | 
             
              it 'If the target name has no MX records, check_host() MUST NOT pretend the target is its single MX, and MUST NOT default to an A lookup on the target-name directly.' do
         | 
| 110 110 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
         | 
| 111 | 
            -
                expect( | 
| 111 | 
            +
                expect([:neutral]).to include(result.code)
         | 
| 112 112 | 
             
              end
         | 
| 113 113 |  | 
| 114 114 | 
             
              it 'domain-spec cannot be empty.' do
         | 
| 115 115 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e13.example.com', 'mail.example.com', options)
         | 
| 116 | 
            -
                expect( | 
| 116 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 117 117 | 
             
              end
         | 
| 118 118 |  | 
| 119 119 | 
             
            end
         | 
| @@ -5,150 +5,146 @@ describe 'Macro expansion rules' do | |
| 5 5 | 
             
                { 'example.com.d.spf.example.com' => [{ 'TXT' => 'v=spf1 redirect=a.spf.example.com' }], 'a.spf.example.com' => [{ 'TXT' => 'v=spf1 include:o.spf.example.com. ~all' }], 'o.spf.example.com' => [{ 'TXT' => 'v=spf1 ip4:192.168.218.40' }], 'msgbas2x.cos.example.com' => [{ 'A' => '192.168.218.40' }], 'example.com' => [{ 'A' => '192.168.90.76' }, { 'TXT' => 'v=spf1 redirect=%{d}.d.spf.example.com.' }], 'exp.example.com' => [{ 'TXT' => 'v=spf1 exp=msg.example.com. -all' }], 'msg.example.com' => [{ 'TXT' => 'This is a test.' }], 'e1.example.com' => [{ 'TXT' => 'v=spf1 -exists:%(ir).sbl.example.com ?all' }], 'e1e.example.com' => [{ 'TXT' => 'v=spf1 exists:foo%(ir).sbl.example.com ?all' }], 'e1t.example.com' => [{ 'TXT' => 'v=spf1 exists:foo%.sbl.example.com ?all' }], 'e1a.example.com' => [{ 'TXT' => 'v=spf1 a:macro%%percent%_%_space%-url-space.example.com -all' }], 'macro%percent  space%20url-space.example.com' => [{ 'A' => '1.2.3.4' }], 'e2.example.com' => [{ 'TXT' => 'v=spf1 -all exp=%{r}.example.com' }], 'e3.example.com' => [{ 'TXT' => 'v=spf1 -all exp=%{ir}.example.com' }], '40.218.168.192.example.com' => [{ 'TXT' => 'Connections from %{c} not authorized.' }], 'somewhat.long.exp.example.com' => [{ 'TXT' => 'v=spf1 -all exp=foobar.%{o}.%{o}.%{o}.%{o}.%{o}.%{o}.%{o}.%{o}.example.com' }], 'somewhat.long.exp.example.com.somewhat.long.exp.example.com.somewhat.long.exp.example.com.somewhat.long.exp.example.com.somewhat.long.exp.example.com.somewhat.long.exp.example.com.somewhat.long.exp.example.com.somewhat.long.exp.example.com.example.com' => [{ 'TXT' => 'Congratulations!  That was tricky.' }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 -all exp=e4msg.example.com' }], 'e4msg.example.com' => [{ 'TXT' => '%{c} is queried as %{ir}.%{v}.arpa' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 a:%{a}.example.com -all' }], 'e6.example.com' => [{ 'TXT' => 'v=spf1 -all exp=e6msg.example.com' }], 'e6msg.example.com' => [{ 'TXT' => 'connect from %{p}' }], 'mx.example.com' => [{ 'A' => '192.168.218.41' }, { 'A' => '192.168.218.42' }, { 'AAAA' => 'CAFE:BABE::2' }, { 'AAAA' => 'CAFE:BABE::3' }], '40.218.168.192.in-addr.arpa' => [{ 'PTR' => 'mx.example.com' }], '41.218.168.192.in-addr.arpa' => [{ 'PTR' => 'mx.example.com' }], '42.218.168.192.in-addr.arpa' => [{ 'PTR' => 'mx.example.com' }, { 'PTR' => 'mx.e7.example.com' }], '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.E.B.A.B.E.F.A.C.ip6.arpa' => [{ 'PTR' => 'mx.example.com' }], '3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.E.B.A.B.E.F.A.C.ip6.arpa' => [{ 'PTR' => 'mx.example.com' }], 'mx.e7.example.com' => [{ 'A' => '192.168.218.42' }], 'mx.e7.example.com.should.example.com' => [{ 'A' => '127.0.0.2' }], 'mx.example.com.ok.example.com' => [{ 'A' => '127.0.0.2' }], 'e7.example.com' => [{ 'TXT' => 'v=spf1 exists:%{p}.should.example.com ~exists:%{p}.ok.example.com' }], 'e8.example.com' => [{ 'TXT' => 'v=spf1 -all exp=msg8.%{D2}' }], 'msg8.example.com' => [{ 'TXT' => 'http://example.com/why.html?l=%{L}' }], 'e9.example.com' => [{ 'TXT' => 'v=spf1 a:%{H} -all' }], 'e10.example.com' => [{ 'TXT' => 'v=spf1 -include:_spfh.%{d2} ip4:1.2.3.0/24 -all' }], '_spfh.example.com' => [{ 'TXT' => 'v=spf1 -a:%{h} +all' }], 'e11.example.com' => [{ 'TXT' => 'v=spf1 exists:%{i}.%{l2r-}.user.%{d2}' }], '1.2.3.4.gladstone.philip.user.example.com' => [{ 'A' => '127.0.0.2' }], 'e12.example.com' => [{ 'TXT' => 'v=spf1 exists:%{l2r+-}.user.%{d2}' }], 'bar.foo.user.example.com' => [{ 'A' => '127.0.0.2' }] }
         | 
| 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 'trailing dot is ignored for domains' do
         | 
| 12 12 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@example.com', 'msgbas2x.cos.example.com', options)
         | 
| 13 | 
            -
                expect( | 
| 13 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 14 14 | 
             
              end
         | 
| 15 15 |  | 
| 16 16 | 
             
              it 'trailing dot is not removed from explanation' do
         | 
| 17 17 | 
             
                # A simple way for an implementation to ignore trailing dots on domains is to remove it when present.  But be careful not to remove it for explanation text.
         | 
| 18 18 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@exp.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 19 | 
            -
                expect( | 
| 19 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 20 20 | 
             
                expect(result.explanation).to eq('This is a test.')
         | 
| 21 21 | 
             
              end
         | 
| 22 22 |  | 
| 23 23 | 
             
              it 'The following macro letters are allowed only in "exp" text: c, r, t' do
         | 
| 24 24 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@e2.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 25 | 
            -
                expect( | 
| 25 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 26 26 | 
             
              end
         | 
| 27 27 |  | 
| 28 28 | 
             
              it 'A % character not followed by a {, %, -, or _ character is a syntax error.' do
         | 
| 29 29 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@e1.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 30 | 
            -
                expect( | 
| 30 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 31 31 | 
             
              end
         | 
| 32 32 |  | 
| 33 33 | 
             
              it 'A % character not followed by a {, %, -, or _ character is a syntax error.' do
         | 
| 34 34 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@e1e.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 35 | 
            -
                expect( | 
| 35 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 36 36 | 
             
              end
         | 
| 37 37 |  | 
| 38 38 | 
             
              it 'A % character not followed by a {, %, -, or _ character is a syntax error.' do
         | 
| 39 39 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@e1t.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 40 | 
            -
                expect( | 
| 40 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 41 41 | 
             
              end
         | 
| 42 42 |  | 
| 43 43 | 
             
              it 'macro-encoded percents (%%), spaces (%_), and URL-percent-encoded spaces (%-)' do
         | 
| 44 44 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'test@e1a.example.com', 'mail.example.com', options)
         | 
| 45 | 
            -
                expect( | 
| 45 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 46 46 | 
             
              end
         | 
| 47 47 |  | 
| 48 48 | 
             
              it 'For IPv4 addresses, both the "i" and "c" macros expand to the standard dotted-quad format.' do
         | 
| 49 49 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@e3.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 50 | 
            -
                expect( | 
| 50 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 51 51 | 
             
                expect(result.explanation).to eq('Connections from 192.168.218.40 not authorized.')
         | 
| 52 52 | 
             
              end
         | 
| 53 53 |  | 
| 54 54 | 
             
              it 'When the result of macro expansion is used in a domain name query, if the expanded domain name exceeds 253 characters, the left side is truncated to fit, by removing successive domain labels until the total length does not exceed 253 characters.' do
         | 
| 55 55 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@somewhat.long.exp.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 56 | 
            -
                expect( | 
| 56 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 57 57 | 
             
                expect(result.explanation).to eq('Congratulations!  That was tricky.')
         | 
| 58 58 | 
             
              end
         | 
| 59 59 |  | 
| 60 60 | 
             
              it 'v = the string "in-addr" if <ip> is ipv4, or "ip6" if <ip> is ipv6' do
         | 
| 61 61 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@e4.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 62 | 
            -
                expect( | 
| 62 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 63 63 | 
             
                expect(result.explanation).to eq('192.168.218.40 is queried as 40.218.168.192.in-addr.arpa')
         | 
| 64 64 | 
             
              end
         | 
| 65 65 |  | 
| 66 66 | 
             
              it 'v = the string "in-addr" if <ip> is ipv4, or "ip6" if <ip> is ipv6' do
         | 
| 67 67 | 
             
                result = Coppertone::SpfService.authenticate_email('CAFE:BABE::1', 'test@e4.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 68 | 
            -
                expect( | 
| 68 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 69 69 | 
             
                expect(result.explanation).to eq('cafe:babe::1 is queried as 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.E.B.A.B.E.F.A.C.ip6.arpa')
         | 
| 70 70 | 
             
              end
         | 
| 71 71 |  | 
| 72 72 | 
             
              it 'Allowed macros chars are slodipvh plus crt in explanation.' do
         | 
| 73 73 | 
             
                result = Coppertone::SpfService.authenticate_email('CAFE:BABE::192.168.218.40', 'test@e5.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 74 | 
            -
                expect( | 
| 74 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 75 75 | 
             
              end
         | 
| 76 76 |  | 
| 77 77 | 
             
              it 'p = the validated domain name of <ip>' do
         | 
| 78 78 | 
             
                # The PTR in this example does not validate.
         | 
| 79 79 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@e6.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 80 | 
            -
                expect( | 
| 80 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 81 81 | 
             
                expect(result.explanation).to eq('connect from unknown')
         | 
| 82 82 | 
             
              end
         | 
| 83 83 |  | 
| 84 84 | 
             
              it 'p = the validated domain name of <ip>' do
         | 
| 85 | 
            -
                pending 'It is currently unclear, based on RFC 7208, why this spec should pass.'
         | 
| 86 85 | 
             
                # If a subdomain of the <domain> is present, it SHOULD be used.
         | 
| 87 | 
            -
                result = Coppertone::SpfService.authenticate_email('192.168.218.41',
         | 
| 88 | 
            -
             | 
| 89 | 
            -
                                                                   'msgbas2x.cos.example.com', options)
         | 
| 90 | 
            -
                expect(%i(fail)).to include(result.code)
         | 
| 86 | 
            +
                result = Coppertone::SpfService.authenticate_email('192.168.218.41', 'test@e6.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 87 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 91 88 | 
             
                expect(result.explanation).to eq('connect from mx.example.com')
         | 
| 92 89 | 
             
              end
         | 
| 93 90 |  | 
| 94 91 | 
             
              it 'p = the validated domain name of <ip>' do
         | 
| 95 92 | 
             
                # The PTR in this example does not validate.
         | 
| 96 93 | 
             
                result = Coppertone::SpfService.authenticate_email('CAFE:BABE::1', 'test@e6.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 97 | 
            -
                expect( | 
| 94 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 98 95 | 
             
                expect(result.explanation).to eq('connect from unknown')
         | 
| 99 96 | 
             
              end
         | 
| 100 97 |  | 
| 101 98 | 
             
              it 'p = the validated domain name of <ip>' do
         | 
| 102 | 
            -
                pending 'It is currently unclear, based on RFC 7208, why this spec should pass.'
         | 
| 103 99 | 
             
                # If a subdomain of the <domain> is present, it SHOULD be used.
         | 
| 104 100 | 
             
                result = Coppertone::SpfService.authenticate_email('CAFE:BABE::3', 'test@e6.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 105 | 
            -
                expect( | 
| 101 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 106 102 | 
             
                expect(result.explanation).to eq('connect from mx.example.com')
         | 
| 107 103 | 
             
              end
         | 
| 108 104 |  | 
| 109 105 | 
             
              it 'p = the validated domain name of <ip>' do
         | 
| 110 106 | 
             
                # If a subdomain of the <domain> is present, it SHOULD be used.
         | 
| 111 107 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.42', 'test@e7.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 112 | 
            -
                expect( | 
| 108 | 
            +
                expect([:pass, :softfail]).to include(result.code)
         | 
| 113 109 | 
             
              end
         | 
| 114 110 |  | 
| 115 111 | 
             
              it 'Uppercased macros expand exactly as their lowercased equivalents, and are then URL escaped.  All chars not in the unreserved set MUST be escaped.' do
         | 
| 116 112 | 
             
                # unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
         | 
| 117 113 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.42', '~jack&jill=up-a_b3.c@e8.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 118 | 
            -
                expect( | 
| 114 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 119 115 | 
             
                expect(result.explanation).to eq('http://example.com/why.html?l=~jack%26jill%3Dup-a_b3.c')
         | 
| 120 116 | 
             
              end
         | 
| 121 117 |  | 
| 122 118 | 
             
              it 'h = HELO/EHLO domain' do
         | 
| 123 119 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@e9.example.com', 'msgbas2x.cos.example.com', options)
         | 
| 124 | 
            -
                expect( | 
| 120 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 125 121 | 
             
              end
         | 
| 126 122 |  | 
| 127 123 | 
             
              it 'h = HELO/EHLO domain, but HELO is invalid' do
         | 
| 128 124 | 
             
                # Domain-spec must end in either a macro, or a valid toplabel. It is not correct to check syntax after macro expansion.
         | 
| 129 125 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@e9.example.com', 'JUMPIN\' JUPITER', options)
         | 
| 130 | 
            -
                expect( | 
| 126 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 131 127 | 
             
              end
         | 
| 132 128 |  | 
| 133 129 | 
             
              it 'h = HELO/EHLO domain, but HELO is a domain literal' do
         | 
| 134 130 | 
             
                # Domain-spec must end in either a macro, or a valid toplabel. It is not correct to check syntax after macro expansion.
         | 
| 135 131 | 
             
                result = Coppertone::SpfService.authenticate_email('192.168.218.40', 'test@e9.example.com', '[192.168.218.40]', options)
         | 
| 136 | 
            -
                expect( | 
| 132 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 137 133 | 
             
              end
         | 
| 138 134 |  | 
| 139 135 | 
             
              it 'Example of requiring valid helo in sender policy.  This is a complex policy testing several points at once.' do
         | 
| 140 136 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'test@e10.example.com', 'OEMCOMPUTER', options)
         | 
| 141 | 
            -
                expect( | 
| 137 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 142 138 | 
             
              end
         | 
| 143 139 |  | 
| 144 140 | 
             
              it 'Macro value transformation (splitting on arbitrary characters, reversal, number of right-hand parts to use)' do
         | 
| 145 141 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'philip-gladstone-test@e11.example.com', 'mail.example.com', options)
         | 
| 146 | 
            -
                expect( | 
| 142 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 147 143 | 
             
              end
         | 
| 148 144 |  | 
| 149 145 | 
             
              it 'Multiple delimiters may be specified in a macro expression.   macro-expand = ( "%{" macro-letter transformers *delimiter "}" )                  / "%%" / "%_" / "%-"' do
         | 
| 150 146 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo-bar+zip+quux@e12.example.com', 'mail.example.com', options)
         | 
| 151 | 
            -
                expect( | 
| 147 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 152 148 | 
             
              end
         | 
| 153 149 |  | 
| 154 150 | 
             
            end
         | 
| @@ -5,38 +5,38 @@ describe 'PTR mechanism syntax' do | |
| 5 5 | 
             
                { 'mail.example.com' => [{ 'A' => '1.2.3.4' }], 'e1.example.com' => [{ 'TXT' => 'v=spf1 ptr/0 -all' }], 'e2.example.com' => [{ 'TXT' => 'v=spf1 ptr:example.com -all' }], '4.3.2.1.in-addr.arpa' => [{ 'PTR' => 'e3.example.com' }, { 'PTR' => 'e4.example.com' }, { 'PTR' => 'mail.example.com' }], '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.E.B.A.B.E.F.A.C.ip6.arpa' => [{ 'PTR' => 'e3.example.com' }], 'e3.example.com' => [{ 'TXT' => 'v=spf1 ptr -all' }, { 'A' => '1.2.3.4' }, { 'AAAA' => 'CAFE:BABE::1' }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 ptr -all' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 ptr:' }] }
         | 
| 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 'PTR              = "ptr"    [ ":" domain-spec ]' 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 'Check all validated domain names to see if they end in the <target-name> domain.' 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 'Check all validated domain names to see if they end in the <target-name> domain.' do
         | 
| 22 22 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
         | 
| 23 | 
            -
                expect( | 
| 23 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 24 24 | 
             
              end
         | 
| 25 25 |  | 
| 26 26 | 
             
              it 'Check all validated domain names to see if they end in the <target-name> domain.' do
         | 
| 27 27 | 
             
                # This PTR record does not validate
         | 
| 28 28 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e4.example.com', 'mail.example.com', options)
         | 
| 29 | 
            -
                expect( | 
| 29 | 
            +
                expect([:fail]).to include(result.code)
         | 
| 30 30 | 
             
              end
         | 
| 31 31 |  | 
| 32 32 | 
             
              it 'Check all validated domain names to see if they end in the <target-name> domain.' do
         | 
| 33 33 | 
             
                result = Coppertone::SpfService.authenticate_email('CAFE:BABE::1', 'foo@e3.example.com', 'mail.example.com', options)
         | 
| 34 | 
            -
                expect( | 
| 34 | 
            +
                expect([:pass]).to include(result.code)
         | 
| 35 35 | 
             
              end
         | 
| 36 36 |  | 
| 37 37 | 
             
              it 'domain-spec cannot be empty.' do
         | 
| 38 38 | 
             
                result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
         | 
| 39 | 
            -
                expect( | 
| 39 | 
            +
                expect([:permerror]).to include(result.code)
         | 
| 40 40 | 
             
              end
         | 
| 41 41 |  | 
| 42 42 | 
             
            end
         |