spf 0.0.2 → 0.0.3

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
@@ -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: