spf 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/spf/eval.rb CHANGED
@@ -69,6 +69,8 @@ class SPF::Server
69
69
  @max_void_dns_lookups = options[:max_void_dns_lookups] ||
70
70
  DEFAULT_MAX_VOID_DNS_LOOKUPS
71
71
 
72
+ @raise_exceptions = options.has_key?(:raise_exceptions) ? options[:raise_exceptions] : true
73
+
72
74
  end
73
75
 
74
76
  def result_class(name = nil)
@@ -249,7 +251,7 @@ class SPF::Server
249
251
  versions.each do |version|
250
252
  klass = RECORD_CLASSES_BY_VERSION[version]
251
253
  begin
252
- record = klass.new_from_string(text)
254
+ record = klass.new_from_string(text, {:raise_exceptions => @raise_exceptions})
253
255
  rescue SPF::InvalidRecordVersionError
254
256
  # Ignore non-SPF and unknown-version records.
255
257
  # Propagate other errors (including syntax errors), though.
data/lib/spf/model.rb CHANGED
@@ -86,12 +86,19 @@ class SPF::Term
86
86
  "
87
87
 
88
88
  attr_reader :ip_address, :ip_network, :ipv4_prefix_length, :ipv6_prefix_length
89
+ attr_accessor :errors
89
90
 
90
- def initialize
91
+ def initialize(options = {})
91
92
  @ip_address = nil
92
93
  @ip_network = nil
93
94
  @ipv4_prefix_length = nil
94
95
  @ipv6_prefix_length = nil
96
+ @errors = []
97
+ end
98
+
99
+ def error(exception)
100
+ @errors << exception
101
+ raise exception
95
102
  end
96
103
 
97
104
  def self.new_from_string(text, options = {})
@@ -118,7 +125,7 @@ class SPF::Term
118
125
  @ip_address = $1
119
126
  elsif required
120
127
  raise SPF::TermIPv4AddressExpectedError.new(
121
- "Missing required IPv4 address in '#{@text}'");
128
+ "Missing required IPv4 address in '#{@text}'")
122
129
  end
123
130
  end
124
131
 
@@ -198,6 +205,8 @@ end
198
205
 
199
206
  class SPF::Mech < SPF::Term
200
207
 
208
+ attr_reader :ip_netblocks, :errors
209
+
201
210
  DEFAULT_QUALIFIER = SPF::Record::DEFAULT_QUALIFIER
202
211
  def default_ipv4_prefix_length; 32; end
203
212
  def default_ipv6_prefix_length; 128; end
@@ -213,7 +222,10 @@ class SPF::Mech < SPF::Term
213
222
  }
214
223
 
215
224
  def initialize(options)
216
- super()
225
+ super(options)
226
+
227
+ @ip_netblocks = []
228
+
217
229
  @text = options[:text]
218
230
  if not self.instance_variable_defined?(:@parse_text)
219
231
  @parse_text = @text.dup
@@ -239,7 +251,7 @@ class SPF::Mech < SPF::Term
239
251
  @qualifier = $1 or DEFAULT_QUALIFIER
240
252
  else
241
253
  raise SPF::InvalidMechQualifierError.new(
242
- "Invalid qualifier encountered in '#{@text}'")
254
+ "Invalid qualifier encountered in '#{@text}'")
243
255
  end
244
256
  end
245
257
 
@@ -247,8 +259,7 @@ class SPF::Mech < SPF::Term
247
259
  if @parse_text.sub!(/^ (#{NAME_PATTERN}) (?: : (?=.) )? /x, '')
248
260
  @name = $1
249
261
  else
250
- raise SPF::InvalidMech.new(
251
- "Unexpected mechanism encountered in '#{@text}'")
262
+ raise SPF::InvalidMech.new("Unexpected mechanism encountered in '#{@text}'")
252
263
  end
253
264
  end
254
265
 
@@ -302,9 +313,11 @@ class SPF::Mech < SPF::Term
302
313
  rrs.each do |rr|
303
314
  if rr.type == 'A'
304
315
  network = IP.new("#{rr.address}/#{ipv4_prefix_length}")
316
+ @ip_netblocks << network
305
317
  return true if network.contains?(request.ip_address)
306
318
  elsif rr.type == 'AAAA'
307
319
  network = IP.new("#{rr.address}/#{ipv6_prefix_length}")
320
+ @ip_netblocks << network
308
321
  return true if network.contains?(request.ip_address_v6)
309
322
  elsif rr.type == 'CNAME'
310
323
  # Ignore -- we should have gotten the A/AAAA records anyway.
@@ -339,7 +352,7 @@ class SPF::Mech < SPF::Term
339
352
 
340
353
  class SPF::Mech::A < SPF::Mech
341
354
 
342
- NAME = 'a'
355
+ NAME = 'a'
343
356
 
344
357
  def parse_params
345
358
  self.parse_domain_spec
@@ -369,7 +382,7 @@ class SPF::Mech < SPF::Term
369
382
 
370
383
  class SPF::Mech::All < SPF::Mech
371
384
 
372
- NAME = 'all'
385
+ NAME = 'all'
373
386
 
374
387
  def parse_params
375
388
  # No parameters.
@@ -383,7 +396,7 @@ class SPF::Mech < SPF::Term
383
396
 
384
397
  class SPF::Mech::Exists < SPF::Mech
385
398
 
386
- NAME = 'exists'
399
+ NAME = 'exists'
387
400
 
388
401
  def parse_params
389
402
  self.parse_domain_spec(true)
@@ -396,12 +409,15 @@ class SPF::Mech < SPF::Term
396
409
  def match(server, request, want_result = true)
397
410
  server.count_dns_interactive_term(request)
398
411
 
412
+ # Other method of denoting "potentially ~infinite" netblocks?
413
+ @ip_netblocks << nil
399
414
  domain = self.domain(server, request)
400
415
  packet = server.dns_lookup(domain, 'A')
401
416
  rrs = (packet.answer or server.count_void_dns_lookup(request))
402
417
  rrs.each do |rr|
403
418
  return true if rr.type == 'A'
404
419
  end
420
+
405
421
  return false
406
422
  end
407
423
 
@@ -409,7 +425,7 @@ class SPF::Mech < SPF::Term
409
425
 
410
426
  class SPF::Mech::IP4 < SPF::Mech
411
427
 
412
- NAME = 'ip4'
428
+ NAME = 'ip4'
413
429
 
414
430
  def parse_params
415
431
  self.parse_ipv4_network(true)
@@ -427,6 +443,7 @@ class SPF::Mech < SPF::Term
427
443
  ip_network_v6 = IP::V4 === @ip_network ?
428
444
  SPF::Util.ipv4_address_to_ipv6(@ip_network) :
429
445
  @ip_network
446
+ @ip_netblocks << @ip_network
430
447
  return ip_network_v6.contains?(request.ip_address_v6)
431
448
  end
432
449
 
@@ -434,7 +451,7 @@ class SPF::Mech < SPF::Term
434
451
 
435
452
  class SPF::Mech::IP6 < SPF::Mech
436
453
 
437
- NAME = 'ip6'
454
+ NAME = 'ip6'
438
455
 
439
456
  def parse_params
440
457
  self.parse_ipv6_network(true)
@@ -448,6 +465,7 @@ class SPF::Mech < SPF::Term
448
465
  end
449
466
 
450
467
  def match(server, request, want_result = true)
468
+ @ip_netblocks << @ip_network
451
469
  return @ip_network.contains?(request.ip_address_v6)
452
470
  end
453
471
 
@@ -455,7 +473,14 @@ class SPF::Mech < SPF::Term
455
473
 
456
474
  class SPF::Mech::Include < SPF::Mech
457
475
 
458
- NAME = 'include'
476
+ NAME = 'include'
477
+
478
+ attr_accessor :nested_record
479
+
480
+ def intitialize(options = {})
481
+ super(options)
482
+ @nested_record = nil
483
+ end
459
484
 
460
485
  def parse_params
461
486
  self.parse_domain_spec(true)
@@ -476,6 +501,8 @@ class SPF::Mech < SPF::Term
476
501
  # Process sub-request:
477
502
  result = server.process(sub_request)
478
503
 
504
+ @nested_record = sub_request.record
505
+
479
506
  # Translate result of sub-request (RFC 4408, 5.9):
480
507
 
481
508
  return false unless want_result
@@ -498,7 +525,7 @@ class SPF::Mech < SPF::Term
498
525
 
499
526
  class SPF::Mech::MX < SPF::Mech
500
527
 
501
- NAME = 'mx'
528
+ NAME = 'mx'
502
529
 
503
530
  def parse_params
504
531
  self.parse_domain_spec
@@ -550,7 +577,7 @@ class SPF::Mech < SPF::Term
550
577
  end
551
578
 
552
579
  class SPF::Mech::PTR < SPF::Mech
553
- NAME = 'ptr'
580
+ NAME = 'ptr'
554
581
 
555
582
  def parse_params
556
583
  self.parse_domain_spec
@@ -678,11 +705,16 @@ class SPF::Mod < SPF::Term
678
705
 
679
706
  class SPF::Mod::Redirect < SPF::GlobalMod
680
707
 
681
- attr_reader :domain_spec
708
+ attr_reader :domain_spec, :included_record
682
709
 
683
- NAME = 'redirect'
684
- PRECEDENCE = 0.8
710
+ NAME = 'redirect'
711
+ PRECEDENCE = 0.8
685
712
 
713
+ def initialize(options = {})
714
+ super(options)
715
+ @nested_record = nil
716
+ end
717
+
686
718
  def parse_params
687
719
  self.parse_domain_spec(true)
688
720
  end
@@ -704,6 +736,8 @@ class SPF::Mod < SPF::Term
704
736
  # Process sub-request:
705
737
  result = server.process(sub_request)
706
738
 
739
+ @nested_record = sub_request.record
740
+
707
741
  # Translate result of sub-request (RFC 4408, 6.1/4):
708
742
  if SPF::Result::None === result
709
743
  server.throw_result(:permerror, request,
@@ -718,7 +752,7 @@ end
718
752
 
719
753
  class SPF::Record
720
754
 
721
- attr_reader :terms, :text
755
+ attr_reader :terms, :text, :errors, :ip_netblocks
722
756
 
723
757
  RESULTS_BY_QUALIFIER = {
724
758
  '' => :pass,
@@ -730,9 +764,12 @@ class SPF::Record
730
764
 
731
765
  def initialize(options)
732
766
  super()
733
- @parse_text = (@text = options[:text] if not self.instance_variable_defined?(:@parse_text)).dup
734
- @terms ||= []
735
- @global_mods ||= {}
767
+ @parse_text = (@text = options[:text] if not self.instance_variable_defined?(:@parse_text)).dup
768
+ @terms ||= []
769
+ @global_mods ||= {}
770
+ @errors = []
771
+ @ip_netblocks = []
772
+ @raise_exceptions = options.has_key?(:raise_exceptions) ? options[:raise_exceptions] : true
736
773
  end
737
774
 
738
775
  def self.new_from_string(text, options = {})
@@ -747,7 +784,18 @@ class SPF::Record
747
784
  raise SPF::NothingToParseError.new('Nothing to parse for record')
748
785
  end
749
786
  self.parse_version_tag
750
- self.parse_term while @parse_text.length > 0
787
+ while @parse_text.length > 0
788
+ term = nil
789
+ begin
790
+ term = self.parse_term
791
+ rescue SPF::Error => e
792
+ term.errors << e if term
793
+ @errors << e
794
+ raise if @raise_exceptions
795
+ end
796
+ @ip_netblocks << term.ip_netblocks if term
797
+ end
798
+ @ip_netblocks.flatten!
751
799
  #self.parse_end
752
800
  end
753
801
 
@@ -772,7 +820,7 @@ class SPF::Record
772
820
  (?: \x20+ | $ )
773
821
  /x
774
822
 
775
-
823
+ term = nil
776
824
  if @parse_text.sub!(regex, '') and $&
777
825
  # Looks like a mechanism:
778
826
  mech_text = $1
@@ -781,7 +829,7 @@ class SPF::Record
781
829
  unless mech_class
782
830
  raise SPF::InvalidMech.new("Unknown mechanism type '#{mech_name}' in '#{@version_tag}' record")
783
831
  end
784
- mech = mech_class.new_from_string(mech_text)
832
+ term = mech = mech_class.new_from_string(mech_text)
785
833
  @terms << mech
786
834
  elsif (
787
835
  @parse_text.sub!(/
@@ -799,7 +847,7 @@ class SPF::Record
799
847
  mod_class = MOD_CLASSES[mod_name]
800
848
  if mod_class
801
849
  # Known modifier.
802
- mod = mod_class.new_from_string(mod_text)
850
+ term = mod = mod_class.new_from_string(mod_text)
803
851
  if SPF::GlobalMod === mod
804
852
  # Global modifier.
805
853
  unless @global_mods[mod_name]
@@ -814,6 +862,7 @@ class SPF::Record
814
862
  else
815
863
  raise SPF::JunkInRecordError.new("Junk encountered in record '#{@text}'")
816
864
  end
865
+ return term
817
866
  end
818
867
 
819
868
  def global_mods
@@ -834,7 +883,6 @@ class SPF::Record
834
883
  begin
835
884
  @terms.each do |term|
836
885
  if SPF::Mech === term
837
- #if term.is_a?(SPF::Mech)
838
886
  # Term is a mechanism.
839
887
  mech = term
840
888
  if mech.match(server, request, request.ip_address != nil)
data/lib/spf/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module SPF
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
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.2"
8
+ s.version = "0.0.3"
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.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: