spf 0.0.46 → 0.0.47
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 +8 -8
- data/Gemfile +1 -0
- data/Gemfile.lock +7 -0
- data/lib/spf/ext/resolv.rb +161 -0
- data/lib/spf/model.rb +6 -4
- data/lib/spf/request.rb +32 -20
- data/lib/spf/result.rb +5 -7
- data/lib/spf/version.rb +1 -1
- data/lib/spf.rb +1 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/spf_spec.rb +1 -4
- data/spf.gemspec +7 -5
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDRiZjE1NWYxNGNhYTc1N2NjODU5YTA4NmMyZjUxNjliMDIxYjlhZg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MmQ4NGVkMTE2MjdkZDA4NzE1ZGJjY2MzZTljMWE1MGRjYWU5N2FhMQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NjY4NWJjNWQ0MjAwN2VlMmNjYTc5ZjVkNmNlYjQxZmNkNzlhNGRjMGYxNDEx
|
10
|
+
M2M1ZGFmZDkyOTQ5NWJlY2QxYmY1NDdlZDIyZmY1Y2UyOWZlYmNmNDBkOWQ2
|
11
|
+
OTE1OTY5OTlhOTQ5MjhiZDVkMjliZWQ1MzY4NzYwMjgzZjVjM2I=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MmE5ZDY4OGYzY2IzZWRiMjI5NDQ3YThlOWM5MGViZGNiZmViM2U5YjBiMTVj
|
14
|
+
YjkyZGVhZTI0MGMzMDMzY2NhZTBmZjNhYWNjOWE4MTJlMzU5YTk0N2MwMjBh
|
15
|
+
YmMzYzM2N2I2MWIxNTY2MGFhMmM1NDg0ODE3NThjNDkzZjQyZmU=
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -4,6 +4,7 @@ GEM
|
|
4
4
|
addressable (2.3.7)
|
5
5
|
builder (3.2.2)
|
6
6
|
diff-lcs (1.2.5)
|
7
|
+
docile (1.1.5)
|
7
8
|
faraday (0.8.9)
|
8
9
|
multipart-post (~> 1.2.0)
|
9
10
|
git (1.2.9.1)
|
@@ -50,6 +51,11 @@ GEM
|
|
50
51
|
diff-lcs (>= 1.1.3, < 2.0)
|
51
52
|
rspec-mocks (2.99.3)
|
52
53
|
ruby-ip (0.9.3)
|
54
|
+
simplecov (0.9.2)
|
55
|
+
docile (~> 1.1.0)
|
56
|
+
multi_json (~> 1.0)
|
57
|
+
simplecov-html (~> 0.9.0)
|
58
|
+
simplecov-html (0.9.0)
|
53
59
|
|
54
60
|
PLATFORMS
|
55
61
|
ruby
|
@@ -60,3 +66,4 @@ DEPENDENCIES
|
|
60
66
|
rdoc (~> 3)
|
61
67
|
rspec (~> 2.9)
|
62
68
|
ruby-ip (~> 0.9.1)
|
69
|
+
simplecov
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
|
3
|
+
require 'rubygems' # Gem.ruby_version / Gem::Version
|
4
|
+
|
5
|
+
|
6
|
+
# TCP fallback support, redux.
|
7
|
+
# A broken version of this made it into Ruby 1.9.2 in October 2010.
|
8
|
+
# <http://bugs.ruby-lang.org/issues/3835> That version would fail when trying
|
9
|
+
# a second TCP nameserver. This improved version fixes that.
|
10
|
+
# Filed upstream as <http://bugs.ruby-lang.org/issues/8285>.
|
11
|
+
###############################################################################
|
12
|
+
|
13
|
+
class Resolv
|
14
|
+
class DNS
|
15
|
+
def each_resource(name, typeclass, &proc)
|
16
|
+
lazy_initialize
|
17
|
+
protocols = {} # PATCH
|
18
|
+
requesters = {} # PATCH
|
19
|
+
senders = {}
|
20
|
+
#begin # PATCH
|
21
|
+
@config.resolv(name) {|candidate, tout, nameserver, port|
|
22
|
+
msg = Message.new
|
23
|
+
msg.rd = 1
|
24
|
+
msg.add_question(candidate, typeclass)
|
25
|
+
protocol = protocols[candidate] ||= :udp # PATCH
|
26
|
+
requester = requesters[[protocol, nameserver]] ||= case protocol # PATCH
|
27
|
+
when :udp then make_udp_requester # PATCH
|
28
|
+
when :tcp then make_tcp_requester(nameserver, port) # PATCH
|
29
|
+
end # PATCH
|
30
|
+
sender = senders[[candidate, requester, nameserver, port]] ||= # PATCH
|
31
|
+
requester.sender(msg, candidate, nameserver, port) # PATCH
|
32
|
+
reply, reply_name = requester.request(sender, tout)
|
33
|
+
case reply.rcode
|
34
|
+
when RCode::NoError
|
35
|
+
if protocol == :udp and reply.tc == 1 # PATCH
|
36
|
+
# Retry via TCP: # PATCH
|
37
|
+
protocols[candidate] = :tcp # PATCH
|
38
|
+
redo # PATCH
|
39
|
+
else # PATCH
|
40
|
+
extract_resources(reply, reply_name, typeclass, &proc)
|
41
|
+
end # PATCH
|
42
|
+
return
|
43
|
+
when RCode::NXDomain
|
44
|
+
raise Config::NXDomain.new(reply_name.to_s)
|
45
|
+
else
|
46
|
+
raise Config::OtherResolvError.new(reply_name.to_s)
|
47
|
+
end
|
48
|
+
}
|
49
|
+
ensure
|
50
|
+
requesters.each_value { |requester| requester.close } # PATCH
|
51
|
+
#end # PATCH
|
52
|
+
end
|
53
|
+
|
54
|
+
#alias_method :make_udp_requester, :make_requester
|
55
|
+
|
56
|
+
def make_tcp_requester(host, port)
|
57
|
+
return Requester::TCP.new(host, port)
|
58
|
+
rescue Errno::ECONNREFUSED
|
59
|
+
# Treat a refused TCP connection attempt to a nameserver like a timeout,
|
60
|
+
# as Resolv::DNS::Config#resolv considers ResolvTimeout exceptions as a
|
61
|
+
# hint to try the next nameserver:
|
62
|
+
raise ResolvTimeout
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# Fix for (unreported) "nil can't be coerced into Fixnum" TypeError exception
|
69
|
+
# caused by truncated (or otherwise malformed) answer packets.
|
70
|
+
###############################################################################
|
71
|
+
|
72
|
+
class Resolv
|
73
|
+
class DNS
|
74
|
+
class Message
|
75
|
+
class MessageDecoder
|
76
|
+
|
77
|
+
def get_labels(limit=nil)
|
78
|
+
limit = @index if !limit || @index < limit
|
79
|
+
d = []
|
80
|
+
while true
|
81
|
+
case @data[@index] && @data[@index].ord # PATCH
|
82
|
+
when nil # PATCH
|
83
|
+
raise DecodeError.new("truncated or malformed packet") # PATCH
|
84
|
+
when 0
|
85
|
+
@index += 1
|
86
|
+
return d
|
87
|
+
when 192..255
|
88
|
+
idx = self.get_unpack('n')[0] & 0x3fff
|
89
|
+
if limit <= idx
|
90
|
+
raise DecodeError.new("non-backward name pointer")
|
91
|
+
end
|
92
|
+
save_index = @index
|
93
|
+
@index = idx
|
94
|
+
d += self.get_labels(limit)
|
95
|
+
@index = save_index
|
96
|
+
return d
|
97
|
+
else
|
98
|
+
d << self.get_label
|
99
|
+
end
|
100
|
+
end
|
101
|
+
return d
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
# Patch to expose timeout and NXDOMAIN errors to the ultimate caller of
|
111
|
+
# Resolv::DNS rather than swallowing them silently and returning an empty
|
112
|
+
# result set.
|
113
|
+
###############################################################################
|
114
|
+
|
115
|
+
class Resolv
|
116
|
+
class TimeoutError < ResolvError; end
|
117
|
+
class NXDomainError < ResolvError; end
|
118
|
+
|
119
|
+
class DNS
|
120
|
+
class Config
|
121
|
+
attr_accessor :raise_errors # PATCH
|
122
|
+
def resolv(name)
|
123
|
+
candidates = generate_candidates(name)
|
124
|
+
timeouts = generate_timeouts
|
125
|
+
# Collect errors while making the various lookup attempts: # PATCH
|
126
|
+
errors = [] # PATCH
|
127
|
+
begin
|
128
|
+
candidates.each {|candidate|
|
129
|
+
begin
|
130
|
+
timeouts.each {|tout|
|
131
|
+
@nameserver_port.each {|nameserver, port|
|
132
|
+
begin
|
133
|
+
yield candidate, tout, nameserver, port
|
134
|
+
rescue ResolvTimeout
|
135
|
+
end
|
136
|
+
}
|
137
|
+
}
|
138
|
+
# Collect a timeout: # PATCH
|
139
|
+
errors << TimeoutError.new("DNS resolv timeout: #{name}") # PATCH
|
140
|
+
rescue NXDomain
|
141
|
+
# Collect an NXDOMAIN error: # PATCH
|
142
|
+
errors << NXDomainError.new("DNS name does not exist: #{name}") # PATCH
|
143
|
+
end
|
144
|
+
}
|
145
|
+
rescue ResolvError
|
146
|
+
# Allow subclasses to set this to override this behavior without # PATCH
|
147
|
+
# wholesale monkeypatching. # PATCH
|
148
|
+
raise if raise_errors # PATCH
|
149
|
+
# Ignore other errors like vanilla Resolv::DNS does. # PATCH
|
150
|
+
# Perhaps this is not a good idea, though, as it silently swallows # PATCH
|
151
|
+
# SERVFAILs, etc. # PATCH
|
152
|
+
end
|
153
|
+
# If one lookup succeeds, we will have returned within "yield" already. # PATCH
|
154
|
+
# Otherwise we now raise the first error that occurred: # PATCH
|
155
|
+
raise errors.first if not errors.empty? # PATCH
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# vim:sw=2 sts=2
|
data/lib/spf/model.rb
CHANGED
@@ -571,7 +571,7 @@ class SPF::Mech < SPF::Term
|
|
571
571
|
return nil unless server and request
|
572
572
|
authority_domain = self.domain(server, request)
|
573
573
|
sub_request = request.new_sub_request({:authority_domain => authority_domain})
|
574
|
-
return @nested_record = server.
|
574
|
+
return @nested_record = server.selectrecord(sub_request, loose_match)
|
575
575
|
end
|
576
576
|
|
577
577
|
end
|
@@ -786,14 +786,14 @@ class SPF::Mod < SPF::Term
|
|
786
786
|
end
|
787
787
|
|
788
788
|
def process(server, request, result)
|
789
|
+
|
789
790
|
server.count_dns_interactive_term(request)
|
790
791
|
|
791
792
|
# Only perform redirection if no mechanism matched (RFC 4408, 6.1/1):
|
792
793
|
return unless SPF::Result::NeutralByDefault === result
|
793
794
|
|
794
795
|
# Create sub-request with mutated authorithy domain:
|
795
|
-
|
796
|
-
sub_request = request.new_sub_request({:authority_domain => authority_domain})
|
796
|
+
sub_request = request.new_sub_request({:authority_domain => @domain_spec})
|
797
797
|
|
798
798
|
# Process sub-request:
|
799
799
|
result = server.process(sub_request)
|
@@ -807,7 +807,7 @@ class SPF::Mod < SPF::Term
|
|
807
807
|
end
|
808
808
|
|
809
809
|
# Propagate any other results as-is:
|
810
|
-
result
|
810
|
+
raise result
|
811
811
|
end
|
812
812
|
|
813
813
|
def nested_record(server=nil, request=nil)
|
@@ -998,6 +998,8 @@ class SPF::Record
|
|
998
998
|
error(SPF::UnexpectedTermObjectError.new("Unexpected term object '#{term}' encountered."))
|
999
999
|
end
|
1000
1000
|
end
|
1001
|
+
server.throw_result(:neutral_by_default, request,
|
1002
|
+
'Default neutral result due to no mechanism matches')
|
1001
1003
|
rescue SPF::Result => result
|
1002
1004
|
# Process global modifiers in ascending order of precedence:
|
1003
1005
|
global_mods.each do |global_mod|
|
data/lib/spf/request.rb
CHANGED
@@ -22,19 +22,19 @@ class SPF::Request
|
|
22
22
|
DEFAULT_LOCALPART = 'postmaster'
|
23
23
|
|
24
24
|
def initialize(options = {})
|
25
|
-
@opt
|
26
|
-
@state
|
27
|
-
@versions
|
28
|
-
@scope
|
29
|
-
@scope
|
30
|
-
@authority_domain
|
31
|
-
@identity
|
32
|
-
@ip_address
|
33
|
-
@helo_identity
|
34
|
-
@root_request
|
35
|
-
@super_request
|
36
|
-
@record
|
37
|
-
@sub_requests
|
25
|
+
@opt = options
|
26
|
+
@state = {}
|
27
|
+
@versions = options[:versions]
|
28
|
+
@scope = options[:scope] || :mfrom
|
29
|
+
@scope = @scope.to_sym if String === @scope
|
30
|
+
@authority_domain = options[:authority_domain]
|
31
|
+
@identity = options[:identity]
|
32
|
+
@ip_address = options[:ip_address]
|
33
|
+
@helo_identity = options[:helo_identity]
|
34
|
+
@root_request = self
|
35
|
+
@super_request = self
|
36
|
+
@record = nil
|
37
|
+
@sub_requests = []
|
38
38
|
|
39
39
|
# Scope:
|
40
40
|
versions_for_scope = VERSIONS_FOR_SCOPE[@scope] or
|
@@ -42,11 +42,11 @@ class SPF::Request
|
|
42
42
|
|
43
43
|
# Versions:
|
44
44
|
if @versions
|
45
|
-
if
|
46
|
-
# Single version specified as a
|
45
|
+
if Fixnum === @versions
|
46
|
+
# Single version specified as a Fixnum:
|
47
47
|
@versions = [@versions]
|
48
48
|
elsif not Array === @versions
|
49
|
-
# Something other than
|
49
|
+
# Something other than Fixnum or array specified:
|
50
50
|
raise SPF::InvalidOptionValueError.new("'versions' option must be symbol or array")
|
51
51
|
end
|
52
52
|
|
@@ -64,6 +64,14 @@ class SPF::Request
|
|
64
64
|
@versions = versions_for_scope
|
65
65
|
end
|
66
66
|
|
67
|
+
versions = @versions.select {|x| versions_for_scope.include?(x)}
|
68
|
+
if versions.empty?
|
69
|
+
raise SPF::InvalidScopeError.new(
|
70
|
+
"Invalid scope '#{@scope}' for record version(s) #{@versions}"
|
71
|
+
)
|
72
|
+
end
|
73
|
+
@versions = versions
|
74
|
+
|
67
75
|
# Identity:
|
68
76
|
raise SPF::OptionRequiredError.new(
|
69
77
|
"Missing required 'identity' option") unless @identity
|
@@ -128,11 +136,15 @@ class SPF::Request
|
|
128
136
|
unless field
|
129
137
|
raise SPF::OptionRequiredError.new('Field name required')
|
130
138
|
end
|
131
|
-
if value
|
132
|
-
|
133
|
-
|
139
|
+
if value
|
140
|
+
if Fixnum === value
|
141
|
+
@state[field] = 0 unless @state[field]
|
142
|
+
return (@state[field] += value)
|
143
|
+
else
|
144
|
+
return (@state[field] = value)
|
145
|
+
end
|
134
146
|
else
|
135
|
-
@state[field]
|
147
|
+
return @state[field]
|
136
148
|
end
|
137
149
|
end
|
138
150
|
end
|
data/lib/spf/result.rb
CHANGED
@@ -4,7 +4,7 @@ require 'spf/util'
|
|
4
4
|
|
5
5
|
class SPF::Result < Exception
|
6
6
|
|
7
|
-
attr_reader :server, :request
|
7
|
+
attr_reader :server, :request, :result_text
|
8
8
|
|
9
9
|
class SPF::Result::Pass < SPF::Result
|
10
10
|
def code
|
@@ -63,9 +63,6 @@ class SPF::Result < Exception
|
|
63
63
|
# This is a special-case of the Neutral result that is thrown as a default
|
64
64
|
# when "falling off" the end of the record. See SPF::Record.eval().
|
65
65
|
NAME = :neutral_by_default
|
66
|
-
def code
|
67
|
-
:neutral_by_default
|
68
|
-
end
|
69
66
|
end
|
70
67
|
|
71
68
|
class SPF::Result::None < SPF::Result
|
@@ -133,6 +130,7 @@ class SPF::Result < Exception
|
|
133
130
|
unless self.instance_variable_defined?(:@request)
|
134
131
|
raise SPF::OptionRequiredError.new('Request object required')
|
135
132
|
end
|
133
|
+
@result_text = args.shift if args.any?
|
136
134
|
end
|
137
135
|
|
138
136
|
def name
|
@@ -142,20 +140,20 @@ class SPF::Result < Exception
|
|
142
140
|
def klass(name=nil)
|
143
141
|
if name
|
144
142
|
name = name.to_sym if String === name
|
145
|
-
return
|
143
|
+
return RESULT_CLASSES[name]
|
146
144
|
else
|
147
145
|
return name
|
148
146
|
end
|
149
147
|
end
|
150
148
|
|
151
149
|
def isa_by_name(name)
|
152
|
-
suspect_class = self.klass(name)
|
150
|
+
suspect_class = self.klass(name.downcase)
|
153
151
|
return false unless suspect_class
|
154
152
|
return suspect_class === self
|
155
153
|
end
|
156
154
|
|
157
155
|
def is_code(code)
|
158
|
-
return self.isa_by_name(code)
|
156
|
+
return self.isa_by_name(code.downcase)
|
159
157
|
end
|
160
158
|
|
161
159
|
def to_s
|
data/lib/spf/version.rb
CHANGED
data/lib/spf.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
3
|
+
|
1
4
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
6
|
require 'rspec'
|
@@ -8,5 +11,4 @@ require 'spf'
|
|
8
11
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
12
|
|
10
13
|
RSpec.configure do |config|
|
11
|
-
|
12
14
|
end
|
data/spec/spf_spec.rb
CHANGED
data/spf.gemspec
CHANGED
@@ -2,14 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: spf 0.0.47 ruby lib
|
5
6
|
|
6
7
|
Gem::Specification.new do |s|
|
7
8
|
s.name = "spf"
|
8
|
-
s.version = "0.0.
|
9
|
+
s.version = "0.0.47"
|
9
10
|
|
10
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
11
13
|
s.authors = ["Andrew Flury", "Julian Mehnle", "Jacob Rideout"]
|
12
|
-
s.date = "2015-
|
14
|
+
s.date = "2015-04-29"
|
13
15
|
s.description = " An object-oriented Ruby implementation of the Sender Policy Framework (SPF)\n e-mail sender authentication system, fully compliant with RFC 4408.\n"
|
14
16
|
s.email = ["code@agari.com", "aflury@agari.com", "jmehnle@agari.com", "jrideout@agari.com"]
|
15
17
|
s.extra_rdoc_files = [
|
@@ -31,18 +33,18 @@ Gem::Specification.new do |s|
|
|
31
33
|
"lib/spf/result.rb",
|
32
34
|
"lib/spf/util.rb",
|
33
35
|
"lib/spf/version.rb",
|
36
|
+
"lib/spf/ext/resolv.rb",
|
34
37
|
"spec/spec_helper.rb",
|
35
38
|
"spec/spf_spec.rb",
|
36
39
|
"spf.gemspec"
|
37
40
|
]
|
38
41
|
s.homepage = "https://github.com/agaridata/spf-ruby"
|
39
42
|
s.licenses = ["none (all rights reserved)"]
|
40
|
-
s.
|
41
|
-
s.rubygems_version = "1.8.23.2"
|
43
|
+
s.rubygems_version = "2.4.6"
|
42
44
|
s.summary = "Implementation of the Sender Policy Framework"
|
43
45
|
|
44
46
|
if s.respond_to? :specification_version then
|
45
|
-
s.specification_version =
|
47
|
+
s.specification_version = 4
|
46
48
|
|
47
49
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
48
50
|
s.add_runtime_dependency(%q<ruby-ip>, ["~> 0.9.1"])
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.47
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Flury
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-
|
13
|
+
date: 2015-04-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: ruby-ip
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- lib/spf.rb
|
104
104
|
- lib/spf/error.rb
|
105
105
|
- lib/spf/eval.rb
|
106
|
+
- lib/spf/ext/resolv.rb
|
106
107
|
- lib/spf/macro_string.rb
|
107
108
|
- lib/spf/model.rb
|
108
109
|
- lib/spf/request.rb
|
@@ -134,7 +135,7 @@ requirements: []
|
|
134
135
|
rubyforge_project:
|
135
136
|
rubygems_version: 2.4.4
|
136
137
|
signing_key:
|
137
|
-
specification_version:
|
138
|
+
specification_version: 4
|
138
139
|
summary: Implementation of the Sender Policy Framework
|
139
140
|
test_files: []
|
140
141
|
has_rdoc:
|