spf 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.
data/lib/spf/eval.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'ip'
2
2
  require 'resolv'
3
3
 
4
+ require 'spf/error'
4
5
  require 'spf/model'
5
6
  require 'spf/result'
6
7
 
@@ -46,7 +47,7 @@ class SPF::Server
46
47
  def initialize(options = {})
47
48
  @default_authority_explanation = options[:default_authority_explanation] ||
48
49
  DEFAULT_DEFAULT_AUTHORITY_EXPLANATION
49
- unless @default_authority_explanation.is_a?(SPF::MacroString)
50
+ unless SPF::MacroString === @default_authority_explanation
50
51
  @default_authority_explanation = SPF::MacroString.new({
51
52
  :text => @default_authority_explanation,
52
53
  :server => self,
@@ -122,7 +123,7 @@ class SPF::Server
122
123
  end
123
124
 
124
125
  def dns_lookup(domain, rr_type)
125
- if domain.is_a?(SPF::MacroString)
126
+ if SPF::MacroString === domain
126
127
  domain = domain.expand
127
128
  # Truncate overlong labels at 63 bytes (RFC 4408, 8.1/27)
128
129
  domain.gsub!(/([^.]{63})[^.]+/, "#{$1}")
@@ -132,7 +133,7 @@ class SPF::Server
132
133
 
133
134
  rr_type = self.resource_typeclass_for_rr_type(rr_type)
134
135
 
135
- domain.sub(/^(.*?)\.?$/, $1 ? "#{$1}".downcase : '')
136
+ domain = domain.sub(/\.$/, '').downcase
136
137
 
137
138
  packet = @dns_resolver.getresources(domain, rr_type)
138
139
 
@@ -214,13 +215,13 @@ class SPF::Server
214
215
 
215
216
  if records.empty?
216
217
  # RFC 4408, 4.5/7
217
- raise SPF::NoAcceptableRecordError('No applicable sender policy available')
218
+ raise SPF::NoAcceptableRecordError.new('No applicable sender policy available')
218
219
  end
219
220
 
220
221
  # Discard all records but the highest acceptable version:
221
222
  preferred_record_class = records[0].class
222
223
 
223
- records = records.select { |record| record.is_a?(preferred_record_class) }
224
+ records = records.select { |record| preferred_record_class === record }
224
225
 
225
226
  if records.length != 1
226
227
  # RFC 4408, 4.5/6
@@ -242,7 +243,7 @@ class SPF::Server
242
243
  rr_type = resource_typeclass_for_rr_type(rr_type)
243
244
  records = []
244
245
  packet.each do |rr|
245
- next unless rr.is_a?(rr_type)
246
+ next unless rr_type === rr
246
247
  text = rr.strings.join('')
247
248
  record = false
248
249
  versions.each do |version|
data/lib/spf/model.rb CHANGED
@@ -85,6 +85,15 @@ class SPF::Term
85
85
  ::
86
86
  "
87
87
 
88
+ attr_reader :ip_address, :ip_network, :ipv4_prefix_length, :ipv6_prefix_length
89
+
90
+ def initialize
91
+ @ip_address = nil
92
+ @ip_network = nil
93
+ @ipv4_prefix_length = nil
94
+ @ipv6_prefix_length = nil
95
+ end
96
+
88
97
  def self.new_from_string(text, options = {})
89
98
  #term = SPF::Term.new(options, {:text => text})
90
99
  options[:text] = text
@@ -210,7 +219,7 @@ class SPF::Mech < SPF::Term
210
219
  @parse_text = @text.dup
211
220
  end
212
221
  if self.instance_variable_defined?(:@domain_spec) and
213
- not @domain_spec.is_a?(SPF::MacroString)
222
+ not SPF::MacroString === @domain_spec
214
223
  @domain_spec = SPF::MacroString.new({:text => @domain_spec})
215
224
  end
216
225
  end
@@ -351,7 +360,7 @@ class SPF::Mech < SPF::Term
351
360
  return params
352
361
  end
353
362
 
354
- def match(server, request)
363
+ def match(server, request, want_result = true)
355
364
  server.count_dns_interactive_term(request)
356
365
  return self.match_in_domain(server, request)
357
366
  end
@@ -366,7 +375,7 @@ class SPF::Mech < SPF::Term
366
375
  # No parameters.
367
376
  end
368
377
 
369
- def match(server, request)
378
+ def match(server, request, want_result = true)
370
379
  return true
371
380
  end
372
381
 
@@ -384,7 +393,7 @@ class SPF::Mech < SPF::Term
384
393
  return @domain_spec ? ':' + @domain_spec : nill
385
394
  end
386
395
 
387
- def match(server, request)
396
+ def match(server, request, want_result = true)
388
397
  server.count_dns_interactive_term(request)
389
398
 
390
399
  domain = self.domain(server, request)
@@ -414,8 +423,8 @@ class SPF::Mech < SPF::Term
414
423
  return result
415
424
  end
416
425
 
417
- def match(server, request)
418
- ip_network_v6 = @ip_network.is_a?(IP::V4) ?
426
+ def match(server, request, want_result = true)
427
+ ip_network_v6 = IP::V4 === @ip_network ?
419
428
  SPF::Util.ipv4_address_to_ipv6(@ip_network) :
420
429
  @ip_network
421
430
  return ip_network_v6.contains?(request.ip_address_v6)
@@ -438,7 +447,7 @@ class SPF::Mech < SPF::Term
438
447
  return params
439
448
  end
440
449
 
441
- def match(server, request)
450
+ def match(server, request, want_result = true)
442
451
  return @ip_network.contains?(request.ip_address_v6)
443
452
  end
444
453
 
@@ -456,7 +465,8 @@ class SPF::Mech < SPF::Term
456
465
  return @domain_spec ? ':' + @domain_spec : nil
457
466
  end
458
467
 
459
- def match(server, request)
468
+ def match(server, request, want_result = true)
469
+
460
470
  server.count_dns_interactive_term(request)
461
471
 
462
472
  # Create sub-request with mutated authority domain:
@@ -468,17 +478,18 @@ class SPF::Mech < SPF::Term
468
478
 
469
479
  # Translate result of sub-request (RFC 4408, 5.9):
470
480
 
471
- return true if
472
- result.is_a?(SPF::Result::Pass)
481
+ return false unless want_result
482
+
483
+ return true if SPF::Result::Pass === result
473
484
 
474
485
  return false if
475
- result.is_a?(SPF::Result::Fail) or
476
- result.is_a?(SPF::Result::SoftFail) or
477
- result.is_a?(SPF::Result::Neutral)
486
+ SPF::Result::Fail === result or
487
+ SPF::Result::SoftFail === result or
488
+ SPF::Result::Neutral === result or
478
489
 
479
490
  server.throw_result('permerror', request,
480
491
  "Include domain '#{authority_domain}' has no applicable sender policy") if
481
- result.is_a?(SPF::Result::None)
492
+ SPF::Result::None === result
482
493
 
483
494
  # Propagate any other results (including {Perm,Temp}Error) as-is:
484
495
  raise result
@@ -508,7 +519,7 @@ class SPF::Mech < SPF::Term
508
519
  return params
509
520
  end
510
521
 
511
- def match(server, request)
522
+ def match(server, request, want_result = true)
512
523
 
513
524
  server.count_dns_interactive_term(request)
514
525
 
@@ -549,7 +560,7 @@ class SPF::Mech < SPF::Term
549
560
  return @domain_spec ? ':' + @domain_spec : nil
550
561
  end
551
562
 
552
- def match(server, request)
563
+ def match(server, request, want_result = true)
553
564
  return SPF::Util.valid_domain_for_ip_address(
554
565
  server, request, request.ip_address, self.domain(server, request)) ?
555
566
  true : false
@@ -566,7 +577,7 @@ class SPF::Mod < SPF::Term
566
577
 
567
578
  @parse_text = @text.dup unless @parse_text
568
579
 
569
- if @domain_spec and not @domain_spec.is_a?(SPF::MacroString)
580
+ if @domain_spec and not SPF::MacroString === @domain_spec
570
581
  @domain_spec = SPF::MacroString.new({:text => @domain_spec})
571
582
  end
572
583
  end
@@ -684,7 +695,7 @@ class SPF::Mod < SPF::Term
684
695
  server.count_dns_interactive_term(request)
685
696
 
686
697
  # Only perform redirection if no mechanism matched (RFC 4408, 6.1/1):
687
- return unless result.is_a?(SPF::Result::NeutralByDefault)
698
+ return unless SPF::Result::NeutralByDefault === result
688
699
 
689
700
  # Create sub-request with mutated authorithy domain:
690
701
  authority_domain = @domain_spec.new({:server => server, :request => request})
@@ -694,7 +705,7 @@ class SPF::Mod < SPF::Term
694
705
  result = server.process(sub_request)
695
706
 
696
707
  # Translate result of sub-request (RFC 4408, 6.1/4):
697
- if result.is_a?(SPF::Result::None)
708
+ if SPF::Result::None === result
698
709
  server.throw_result(:permerror, request,
699
710
  "Redirect domain '#{authority_domain}' has no applicable sender policy")
700
711
  end
@@ -789,13 +800,13 @@ class SPF::Record
789
800
  if mod_class
790
801
  # Known modifier.
791
802
  mod = mod_class.new_from_string(mod_text)
792
- if mod.is_a?(SPF::GlobalMod)
803
+ if SPF::GlobalMod === mod
793
804
  # Global modifier.
794
805
  unless @global_mods[mod_name]
795
806
  raise SPF::DuplicateGlobalMod.new("Duplicate global modifier '#{mod_name}' encountered")
796
807
  end
797
808
  @global_mods[mod_name] = mod
798
- elsif mod.is_a?(SPF::PositionalMod)
809
+ elsif SPF::PositionalMod === mod
799
810
  # Positional modifier, queue normally:
800
811
  @terms << mod
801
812
  end
@@ -822,26 +833,26 @@ class SPF::Record
822
833
  raise SPF::OptionRequiredError.new('Request object required for record evaluation') unless request
823
834
  begin
824
835
  @terms.each do |term|
825
- if term.is_a?(SPF::Mech)
836
+ if SPF::Mech === term
837
+ #if term.is_a?(SPF::Mech)
826
838
  # Term is a mechanism.
827
839
  mech = term
828
- if mech.match(server, request)
840
+ if mech.match(server, request, request.ip_address != nil)
829
841
  result_name = RESULTS_BY_QUALIFIER[mech.qualifier]
830
842
  result_class = server.result_class(result_name)
831
843
  result = result_class.new([server, request, "Mechanism '#{term}' matched"])
832
844
  mech.explain(server, request, result)
833
845
  raise result
834
846
  end
835
- elsif term.is_a?(SPF::PositionalMod)
847
+ elsif SPF::PositionalMod === term
836
848
  # Term is a positional modifier.
837
849
  mod = term
838
850
  mod.process(server, request)
839
- elsif term.is_a?(SPF::UnknownMod)
851
+ elsif SPF::UnknownMod === term
840
852
  # Term is an unknown modifier. Ignore it (RFC 4408, 6/3).
841
853
  else
842
854
  # Invalid term object encountered:
843
- raise SPF::UnexpectedTermObjectError.new(
844
- "Unexpected term object '#{term}' encountered.")
855
+ raise SPF::UnexpectedTermObjectError.new("Unexpected term object '#{term}' encountered.")
845
856
  end
846
857
  end
847
858
  rescue SPF::Result => result
@@ -880,6 +891,10 @@ class SPF::Record
880
891
  'v=spf1'
881
892
  end
882
893
 
894
+ def self.version_tag
895
+ 'v=spf1'
896
+ end
897
+
883
898
  def version_tag_pattern
884
899
  " v=spf(1) (?= \\x20+ | $ ) "
885
900
  end
data/lib/spf/request.rb CHANGED
@@ -25,7 +25,7 @@ class SPF::Request
25
25
  @state = {}
26
26
  @versions = options[:versions]
27
27
  @scope = options[:scope] || :mfrom
28
- @scope = @scope.to_sym if @scope.is_a?(String)
28
+ @scope = @scope.to_sym if String === @scope
29
29
  @_authority_domain = options[:authority_domain]
30
30
  @identity = options[:identity]
31
31
  @ip_address = options[:ip_address]
@@ -40,14 +40,13 @@ class SPF::Request
40
40
  raise SPF::InvalidScopeError.new("Invalid scope '#{@scope}'")
41
41
 
42
42
  # Versions:
43
- if self.instance_variable_defined?(:@versions)
44
- if @versions.is_a?(Symbol)
43
+ if @versions
44
+ if Symbol === @versions
45
45
  # Single version specified as a symbol:
46
46
  @versions = [@versions]
47
- elsif not @versions.is_a?(Array)
47
+ elsif not Array === @versions
48
48
  # Something other than symbol or array specified:
49
- raise SPF::InvalidOptionValueError.new(
50
- "'versions' option must be symbol or array")
49
+ raise SPF::InvalidOptionValueError.new("'versions' option must be symbol or array")
51
50
  end
52
51
 
53
52
  # All requested record versions must be supported:
@@ -91,24 +90,21 @@ class SPF::Request
91
90
  @helo_identity ||= @identity
92
91
  end
93
92
 
94
- # IP address:
95
- if [:helo, :mfrom, :pra].find(@scope) and not self.instance_variable_defined?(:@ip_address)
96
- raise SPF::OptionRequiredError.new("Missing required 'ip_address' option")
97
- end
93
+ return unless @ip_address
98
94
 
99
95
  # Ensure ip_address is an IP object:
100
- unless @ip_address.is_a?(IP)
96
+ unless IP === @ip_address
101
97
  @ip_address = IP.new(@ip_address)
102
98
  end
103
99
 
104
100
  # Convert IPv4 address to IPv4-mapped IPv6 address:
105
101
 
106
- if SPF::Util.ipv6_address_is_ipv4_mapped(self.ip_address)
102
+ if SPF::Util.ipv6_address_is_ipv4_mapped(@ip_address)
107
103
  @ip_address_v6 = @ip_address # Accept as IPv6 address as-is
108
104
  @ip_address = SPF::Util.ipv6_address_to_ipv4(@ip_address)
109
- elsif @ip_address.is_a?(IP::V4)
105
+ elsif IP::V4 === @ip_address
110
106
  @ip_address_v6 = SPF::Util.ipv4_address_to_ipv6(@ip_address)
111
- elsif @ip_address.is_a?(IP::V6)
107
+ elsif IP::V6 === @ip_address
112
108
  @ip_address_v6 = @ip_address
113
109
  else
114
110
  raise SPF::InvalidOptionValueError.new("Unexpected IP address version");
@@ -131,7 +127,7 @@ class SPF::Request
131
127
  unless field
132
128
  raise SPF::OptionRequiredError.new('Field name required')
133
129
  end
134
- if value and value === Fixnum
130
+ if value and Fixnum === value
135
131
  @state[field] = 0 unless @state[field]
136
132
  @state[field] += value
137
133
  else
data/lib/spf/result.rb CHANGED
@@ -140,7 +140,7 @@ class SPF::Result < Exception
140
140
 
141
141
  def klass(name=nil)
142
142
  if name
143
- name = name.to_sym if name.is_a?(String)
143
+ name = name.to_sym if String === name
144
144
  return self.RESULT_CLASSES[name]
145
145
  else
146
146
  return name
@@ -150,7 +150,7 @@ class SPF::Result < Exception
150
150
  def isa_by_name(name)
151
151
  suspect_class = self.klass(name)
152
152
  return false unless suspect_class
153
- return self.is_a?(suspect_class)
153
+ return suspect_class === self
154
154
  end
155
155
 
156
156
  def is_code(code)
data/lib/spf/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module SPF
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
4
4
 
5
5
  # vim:sw=2 sts=2
data/spf.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "spf"
8
- s.version = "0.0.1"
8
+ s.version = "0.0.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Andrew Flury", "Julian Mehnle"]
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.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: