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 +3 -1
- data/lib/spf/model.rb +74 -26
- data/lib/spf/version.rb +1 -1
- data/spf.gemspec +1 -1
- metadata +1 -1
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
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
684
|
-
PRECEDENCE
|
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
|
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
|
-
|
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
data/spf.gemspec
CHANGED