spf 0.0.46 → 0.0.47
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|