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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/LICENSE +1 -1
  4. data/Rakefile +2 -2
  5. data/coppertone.gemspec +1 -0
  6. data/lib/coppertone/directive.rb +14 -7
  7. data/lib/coppertone/error.rb +24 -5
  8. data/lib/coppertone/ip_address_wrapper.rb +9 -6
  9. data/lib/coppertone/macro_context.rb +7 -13
  10. data/lib/coppertone/macro_string/macro_expand.rb +3 -2
  11. data/lib/coppertone/macro_string.rb +12 -5
  12. data/lib/coppertone/mechanism/a.rb +5 -1
  13. data/lib/coppertone/mechanism/all.rb +11 -4
  14. data/lib/coppertone/mechanism/cidr_parser.rb +1 -1
  15. data/lib/coppertone/mechanism/domain_spec_mechanism.rb +9 -0
  16. data/lib/coppertone/mechanism/domain_spec_optional.rb +1 -0
  17. data/lib/coppertone/mechanism/domain_spec_required.rb +1 -0
  18. data/lib/coppertone/mechanism/domain_spec_with_dual_cidr.rb +1 -0
  19. data/lib/coppertone/mechanism/exists.rb +5 -1
  20. data/lib/coppertone/mechanism/include.rb +17 -5
  21. data/lib/coppertone/mechanism/ip4.rb +5 -1
  22. data/lib/coppertone/mechanism/ip6.rb +5 -1
  23. data/lib/coppertone/mechanism/ip_mechanism.rb +9 -1
  24. data/lib/coppertone/mechanism/mx.rb +5 -1
  25. data/lib/coppertone/mechanism/ptr.rb +5 -1
  26. data/lib/coppertone/mechanism.rb +25 -2
  27. data/lib/coppertone/modifier/base.rb +1 -0
  28. data/lib/coppertone/modifier/exp.rb +6 -2
  29. data/lib/coppertone/modifier/redirect.rb +5 -1
  30. data/lib/coppertone/modifier/unknown.rb +6 -1
  31. data/lib/coppertone/modifier.rb +11 -2
  32. data/lib/coppertone/null_macro_context.rb +23 -0
  33. data/lib/coppertone/qualifier.rb +8 -0
  34. data/lib/coppertone/record.rb +33 -41
  35. data/lib/coppertone/record_finder.rb +3 -1
  36. data/lib/coppertone/record_term_parser.rb +30 -0
  37. data/lib/coppertone/request.rb +3 -1
  38. data/lib/coppertone/request_context.rb +1 -2
  39. data/lib/coppertone/result.rb +6 -2
  40. data/lib/coppertone/utils/validated_domain_finder.rb +6 -4
  41. data/lib/coppertone/version.rb +1 -1
  42. data/lib/coppertone.rb +3 -1
  43. data/spec/directive_spec.rb +13 -0
  44. data/spec/ip_address_wrapper_spec.rb +3 -0
  45. data/spec/macro_string_spec.rb +20 -0
  46. data/spec/mechanism/a_spec.rb +22 -7
  47. data/spec/mechanism/all_spec.rb +8 -0
  48. data/spec/mechanism/exists_spec.rb +14 -6
  49. data/spec/mechanism/include_spec.rb +13 -1
  50. data/spec/mechanism/ip4_spec.rb +15 -5
  51. data/spec/mechanism/ip6_spec.rb +18 -11
  52. data/spec/mechanism/mx_spec.rb +56 -0
  53. data/spec/mechanism/ptr_spec.rb +7 -0
  54. data/spec/modifier/exp_spec.rb +10 -0
  55. data/spec/modifier/redirect_spec.rb +10 -0
  56. data/spec/open_spf/ALL_mechanism_syntax_spec.rb +6 -6
  57. data/spec/open_spf/A_mechanism_syntax_spec.rb +30 -30
  58. data/spec/open_spf/EXISTS_mechanism_syntax_spec.rb +8 -8
  59. data/spec/open_spf/IP4_mechanism_syntax_spec.rb +10 -10
  60. data/spec/open_spf/IP6_mechanism_syntax_spec.rb +10 -10
  61. data/spec/open_spf/Include_mechanism_semantics_and_syntax_spec.rb +10 -10
  62. data/spec/open_spf/Initial_processing_spec.rb +21 -14
  63. data/spec/open_spf/MX_mechanism_syntax_spec.rb +22 -22
  64. data/spec/open_spf/Macro_expansion_rules_spec.rb +26 -30
  65. data/spec/open_spf/PTR_mechanism_syntax_spec.rb +7 -7
  66. data/spec/open_spf/Processing_limits_spec.rb +12 -12
  67. data/spec/open_spf/Record_evaluation_spec.rb +13 -13
  68. data/spec/open_spf/Record_lookup_spec.rb +8 -8
  69. data/spec/open_spf/Selecting_records_spec.rb +11 -11
  70. data/spec/open_spf/Semantics_of_exp_and_other_modifiers_spec.rb +27 -25
  71. data/spec/open_spf/Test_cases_from_implementation_bugs_spec.rb +2 -2
  72. data/spec/record_spec.rb +34 -13
  73. data/spec/request_context_spec.rb +1 -1
  74. data/spec/rfc7208-tests.yml +10 -0
  75. metadata +59 -48
  76. data/lib/coppertone/dns/error.rb +0 -9
  77. data/lib/coppertone/dns/mock_client.rb +0 -106
  78. data/lib/coppertone/dns/resolv_client.rb +0 -110
  79. data/lib/coppertone/dns.rb +0 -3
  80. data/lib/resolv/dns/resource/in/spf.rb +0 -15
  81. data/spec/dns/resolv_client_spec.rb +0 -307
@@ -128,6 +128,14 @@ tests:
128
128
  host: 192.0.2.3
129
129
  mailfrom: "foobar@ctrl.example.com"
130
130
  result: permerror
131
+ two-spaces:
132
+ description: >-
133
+ ABNF for term separation is one or more spaces, not just one.
134
+ spec: 4.6.1
135
+ helo: hosed
136
+ host: 1.2.3.4
137
+ mailfrom: "actually@fine.example.com"
138
+ result: fail
131
139
  zonedata:
132
140
  example.com:
133
141
  - TIMEOUT
@@ -151,6 +159,8 @@ zonedata:
151
159
  ctrl.example.com:
152
160
  - TXT: "v=spf1 a:ctrl.example.com\x0dptr -all"
153
161
  - A: 192.0.2.3
162
+ fine.example.com:
163
+ - TXT: "v=spf1 a -all"
154
164
  ---
155
165
  description: Record lookup
156
166
  tests:
metadata CHANGED
@@ -1,124 +1,136 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coppertone
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-19 00:00:00.000000000 Z
11
+ date: 2014-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: i18n
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
- - - ">="
16
+ - - '>='
18
17
  - !ruby/object:Gem::Version
19
18
  version: '0'
20
- type: :runtime
19
+ name: dns_adapter
21
20
  prerelease: false
21
+ type: :runtime
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: addressable
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
- - - ">="
30
+ - - '>='
32
31
  - !ruby/object:Gem::Version
33
32
  version: '0'
33
+ name: i18n
34
+ prerelease: false
34
35
  type: :runtime
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ name: addressable
35
48
  prerelease: false
49
+ type: :runtime
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
- - - ">="
52
+ - - '>='
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
- name: activesupport
43
56
  requirement: !ruby/object:Gem::Requirement
44
57
  requirements:
45
- - - ">="
58
+ - - '>='
46
59
  - !ruby/object:Gem::Version
47
60
  version: '3.0'
48
- type: :runtime
61
+ name: activesupport
49
62
  prerelease: false
63
+ type: :runtime
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - ">="
66
+ - - '>='
53
67
  - !ruby/object:Gem::Version
54
68
  version: '3.0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: bundler
57
70
  requirement: !ruby/object:Gem::Requirement
58
71
  requirements:
59
- - - ">="
72
+ - - '>='
60
73
  - !ruby/object:Gem::Version
61
74
  version: '0'
62
- type: :development
75
+ name: bundler
63
76
  prerelease: false
77
+ type: :development
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - ">="
80
+ - - '>='
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
- name: rake
71
84
  requirement: !ruby/object:Gem::Requirement
72
85
  requirements:
73
- - - "~>"
86
+ - - ~>
74
87
  - !ruby/object:Gem::Version
75
88
  version: '10.0'
76
- type: :development
89
+ name: rake
77
90
  prerelease: false
91
+ type: :development
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - "~>"
94
+ - - ~>
81
95
  - !ruby/object:Gem::Version
82
96
  version: '10.0'
83
97
  - !ruby/object:Gem::Dependency
84
- name: rspec
85
98
  requirement: !ruby/object:Gem::Requirement
86
99
  requirements:
87
- - - ">="
100
+ - - '>='
88
101
  - !ruby/object:Gem::Version
89
102
  version: '3.0'
90
- type: :development
103
+ name: rspec
91
104
  prerelease: false
105
+ type: :development
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
- - - ">="
108
+ - - '>='
95
109
  - !ruby/object:Gem::Version
96
110
  version: '3.0'
97
111
  - !ruby/object:Gem::Dependency
98
- name: rubocop
99
112
  requirement: !ruby/object:Gem::Requirement
100
113
  requirements:
101
- - - ">="
114
+ - - '>='
102
115
  - !ruby/object:Gem::Version
103
116
  version: '0'
104
- type: :development
117
+ name: rubocop
105
118
  prerelease: false
119
+ type: :development
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
- - - ">="
122
+ - - '>='
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
111
- description: Coppertone includes tools for parsing SPF DNS records, evaluating the
112
- result of SPF checks for received emails, and creating appropriate email headers
113
- from SPF results.
125
+ description: Coppertone includes tools for parsing SPF DNS records, evaluating the result of SPF checks for received emails, and creating appropriate email headers from SPF results.
114
126
  email:
115
127
  - peter.m.goldstein@gmail.com
116
128
  executables: []
117
129
  extensions: []
118
130
  extra_rdoc_files: []
119
131
  files:
120
- - ".gitignore"
121
- - ".travis.yml"
132
+ - .gitignore
133
+ - .travis.yml
122
134
  - Gemfile
123
135
  - LICENSE
124
136
  - README.md
@@ -127,10 +139,6 @@ files:
127
139
  - lib/coppertone.rb
128
140
  - lib/coppertone/class_builder.rb
129
141
  - lib/coppertone/directive.rb
130
- - lib/coppertone/dns.rb
131
- - lib/coppertone/dns/error.rb
132
- - lib/coppertone/dns/mock_client.rb
133
- - lib/coppertone/dns/resolv_client.rb
134
142
  - lib/coppertone/domain_spec.rb
135
143
  - lib/coppertone/error.rb
136
144
  - lib/coppertone/ip_address_wrapper.rb
@@ -161,10 +169,12 @@ files:
161
169
  - lib/coppertone/modifier/exp.rb
162
170
  - lib/coppertone/modifier/redirect.rb
163
171
  - lib/coppertone/modifier/unknown.rb
172
+ - lib/coppertone/null_macro_context.rb
164
173
  - lib/coppertone/qualifier.rb
165
174
  - lib/coppertone/record.rb
166
175
  - lib/coppertone/record_evaluator.rb
167
176
  - lib/coppertone/record_finder.rb
177
+ - lib/coppertone/record_term_parser.rb
168
178
  - lib/coppertone/request.rb
169
179
  - lib/coppertone/request_context.rb
170
180
  - lib/coppertone/request_count_limiter.rb
@@ -178,9 +188,7 @@ files:
178
188
  - lib/coppertone/utils/ip_in_domain_checker.rb
179
189
  - lib/coppertone/utils/validated_domain_finder.rb
180
190
  - lib/coppertone/version.rb
181
- - lib/resolv/dns/resource/in/spf.rb
182
191
  - spec/directive_spec.rb
183
- - spec/dns/resolv_client_spec.rb
184
192
  - spec/domain_spec_spec.rb
185
193
  - spec/ip_address_wrapper_spec.rb
186
194
  - spec/macro_context_spec.rb
@@ -197,6 +205,8 @@ files:
197
205
  - spec/mechanism/mx_spec.rb
198
206
  - spec/mechanism/ptr_spec.rb
199
207
  - spec/mechanism_spec.rb
208
+ - spec/modifier/exp_spec.rb
209
+ - spec/modifier/redirect_spec.rb
200
210
  - spec/modifier_spec.rb
201
211
  - spec/open_spf/ALL_mechanism_syntax_spec.rb
202
212
  - spec/open_spf/A_mechanism_syntax_spec.rb
@@ -233,29 +243,28 @@ homepage: https://github.com/petergoldstein/coppertone
233
243
  licenses:
234
244
  - Apache
235
245
  metadata: {}
236
- post_install_message:
246
+ post_install_message:
237
247
  rdoc_options: []
238
248
  require_paths:
239
249
  - lib
240
250
  required_ruby_version: !ruby/object:Gem::Requirement
241
251
  requirements:
242
- - - ">="
252
+ - - '>='
243
253
  - !ruby/object:Gem::Version
244
254
  version: '0'
245
255
  required_rubygems_version: !ruby/object:Gem::Requirement
246
256
  requirements:
247
- - - ">="
257
+ - - '>='
248
258
  - !ruby/object:Gem::Version
249
259
  version: '0'
250
260
  requirements: []
251
- rubyforge_project:
252
- rubygems_version: 2.4.1
253
- signing_key:
261
+ rubyforge_project:
262
+ rubygems_version: 2.1.9
263
+ signing_key:
254
264
  specification_version: 4
255
265
  summary: A Sender Policy Framework (SPF) toolkit
256
266
  test_files:
257
267
  - spec/directive_spec.rb
258
- - spec/dns/resolv_client_spec.rb
259
268
  - spec/domain_spec_spec.rb
260
269
  - spec/ip_address_wrapper_spec.rb
261
270
  - spec/macro_context_spec.rb
@@ -272,6 +281,8 @@ test_files:
272
281
  - spec/mechanism/mx_spec.rb
273
282
  - spec/mechanism/ptr_spec.rb
274
283
  - spec/mechanism_spec.rb
284
+ - spec/modifier/exp_spec.rb
285
+ - spec/modifier/redirect_spec.rb
275
286
  - spec/modifier_spec.rb
276
287
  - spec/open_spf/ALL_mechanism_syntax_spec.rb
277
288
  - spec/open_spf/A_mechanism_syntax_spec.rb
@@ -1,9 +0,0 @@
1
- require 'coppertone/error'
2
-
3
- module Coppertone
4
- module DNS
5
- class Error < ::Coppertone::TemperrorError; end
6
- class TimeoutError < Error; end
7
- class NXDomainError < Error; end
8
- end
9
- end
@@ -1,106 +0,0 @@
1
- require 'coppertone/error'
2
-
3
- module Coppertone
4
- module DNS
5
- # A mock client for use in tests.
6
- class MockClient
7
- def initialize(zone_data)
8
- @zone_data = {}
9
- zone_data.each do |k, v|
10
- @zone_data[k.downcase] = v.dup
11
- end
12
- end
13
-
14
- def fetch_a_records(domain)
15
- fetch_records(domain, 'A')
16
- end
17
-
18
- def fetch_aaaa_records(domain)
19
- fetch_records(domain, 'AAAA')
20
- end
21
-
22
- def fetch_mx_records(domain)
23
- fetch_records(domain, 'MX')
24
- end
25
-
26
- def fetch_ptr_records(arpa_address)
27
- fetch_records(arpa_address, 'PTR')
28
- end
29
-
30
- def fetch_txt_records(domain)
31
- fetch_records(domain, 'TXT')
32
- end
33
-
34
- def fetch_spf_records(domain)
35
- fetch_records(domain, 'SPF')
36
- end
37
-
38
- def fetch_records(domain, type)
39
- record_set = find_records_for_domain(domain)
40
- return [] if record_set.empty?
41
- records = records_for_type(record_set, type)
42
- if records.empty?
43
- check_for_timeout(record_set)
44
- else
45
- formatted_records(records, type)
46
- end
47
- end
48
-
49
- private
50
-
51
- def normalize_domain(domain)
52
- return if domain.blank?
53
- domain = domain[0...-1] if domain[domain.length - 1] == '.'
54
- domain.downcase
55
- end
56
-
57
- def find_records_for_domain(domain)
58
- return [] if domain.blank?
59
- @zone_data[normalize_domain(domain)] || []
60
- end
61
-
62
- def records_for_type(record_set, type)
63
- record_set.select do |r|
64
- r.is_a?(Hash) && r[type] && r[type] != 'NONE'
65
- end
66
- end
67
-
68
- TIMEOUT = 'TIMEOUT'
69
- def check_for_timeout(record_set)
70
- return [] if record_set.select { |r| r == TIMEOUT }.empty?
71
- fail Coppertone::DNS::TimeoutError
72
- end
73
-
74
- RECORD_TYPE_TO_ATTR_NAME_MAP = {
75
- 'A' => :address,
76
- 'AAAA' => :address,
77
- 'MX' => :exchange,
78
- 'PTR' => :name,
79
- 'SPF' => :text,
80
- 'TXT' => :text
81
- }
82
-
83
- def formatted_records(records, type)
84
- records.map do |r|
85
- val = r[type]
86
- fail Coppertone::DNS::TimeoutError if val == TIMEOUT
87
- val = normalize_value(val, type)
88
- {
89
- type: type,
90
- RECORD_TYPE_TO_ATTR_NAME_MAP[type] => val
91
- }
92
- end
93
- end
94
-
95
- def normalize_value(value, type)
96
- if type == 'MX' && value.is_a?(Array)
97
- value.last
98
- elsif (type == 'TXT' || type == 'SPF') && value.is_a?(Array)
99
- value.join('')
100
- else
101
- value
102
- end
103
- end
104
- end
105
- end
106
- end
@@ -1,110 +0,0 @@
1
- require 'resolv'
2
- require 'resolv/dns/resource/in/spf'
3
-
4
- module Coppertone
5
- module DNS
6
- # An adapter client for the internal Resolv DNS client.
7
- class ResolvClient
8
- def fetch_a_records(domain)
9
- fetch_a_type_records(domain, 'A')
10
- end
11
-
12
- def fetch_aaaa_records(domain)
13
- fetch_a_type_records(domain, 'AAAA')
14
- end
15
-
16
- def fetch_mx_records(domain)
17
- fetch_records(domain, 'MX') do |record|
18
- {
19
- type: 'MX',
20
- exchange: record.exchange.to_s
21
- }
22
- end
23
- end
24
-
25
- def fetch_ptr_records(arpa_address)
26
- fetch_records(arpa_address, 'PTR') do |record|
27
- {
28
- type: 'PTR',
29
- name: record.name.to_s
30
- }
31
- end
32
- end
33
-
34
- def fetch_txt_records(domain)
35
- fetch_txt_type_records(domain, 'TXT')
36
- end
37
-
38
- def fetch_spf_records(domain)
39
- fetch_txt_type_records(domain, 'SPF')
40
- end
41
-
42
- private
43
-
44
- def fetch_a_type_records(domain, type)
45
- fetch_records(domain, type) do |record|
46
- {
47
- type: type,
48
- address: record.address.to_s
49
- }
50
- end
51
- end
52
-
53
- def fetch_txt_type_records(domain, type)
54
- fetch_records(domain, type) do |record|
55
- {
56
- type: type,
57
- # Use strings.join('') to avoid JRuby issue where
58
- # data only returns the first string
59
- text: record.strings.join('')
60
- }
61
- end
62
- end
63
-
64
- def fetch_records(domain, type, &block)
65
- records = dns_lookup(domain, type)
66
- records.map(&block)
67
- end
68
-
69
- TRAILING_DOT_REGEXP = /\.\z/
70
- def normalize_domain(domain)
71
- (domain.sub(TRAILING_DOT_REGEXP, '') || domain).downcase
72
- end
73
-
74
- def dns_lookup(domain, rr_type)
75
- domain = normalize_domain(domain)
76
- resources = getresources(domain, rr_type)
77
-
78
- unless resources
79
- fail Coppertone::DNS::Error,
80
- "Unknown error on DNS '#{rr_type}' lookup of '#{domain}'"
81
- end
82
-
83
- resources
84
- end
85
-
86
- def getresources(domain, rr_type)
87
- rr_class = self.class.type_class(rr_type)
88
- dns_resolver.getresources(domain, rr_class)
89
- rescue Resolv::ResolvTimeout
90
- raise Coppertone::DNS::TimeoutError,
91
- "Time-out on DNS '#{rr_type}' lookup of '#{domain}'"
92
- rescue Resolv::ResolvError
93
- raise Coppertone::DNS::Error, "Error on DNS lookup of '#{domain}'"
94
- end
95
-
96
- SUPPORTED_RR_TYPES = %w(A AAAA MX PTR TXT SPF)
97
- def self.type_class(rr_type)
98
- if SUPPORTED_RR_TYPES.include?(rr_type)
99
- Resolv::DNS::Resource::IN.const_get(rr_type)
100
- else
101
- fail ArgumentError, "Unknown RR type: #{rr_type}"
102
- end
103
- end
104
-
105
- def dns_resolver
106
- @dns_resolver ||= Resolv::DNS.new
107
- end
108
- end
109
- end
110
- end
@@ -1,3 +0,0 @@
1
- require 'coppertone/dns/error'
2
- require 'coppertone/dns/resolv_client'
3
- require 'coppertone/dns/mock_client'
@@ -1,15 +0,0 @@
1
- require 'resolv'
2
-
3
- class Resolv
4
- class DNS
5
- class Resource
6
- module IN
7
- # DNS record type for SPF records
8
- class SPF < Resolv::DNS::Resource::IN::TXT
9
- # resolv.rb doesn't define an SPF resource type.
10
- TypeValue = 99 # rubocop:disable Style/ConstantName
11
- end
12
- end
13
- end
14
- end
15
- end