dns-zone2 0.3.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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/HISTORY.md +45 -0
  4. data/README.md +151 -0
  5. data/Rakefile +15 -0
  6. data/dns-zone2.gemspec +41 -0
  7. data/lib/dns/zone.rb +207 -0
  8. data/lib/dns/zone/rr.rb +87 -0
  9. data/lib/dns/zone/rr/a.rb +21 -0
  10. data/lib/dns/zone/rr/aaaa.rb +5 -0
  11. data/lib/dns/zone/rr/cdnskey.rb +5 -0
  12. data/lib/dns/zone/rr/cds.rb +5 -0
  13. data/lib/dns/zone/rr/cname.rb +21 -0
  14. data/lib/dns/zone/rr/dlv.rb +5 -0
  15. data/lib/dns/zone/rr/dnskey.rb +38 -0
  16. data/lib/dns/zone/rr/ds.rb +38 -0
  17. data/lib/dns/zone/rr/hinfo.rb +31 -0
  18. data/lib/dns/zone/rr/mx.rb +33 -0
  19. data/lib/dns/zone/rr/naptr.rb +44 -0
  20. data/lib/dns/zone/rr/ns.rb +21 -0
  21. data/lib/dns/zone/rr/nsec.rb +32 -0
  22. data/lib/dns/zone/rr/nsec3.rb +45 -0
  23. data/lib/dns/zone/rr/nsec3param.rb +38 -0
  24. data/lib/dns/zone/rr/ptr.rb +21 -0
  25. data/lib/dns/zone/rr/record.rb +88 -0
  26. data/lib/dns/zone/rr/rrsig.rb +54 -0
  27. data/lib/dns/zone/rr/soa.rb +51 -0
  28. data/lib/dns/zone/rr/spf.rb +5 -0
  29. data/lib/dns/zone/rr/srv.rb +38 -0
  30. data/lib/dns/zone/rr/sshfp.rb +35 -0
  31. data/lib/dns/zone/rr/txt.rb +24 -0
  32. data/lib/dns/zone/test_case.rb +27 -0
  33. data/lib/dns/zone/version.rb +6 -0
  34. data/test/rr/a_test.rb +37 -0
  35. data/test/rr/aaaa_test.rb +27 -0
  36. data/test/rr/cdnskey_test.rb +31 -0
  37. data/test/rr/cds_test.rb +28 -0
  38. data/test/rr/cname_test.rb +19 -0
  39. data/test/rr/dlv_test.rb +28 -0
  40. data/test/rr/dnskey_test.rb +31 -0
  41. data/test/rr/ds_test.rb +28 -0
  42. data/test/rr/hinfo_test.rb +44 -0
  43. data/test/rr/mx_test.rb +26 -0
  44. data/test/rr/naptr_test.rb +60 -0
  45. data/test/rr/ns_test.rb +18 -0
  46. data/test/rr/nsec3_test.rb +33 -0
  47. data/test/rr/nsec3param_test.rb +29 -0
  48. data/test/rr/nsec_test.rb +24 -0
  49. data/test/rr/ptr_test.rb +19 -0
  50. data/test/rr/record_test.rb +37 -0
  51. data/test/rr/rrsig_test.rb +40 -0
  52. data/test/rr/soa_test.rb +34 -0
  53. data/test/rr/spf_test.rb +20 -0
  54. data/test/rr/srv_test.rb +24 -0
  55. data/test/rr/sshfp_test.rb +24 -0
  56. data/test/rr/txt_test.rb +44 -0
  57. data/test/rr_test.rb +50 -0
  58. data/test/version_test.rb +9 -0
  59. data/test/zone_test.rb +273 -0
  60. metadata +217 -0
@@ -0,0 +1,87 @@
1
+ module DNS
2
+ class Zone
3
+
4
+ # The module containes resource record types supported by this gem.
5
+ # The #{load} method will convert RR string data into a Ruby class.
6
+ module RR
7
+
8
+ REGEX_TTL = /\d+[wdmhs]?/i
9
+ REGEX_KLASS = /(?<klass>IN)?/i
10
+ REGEX_TYPE = /(?<type>A|AAAA|CDNSKEY|CDS|CNAME|DLV|DNSKEY|DS|HINFO|MX|NAPTR|NS|NSEC|NSEC3|NSEC3PARAM|RRSIG|SOA|SPF|SRV|SSHFP|TXT|PTR)\s{1}/i
11
+ REGEX_RR = /^(?<label>\S+|\s{1})\s*(?<ttl>#{REGEX_TTL})?\s*#{REGEX_KLASS}\s*#{REGEX_TYPE}\s*(?<rdata>[\s\S]*)$/i
12
+ REGEX_DOMAINNAME = /\S+\.?/i
13
+ REGEX_STRING = /((?:[^"\\]+|\\.)*)/
14
+ REGEX_CHARACTER_STRING = %r{
15
+ "#{DNS::Zone::RR::REGEX_STRING}"|#{DNS::Zone::RR::REGEX_STRING}
16
+ }mx
17
+
18
+ # Load RR string data and return an instance representing the RR.
19
+ #
20
+ # @param string [String] RR ASCII string data
21
+ # @param options [Hash] additional data required to correctly parse a 'whole' zone
22
+ # @option options [String] :last_label The last label used by the previous RR
23
+ # @return [Object]
24
+ def self.load(string, options = {})
25
+ # strip comments, unless its escaped.
26
+ # skip semicolons within "quote segments" (TXT records)
27
+ string.gsub!(/((?<!\\);)(?=(?:[^"]|"[^"]*")*$).*/o, "")
28
+
29
+ captures = string.match(REGEX_RR)
30
+ return nil unless captures
31
+
32
+ case captures[:type]
33
+ when 'A' then A.new.load(string, options)
34
+ when 'AAAA' then AAAA.new.load(string, options)
35
+ when 'CDNSKEY' then CDNSKEY.new.load(string, options)
36
+ when 'CDS' then CDS.new.load(string, options)
37
+ when 'CNAME' then CNAME.new.load(string, options)
38
+ when 'DLV' then DLV.new.load(string, options)
39
+ when 'DNSKEY' then DNSKEY.new.load(string, options)
40
+ when 'DS' then DS.new.load(string, options)
41
+ when 'HINFO' then HINFO.new.load(string, options)
42
+ when 'MX' then MX.new.load(string, options)
43
+ when 'NAPTR' then NAPTR.new.load(string, options)
44
+ when 'NS' then NS.new.load(string, options)
45
+ when 'NSEC' then NSEC.new.load(string, options)
46
+ when 'NSEC3' then NSEC3.new.load(string, options)
47
+ when 'NSEC3PARAM' then NSEC3PARAM.new.load(string, options)
48
+ when 'PTR' then PTR.new.load(string, options)
49
+ when 'RRSIG' then RRSIG.new.load(string, options)
50
+ when 'SOA' then SOA.new.load(string, options)
51
+ when 'SPF' then SPF.new.load(string, options)
52
+ when 'SRV' then SRV.new.load(string, options)
53
+ when 'SSHFP' then SSHFP.new.load(string, options)
54
+ when 'TXT' then TXT.new.load(string, options)
55
+ else
56
+ raise 'Unknown or unsupported RR Type'
57
+ end
58
+ end
59
+
60
+ autoload :Record, 'dns/zone/rr/record'
61
+
62
+ autoload :A, 'dns/zone/rr/a'
63
+ autoload :AAAA, 'dns/zone/rr/aaaa'
64
+ autoload :CDNSKEY, 'dns/zone/rr/cdnskey'
65
+ autoload :CDS, 'dns/zone/rr/cds'
66
+ autoload :CNAME, 'dns/zone/rr/cname'
67
+ autoload :DLV, 'dns/zone/rr/dlv'
68
+ autoload :DNSKEY, 'dns/zone/rr/dnskey'
69
+ autoload :DS, 'dns/zone/rr/ds'
70
+ autoload :HINFO, 'dns/zone/rr/hinfo'
71
+ autoload :MX, 'dns/zone/rr/mx'
72
+ autoload :NAPTR, 'dns/zone/rr/naptr'
73
+ autoload :NS, 'dns/zone/rr/ns'
74
+ autoload :NSEC, 'dns/zone/rr/nsec'
75
+ autoload :NSEC3, 'dns/zone/rr/nsec3'
76
+ autoload :NSEC3PARAM, 'dns/zone/rr/nsec3param'
77
+ autoload :PTR, 'dns/zone/rr/ptr'
78
+ autoload :RRSIG, 'dns/zone/rr/rrsig'
79
+ autoload :SOA, 'dns/zone/rr/soa'
80
+ autoload :SPF, 'dns/zone/rr/spf'
81
+ autoload :SRV, 'dns/zone/rr/srv'
82
+ autoload :SSHFP, 'dns/zone/rr/sshfp'
83
+ autoload :TXT, 'dns/zone/rr/txt'
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,21 @@
1
+ # `A` resource record.
2
+ #
3
+ # RFC 1035
4
+ class DNS::Zone::RR::A < DNS::Zone::RR::Record
5
+
6
+ attr_accessor :address
7
+
8
+ def dump
9
+ parts = general_prefix
10
+ parts << address
11
+ parts.join(' ')
12
+ end
13
+
14
+ def load(string, options = {})
15
+ rdata = load_general_and_get_rdata(string, options)
16
+ return nil unless rdata
17
+ @address = rdata
18
+ self
19
+ end
20
+
21
+ end
@@ -0,0 +1,5 @@
1
+ # `AAAA` resource record.
2
+ #
3
+ # RFC xxxx
4
+ class DNS::Zone::RR::AAAA < DNS::Zone::RR::A
5
+ end
@@ -0,0 +1,5 @@
1
+ # `CDNSKEY` resource record.
2
+ #
3
+ # RFC 7344
4
+ class DNS::Zone::RR::CDNSKEY < DNS::Zone::RR::DNSKEY
5
+ end
@@ -0,0 +1,5 @@
1
+ # `CDS` resource record.
2
+ #
3
+ # RFC 7344
4
+ class DNS::Zone::RR::CDS < DNS::Zone::RR::DS
5
+ end
@@ -0,0 +1,21 @@
1
+ # `CNAME` resource record.
2
+ #
3
+ # RFC 1035
4
+ class DNS::Zone::RR::CNAME < DNS::Zone::RR::Record
5
+
6
+ attr_accessor :domainname
7
+
8
+ def dump
9
+ parts = general_prefix
10
+ parts << domainname
11
+ parts.join(' ')
12
+ end
13
+
14
+ def load(string, options = {})
15
+ rdata = load_general_and_get_rdata(string, options)
16
+ return nil unless rdata
17
+ @domainname = rdata
18
+ self
19
+ end
20
+
21
+ end
@@ -0,0 +1,5 @@
1
+ # `DLV` resource record.
2
+ #
3
+ # RFC 4431
4
+ class DNS::Zone::RR::DLV < DNS::Zone::RR::DS
5
+ end
@@ -0,0 +1,38 @@
1
+ # `DNSKEY` resource record.
2
+ #
3
+ # RFC 4034
4
+ class DNS::Zone::RR::DNSKEY < DNS::Zone::RR::Record
5
+
6
+ REGEX_DNSKEY_RDATA = %r{
7
+ (?<flags>\d+)\s*
8
+ (?<protocol>\d+)\s*
9
+ (?<algorithm>\d+)\s*
10
+ (?<key>#{DNS::Zone::RR::REGEX_STRING})\s*
11
+ }mx
12
+
13
+ attr_accessor :flags, :protocol, :algorithm, :key
14
+
15
+ def dump
16
+ parts = general_prefix
17
+ parts << flags
18
+ parts << protocol
19
+ parts << algorithm
20
+ parts << key
21
+ parts.join(' ')
22
+ end
23
+
24
+ def load(string, options = {})
25
+ rdata = load_general_and_get_rdata(string, options)
26
+ return nil unless rdata
27
+
28
+ captures = rdata.match(REGEX_DNSKEY_RDATA)
29
+ return nil unless captures
30
+
31
+ @flags = captures[:flags].to_i
32
+ @protocol = captures[:protocol].to_i
33
+ @algorithm = captures[:algorithm].to_i
34
+ @key = captures[:key].scan(/#{DNS::Zone::RR::REGEX_STRING}/).join
35
+ self
36
+ end
37
+
38
+ end
@@ -0,0 +1,38 @@
1
+ # `DS` resource record.
2
+ #
3
+ # RFC 4034
4
+ class DNS::Zone::RR::DS < DNS::Zone::RR::Record
5
+
6
+ REGEX_DS_RDATA = %r{
7
+ (?<key_tag>\d+)\s*
8
+ (?<algorithm>\d+)\s*
9
+ (?<digest_type>\d+)\s*
10
+ (?<digest>#{DNS::Zone::RR::REGEX_STRING})\s*
11
+ }mx
12
+
13
+ attr_accessor :key_tag, :algorithm, :digest_type, :digest
14
+
15
+ def dump
16
+ parts = general_prefix
17
+ parts << key_tag
18
+ parts << algorithm
19
+ parts << digest_type
20
+ parts << digest
21
+ parts.join(' ')
22
+ end
23
+
24
+ def load(string, options = {})
25
+ rdata = load_general_and_get_rdata(string, options)
26
+ return nil unless rdata
27
+
28
+ captures = rdata.match(REGEX_DS_RDATA)
29
+ return nil unless captures
30
+
31
+ @key_tag = captures[:key_tag].to_i
32
+ @algorithm = captures[:algorithm].to_i
33
+ @digest_type = captures[:digest_type].to_i
34
+ @digest = captures[:digest].scan(/#{DNS::Zone::RR::REGEX_STRING}/).join
35
+ self
36
+ end
37
+
38
+ end
@@ -0,0 +1,31 @@
1
+ # `HINFO` resource record.
2
+ #
3
+ # RFC 1035
4
+ class DNS::Zone::RR::HINFO < DNS::Zone::RR::Record
5
+
6
+ REGEX_HINFO_RDATA = %r{
7
+ (?<cpu>(?:#{DNS::Zone::RR::REGEX_CHARACTER_STRING})){1}\s
8
+ (?<os>(?:#{DNS::Zone::RR::REGEX_CHARACTER_STRING})){1}
9
+ }mx
10
+ attr_accessor :cpu
11
+ attr_accessor :os
12
+
13
+ def dump
14
+ parts = general_prefix
15
+ parts << %Q{"#{cpu}" "#{os}"}
16
+ parts.join(' ')
17
+ end
18
+
19
+ def load(string, options = {})
20
+ rdata = load_general_and_get_rdata(string, options)
21
+ return nil unless rdata
22
+
23
+ captures = rdata.match(REGEX_HINFO_RDATA)
24
+ return nil unless captures
25
+
26
+ @cpu = captures[:cpu].scan(/#{DNS::Zone::RR::REGEX_CHARACTER_STRING}/).join
27
+ @os = captures[:os].scan(/#{DNS::Zone::RR::REGEX_CHARACTER_STRING}/).join
28
+ self
29
+ end
30
+
31
+ end
@@ -0,0 +1,33 @@
1
+ # `MX` resource record.
2
+ #
3
+ # RFC 1035
4
+ class DNS::Zone::RR::MX < DNS::Zone::RR::Record
5
+
6
+ REGEX_MX_RDATA = %r{
7
+ (?<priority>\d+)\s*
8
+ (?<exchange>#{DNS::Zone::RR::REGEX_DOMAINNAME})\s*
9
+ }mx
10
+
11
+ attr_accessor :priority
12
+ attr_accessor :exchange
13
+
14
+ def dump
15
+ parts = general_prefix
16
+ parts << priority
17
+ parts << exchange
18
+ parts.join(' ')
19
+ end
20
+
21
+ def load(string, options = {})
22
+ rdata = load_general_and_get_rdata(string, options)
23
+ return nil unless rdata
24
+
25
+ captures = rdata.match(REGEX_MX_RDATA)
26
+ return nil unless captures
27
+
28
+ @priority = captures[:priority].to_i
29
+ @exchange = captures[:exchange]
30
+ self
31
+ end
32
+
33
+ end
@@ -0,0 +1,44 @@
1
+ # `NAPTR` resource record.
2
+ #
3
+ # RFC 3403
4
+ class DNS::Zone::RR::NAPTR < DNS::Zone::RR::Record
5
+
6
+ REGEX_NAPTR_RDATA = %r{
7
+ (?<order>\d+)\s*
8
+ (?<pref>\d+)\s*
9
+ (?<flags>#{DNS::Zone::RR::REGEX_CHARACTER_STRING})\s*
10
+ (?<service>#{DNS::Zone::RR::REGEX_CHARACTER_STRING})\s*
11
+ (?<regexp>#{DNS::Zone::RR::REGEX_CHARACTER_STRING})\s*
12
+ (?<replacement>#{DNS::Zone::RR::REGEX_DOMAINNAME}|\.{1})\s*
13
+ }mx
14
+
15
+ attr_accessor :order, :pref, :flags, :service, :regexp, :replacement
16
+
17
+ def dump
18
+ parts = general_prefix
19
+ parts << order
20
+ parts << pref
21
+ parts << %Q{"#{flags}"}
22
+ parts << %Q{"#{service}"}
23
+ parts << %Q{"#{regexp}"}
24
+ parts << replacement
25
+ parts.join(' ')
26
+ end
27
+
28
+ def load(string, options = {})
29
+ rdata = load_general_and_get_rdata(string, options)
30
+ return nil unless rdata
31
+
32
+ captures = rdata.match(REGEX_NAPTR_RDATA)
33
+ return nil unless captures
34
+
35
+ @order = captures[:order].to_i
36
+ @pref = captures[:pref].to_i
37
+ @flags = captures[:flags].scan(/#{DNS::Zone::RR::REGEX_CHARACTER_STRING}/).join
38
+ @service = captures[:service].scan(/#{DNS::Zone::RR::REGEX_CHARACTER_STRING}/).join
39
+ @regexp = captures[:regexp].scan(/#{DNS::Zone::RR::REGEX_CHARACTER_STRING}/).join
40
+ @replacement = captures[:replacement]
41
+ self
42
+ end
43
+
44
+ end
@@ -0,0 +1,21 @@
1
+ # `NS` resource record.
2
+ #
3
+ # RFC 1035
4
+ class DNS::Zone::RR::NS < DNS::Zone::RR::Record
5
+
6
+ attr_accessor :nameserver
7
+
8
+ def dump
9
+ parts = general_prefix
10
+ parts << nameserver
11
+ parts.join(' ')
12
+ end
13
+
14
+ def load(string, options = {})
15
+ rdata = load_general_and_get_rdata(string, options)
16
+ return nil unless rdata
17
+ @nameserver = rdata
18
+ self
19
+ end
20
+
21
+ end
@@ -0,0 +1,32 @@
1
+ # `NSEC` resource record.
2
+ #
3
+ # RFC 4034
4
+ class DNS::Zone::RR::NSEC < DNS::Zone::RR::Record
5
+
6
+ REGEX_NSEC_RDATA = %r{
7
+ (?<next_domain>#{DNS::Zone::RR::REGEX_DOMAINNAME})\s*
8
+ (?<rrset_types>#{DNS::Zone::RR::REGEX_STRING})\s*
9
+ }mx
10
+
11
+ attr_accessor :next_domain, :rrset_types
12
+
13
+ def dump
14
+ parts = general_prefix
15
+ parts << next_domain
16
+ parts << rrset_types
17
+ parts.join(' ')
18
+ end
19
+
20
+ def load(string, options = {})
21
+ rdata = load_general_and_get_rdata(string, options)
22
+ return nil unless rdata
23
+
24
+ captures = rdata.match(REGEX_NSEC_RDATA)
25
+ return nil unless captures
26
+
27
+ @next_domain = captures[:next_domain]
28
+ @rrset_types = captures[:rrset_types]
29
+ self
30
+ end
31
+
32
+ end
@@ -0,0 +1,45 @@
1
+ # `NSEC3` resource record.
2
+ #
3
+ # RFC 5155
4
+ class DNS::Zone::RR::NSEC3 < DNS::Zone::RR::Record
5
+
6
+ REGEX_NSEC3_RDATA = %r{
7
+ (?<algorithm>\d+)\s*
8
+ (?<flags>\d+)\s*
9
+ (?<iterations>\d+)\s*
10
+ (?<salt>\S+)\s*
11
+ (?<next_hashed_owner_name>\S+)\s*
12
+ (?<rrset_types>#{DNS::Zone::RR::REGEX_STRING})\s*
13
+ }mx
14
+
15
+ attr_accessor :algorithm, :flags, :iterations, :salt,
16
+ :next_hashed_owner_name, :rrset_types
17
+
18
+ def dump
19
+ parts = general_prefix
20
+ parts << algorithm
21
+ parts << flags
22
+ parts << iterations
23
+ parts << salt
24
+ parts << next_hashed_owner_name
25
+ parts << rrset_types
26
+ parts.join(' ')
27
+ end
28
+
29
+ def load(string, options = {})
30
+ rdata = load_general_and_get_rdata(string, options)
31
+ return nil unless rdata
32
+
33
+ captures = rdata.match(REGEX_NSEC3_RDATA)
34
+ return nil unless captures
35
+
36
+ @algorithm = captures[:algorithm].to_i
37
+ @flags = captures[:flags].to_i
38
+ @iterations = captures[:iterations].to_i
39
+ @salt = captures[:salt]
40
+ @next_hashed_owner_name = captures[:next_hashed_owner_name]
41
+ @rrset_types = captures[:rrset_types]
42
+ self
43
+ end
44
+
45
+ end