dns-zone 0.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/Guardfile +14 -0
- data/HISTORY.md +3 -0
- data/README.md +100 -0
- data/Rakefile +15 -0
- data/dns-zone.gemspec +41 -0
- data/lib/dns/zone.rb +105 -0
- data/lib/dns/zone/rr.rb +51 -0
- data/lib/dns/zone/rr/a.rb +21 -0
- data/lib/dns/zone/rr/aaaa.rb +5 -0
- data/lib/dns/zone/rr/cname.rb +14 -0
- data/lib/dns/zone/rr/mx.rb +16 -0
- data/lib/dns/zone/rr/ns.rb +21 -0
- data/lib/dns/zone/rr/record.rb +81 -0
- data/lib/dns/zone/rr/soa.rb +52 -0
- data/lib/dns/zone/rr/srv.rb +17 -0
- data/lib/dns/zone/rr/txt.rb +22 -0
- data/lib/dns/zone/test_case.rb +28 -0
- data/lib/dns/zone/version.rb +6 -0
- data/test/rr/a_test.rb +36 -0
- data/test/rr/aaaa_test.rb +13 -0
- data/test/rr/cname_test.rb +12 -0
- data/test/rr/mx_test.rb +18 -0
- data/test/rr/ns_test.rb +11 -0
- data/test/rr/record_test.rb +30 -0
- data/test/rr/soa_test.rb +34 -0
- data/test/rr/srv_test.rb +14 -0
- data/test/rr/txt_test.rb +44 -0
- data/test/rr_test.rb +42 -0
- data/test/version_test.rb +9 -0
- data/test/zone_test.rb +111 -0
- metadata +190 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 82c71fa4e618b0bff6f946e6097511699e49aec0
|
4
|
+
data.tar.gz: b4021e5692ea47f2ca8a3d129aed828593155af7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f162ed2a6e7804120dd8b6fadac2c5b4c6d2a51b9044e66dc8e48fc2c86e1dbad34f89b185934febb26cc41d6f032dfff742bf7254b5fd553cbf77363d82653c
|
7
|
+
data.tar.gz: 4242b9bbf4fe966a790e948ff5e31affc9488ac0fe5c6f6dd1ae2e92cd4b4e2f68f04bfe2f0bb685e7f8eac15e2420855568da9419a1849534c43e089022acd3
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Run minitest suite
|
2
|
+
guard :minitest do
|
3
|
+
watch(%r{^dns-zone\.gemspec}) { 'all' }
|
4
|
+
watch(%r{^lib/dns/(.*)\.rb}) { |m| "test/#{m[1]}_test.rb" }
|
5
|
+
watch(%r{^lib/dns/zone/test_case\.rb}) { 'all' }
|
6
|
+
watch(%r{^lib/dns/zone/(.*/)?([^/]+)\.rb}) { |m| "test/#{m[1]}#{m[2]}_test.rb" }
|
7
|
+
watch(%r{^test/(.*/)*(.*)_test\.rb})
|
8
|
+
end
|
9
|
+
|
10
|
+
# Run `bundle update|install` when gem files altered.
|
11
|
+
guard :bundler do
|
12
|
+
watch('Gemfile')
|
13
|
+
watch(/^.+\.gemspec/)
|
14
|
+
end
|
data/HISTORY.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
dns-zone
|
2
|
+
========
|
3
|
+
|
4
|
+
A Ruby library for building and parsing DNS zone files.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your Gemfile:
|
9
|
+
|
10
|
+
gem 'dns-zone'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
bundle install
|
15
|
+
|
16
|
+
Require the gem in your code:
|
17
|
+
|
18
|
+
require 'dns/zone'
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
DNS::Zone.new
|
23
|
+
DNS::Zone.load(zone_as_string)
|
24
|
+
DNS::Zone::RR.load(rr_as_string)
|
25
|
+
DNS::Zone::RR::A.load(a_rr_as_string)
|
26
|
+
|
27
|
+
zone = DNS::Zone.new
|
28
|
+
zone.origin = 'example.com.'
|
29
|
+
# FIXME: not sure what RFC (if any) defines the time formatting
|
30
|
+
zone.ttl = '1d'
|
31
|
+
# FIXME: keep DNS style representation? for <domain-name>s and email addresses
|
32
|
+
zone.soa.nameserver = 'ns0.lividpenguin.com.'
|
33
|
+
zone.soa.email = 'hostmaster.lividpenguin.com.'
|
34
|
+
|
35
|
+
# output as dns zone file
|
36
|
+
zone.to_zone_file
|
37
|
+
|
38
|
+
# Development
|
39
|
+
|
40
|
+
## Development Commands
|
41
|
+
|
42
|
+
# install external gem dependencies first
|
43
|
+
bundle install
|
44
|
+
|
45
|
+
# run all tests and build code coverage
|
46
|
+
bundle exec rake test
|
47
|
+
|
48
|
+
# hints where to improve docs
|
49
|
+
bundle exec inch
|
50
|
+
|
51
|
+
# watch for changes and run development commands (tests, documentation, etc)
|
52
|
+
bundle exec guard
|
53
|
+
|
54
|
+
# TODO
|
55
|
+
|
56
|
+
## Must have
|
57
|
+
|
58
|
+
[ ] Ability to load a whole zone
|
59
|
+
[x] Add support for RR Type: SOA
|
60
|
+
[ ] Add support for RR Type: PTR
|
61
|
+
[ ] Add support for RR Type: SPF
|
62
|
+
[ ] Add support for RR Type: LOC
|
63
|
+
[ ] Add support for RR Type: HINFO
|
64
|
+
|
65
|
+
## Would be nice
|
66
|
+
|
67
|
+
[ ] Handle parsing a zone file that uses more then one $ORIGIN directive.
|
68
|
+
[ ] Validation, error checking...
|
69
|
+
[ ] Only one SOA per zone.
|
70
|
+
[ ] CNAMEs can't use a label of `@`.
|
71
|
+
|
72
|
+
[ ] Ability to 'include' defaults/records into a zone.
|
73
|
+
This may or may not want to mean supporting the `$INCLUDE` directive.
|
74
|
+
|
75
|
+
## At some point; low priority
|
76
|
+
|
77
|
+
[ ] Configuration options:
|
78
|
+
[ ] spaces/tabs used between RR params in zone file output
|
79
|
+
[ ] time format to use (seconds or bind time format (e.g. 1d))
|
80
|
+
[ ] add comments to explain TTL's that are in seconds
|
81
|
+
[ ] Ability to add comment to RR (n.b. currently we strip comments when parsing)
|
82
|
+
[ ] Add support for RR Type: DNAME
|
83
|
+
[ ] Add support for RR Type: DNSKEY
|
84
|
+
[ ] Add support for RR Type: DS
|
85
|
+
[ ] Add support for RR Type: KEY
|
86
|
+
[ ] Add support for RR Type: NSEC
|
87
|
+
[ ] Add support for RR Type: RRSIG
|
88
|
+
[ ] Add support for RR Type: NAPTR
|
89
|
+
[ ] Add support for RR Type: RP
|
90
|
+
[ ] Add support for RR Type: RT
|
91
|
+
|
92
|
+
# Notes
|
93
|
+
|
94
|
+
- RR Format: `[<TTL>] [<class>] <type> <RDATA>`
|
95
|
+
- A DNS zone is built from RR's and a couple of other special directives.
|
96
|
+
- If zone file does not include $ORIGIN, it will be inferred by the `zone "<zone-name>" {}` clause from bind.conf
|
97
|
+
In general we should always explicitly define an $ORIGIN directive unless there is a very good reason not to.
|
98
|
+
- [RFC 1035 - Domain Names - Implementation and Specification](http://www.ietf.org/rfc/rfc1035.txt)
|
99
|
+
- [RFC 2308 - Negative Caching of DNS Queries (DNS NCACHE)](http://www.ietf.org/rfc/rfc2308.txt)
|
100
|
+
- [RFC 3596 - DNS Extensions to Support IP Version 6](http://www.ietf.org/rfc/rfc3596.txt)
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems' unless defined?(Gem)
|
2
|
+
require 'bundler'
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
# by default run unit tests.
|
7
|
+
task :default => 'test'
|
8
|
+
|
9
|
+
desc 'Run full test suite and generate code coverage -- COVERAGE=false to disable code coverage'
|
10
|
+
Rake::TestTask.new(:test) do |task|
|
11
|
+
ENV['COVERAGE'] ||= 'yes'
|
12
|
+
task.libs << 'test'
|
13
|
+
task.pattern = 'test/**/*_test.rb'
|
14
|
+
task.verbose = true
|
15
|
+
end
|
data/dns-zone.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require './lib/dns/zone/version'
|
2
|
+
|
3
|
+
spec = Gem::Specification.new do |s|
|
4
|
+
# gem information/details
|
5
|
+
s.name = 'dns-zone'
|
6
|
+
s.version = DNS::Zone::Version
|
7
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
8
|
+
s.summary = 'A Ruby library for building and parsing DNS zone files.'
|
9
|
+
s.license = 'MIT'
|
10
|
+
s.homepage = 'https://github.com/lantins/dns-zone'
|
11
|
+
s.authors = ['Luke Antins']
|
12
|
+
s.email = ['luke@lividpenguin.com']
|
13
|
+
|
14
|
+
# gem settings for what files to include.
|
15
|
+
s.files = %w(Rakefile README.md HISTORY.md Gemfile Guardfile dns-zone.gemspec)
|
16
|
+
s.files += Dir.glob('{test/**/*,lib/**/*}')
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
#s.executables = ['dns-zone']
|
19
|
+
#s.default_executable = 'dns-zone'
|
20
|
+
|
21
|
+
# min ruby version
|
22
|
+
s.required_ruby_version = ::Gem::Requirement.new("~> 1.9")
|
23
|
+
|
24
|
+
# cross platform gem dependencies
|
25
|
+
#s.add_dependency('gli')
|
26
|
+
#s.add_dependency('paint')
|
27
|
+
s.add_development_dependency('bundler', '~> 0')
|
28
|
+
s.add_development_dependency('rake', '~> 0')
|
29
|
+
s.add_development_dependency('minitest', '~> 0')
|
30
|
+
s.add_development_dependency('simplecov', '~> 0.7')
|
31
|
+
s.add_development_dependency('yard', '~> 0')
|
32
|
+
s.add_development_dependency('inch', '~> 0')
|
33
|
+
s.add_development_dependency('guard-minitest', '~> 0')
|
34
|
+
s.add_development_dependency('guard-bundler', '~> 0')
|
35
|
+
|
36
|
+
# long description.
|
37
|
+
s.description = <<-EOL
|
38
|
+
A Ruby library for building and parsing DNS zone files for use with
|
39
|
+
Bind and PowerDNS (with Bind backend) DNS servers.
|
40
|
+
EOL
|
41
|
+
end
|
data/lib/dns/zone.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'dns/zone/rr'
|
2
|
+
require 'dns/zone/version'
|
3
|
+
|
4
|
+
# :nodoc:
|
5
|
+
module DNS
|
6
|
+
|
7
|
+
# Represents a 'whole' zone of many resource records (RRs).
|
8
|
+
#
|
9
|
+
# This is also the primary namespace for the `dns-zone` gem.
|
10
|
+
class Zone
|
11
|
+
|
12
|
+
attr_accessor :ttl, :origin, :records
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@records = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# FROM RFC:
|
19
|
+
# The format of these files is a sequence of entries. Entries are
|
20
|
+
# predominantly line-oriented, though parentheses can be used to continue
|
21
|
+
# a list of items across a line boundary, and text literals can contain
|
22
|
+
# CRLF within the text. Any combination of tabs and spaces act as a
|
23
|
+
# delimiter between the separate items that make up an entry. The end of
|
24
|
+
# any line in the master file can end with a comment. The comment starts
|
25
|
+
# with a ";" (semicolon).
|
26
|
+
|
27
|
+
def self.load(string)
|
28
|
+
# get entries
|
29
|
+
entries = self.extract_entries(string)
|
30
|
+
|
31
|
+
instance = self.new
|
32
|
+
|
33
|
+
entries.each do |entry|
|
34
|
+
if entry =~ /\$(ORIGIN|TTL)\s+(.+)/
|
35
|
+
instance.ttl = $2 if $1 == 'TTL'
|
36
|
+
instance.origin = $2 if $1 == 'ORIGIN'
|
37
|
+
next
|
38
|
+
end
|
39
|
+
|
40
|
+
if entry =~ DNS::Zone::RR::RX_RR
|
41
|
+
rec = DNS::Zone::RR.load(entry)
|
42
|
+
instance.records << rec if rec
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
# read in special statments like $TTL and $ORIGIN
|
48
|
+
# parse each RR and create a Ruby object for it
|
49
|
+
return instance
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.extract_entries(string)
|
53
|
+
entries = []
|
54
|
+
mode = :line
|
55
|
+
entry = ''
|
56
|
+
|
57
|
+
parentheses_ref_count = 0
|
58
|
+
|
59
|
+
string.lines.each do |line|
|
60
|
+
# strip comments unless escaped
|
61
|
+
line = line.gsub(/(?<!\\);.*/o, '').chomp
|
62
|
+
|
63
|
+
next if line.gsub(/\s+/, '').empty?
|
64
|
+
|
65
|
+
# append to entry line
|
66
|
+
entry << line
|
67
|
+
|
68
|
+
quotes = entry.count('"')
|
69
|
+
has_quotes = quotes > 0
|
70
|
+
|
71
|
+
parentheses = entry.count('()')
|
72
|
+
has_parentheses = parentheses > 0
|
73
|
+
|
74
|
+
if has_quotes
|
75
|
+
character_strings = entry.scan(/("(?:[^"\\]+|\\.)*")/).join(' ')
|
76
|
+
without = entry.gsub(/"((?:[^"\\]+|\\.)*)"/, '')
|
77
|
+
parentheses_ref_count = without.count('(') - without.count(')')
|
78
|
+
else
|
79
|
+
parentheses_ref_count = entry.count('(') - entry.count(')')
|
80
|
+
end
|
81
|
+
|
82
|
+
# are parentheses balanced?
|
83
|
+
if parentheses_ref_count == 0
|
84
|
+
if has_quotes
|
85
|
+
without.gsub!(/[()]/, '')
|
86
|
+
without.gsub!(/[ ]{2,}/, ' ')
|
87
|
+
#entries << (without + character_strings)
|
88
|
+
entry = (without + character_strings)
|
89
|
+
else
|
90
|
+
entry.gsub!(/[()]/, '')
|
91
|
+
entry.gsub!(/[ ]{2,}/, ' ')
|
92
|
+
entry.gsub!(/[ ]+$/, '')
|
93
|
+
#entries << entry
|
94
|
+
end
|
95
|
+
entries << entry
|
96
|
+
entry = ''
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
return entries
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
data/lib/dns/zone/rr.rb
ADDED
@@ -0,0 +1,51 @@
|
|
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
|
+
RX_TTL = /\d+[wdmhs]?/i
|
9
|
+
RX_KLASS = /(?<klass>IN)?/i
|
10
|
+
RX_TYPE = /(?<type>A|AAAA|CNAME|MX|NS|SOA|SRV|TXT)\s{1}/i
|
11
|
+
RX_RR = /^(?<label>\S+|\s{1})\s*(?<ttl>#{RX_TTL})?\s*#{RX_KLASS}\s*#{RX_TYPE}\s*(?<rdata>[\s\S]*)$/i
|
12
|
+
RX_DOMAINNAME = /\S+\./i
|
13
|
+
|
14
|
+
# Load RR string data and return an instance representing the RR.
|
15
|
+
#
|
16
|
+
# @param string [String] RR ASCII string data
|
17
|
+
# @param options [Hash] additional data required to correctly parse a 'whole' zone
|
18
|
+
# @option options [String] :last_label The last label used by the previous RR
|
19
|
+
# @return [Object]
|
20
|
+
def self.load(string, options = {})
|
21
|
+
# strip comments, unless its escaped.
|
22
|
+
string.gsub!(/(?<!\\);.*/o, "");
|
23
|
+
|
24
|
+
captures = string.match(RX_RR)
|
25
|
+
return nil unless captures
|
26
|
+
|
27
|
+
case captures[:type]
|
28
|
+
when 'A' then A.new.load(string, options)
|
29
|
+
when 'AAAA' then AAAA.new.load(string, options)
|
30
|
+
when 'TXT' then TXT.new.load(string, options)
|
31
|
+
when 'SOA' then SOA.new.load(string, options)
|
32
|
+
when 'NS' then NS.new.load(string, options)
|
33
|
+
else
|
34
|
+
raise 'Unknown RR Type'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
autoload :Record, 'dns/zone/rr/record'
|
39
|
+
|
40
|
+
autoload :A, 'dns/zone/rr/a'
|
41
|
+
autoload :AAAA, 'dns/zone/rr/aaaa'
|
42
|
+
autoload :CNAME, 'dns/zone/rr/cname'
|
43
|
+
autoload :MX, 'dns/zone/rr/mx'
|
44
|
+
autoload :NS, 'dns/zone/rr/ns'
|
45
|
+
autoload :SOA, 'dns/zone/rr/soa'
|
46
|
+
autoload :SRV, 'dns/zone/rr/srv'
|
47
|
+
autoload :TXT, 'dns/zone/rr/txt'
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
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 to_s
|
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,16 @@
|
|
1
|
+
# `MX` resource record.
|
2
|
+
#
|
3
|
+
# RFC 1035
|
4
|
+
class DNS::Zone::RR::MX < DNS::Zone::RR::Record
|
5
|
+
|
6
|
+
attr_accessor :preference
|
7
|
+
attr_accessor :exchange
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
parts = general_prefix
|
11
|
+
parts << preference
|
12
|
+
parts << exchange
|
13
|
+
parts.join(' ')
|
14
|
+
end
|
15
|
+
|
16
|
+
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 to_s
|
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,81 @@
|
|
1
|
+
# Parent class of all RR types, common resource record code lives here.
|
2
|
+
# Is responsible for building a Ruby object given a RR string.
|
3
|
+
#
|
4
|
+
# @abstract Each RR TYPE should subclass and override: {#load} and #{to_s}
|
5
|
+
class DNS::Zone::RR::Record
|
6
|
+
|
7
|
+
attr_accessor :label, :ttl
|
8
|
+
attr_reader :klass
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@label = '@'
|
12
|
+
@klass = 'IN'
|
13
|
+
end
|
14
|
+
|
15
|
+
# FIXME: should we just: `def type; 'SOA'; end` rather then do the class name convension?
|
16
|
+
#
|
17
|
+
# Figures out TYPE of RR using class name.
|
18
|
+
# This means the class name _must_ match the RR ASCII TYPE.
|
19
|
+
#
|
20
|
+
# When called directly on the parent class (that you should never do), it will
|
21
|
+
# return the string as `<type>`, for use with internal tests.
|
22
|
+
#
|
23
|
+
# @return [String] the RR type
|
24
|
+
def type
|
25
|
+
name = self.class.name.split('::').last
|
26
|
+
return '<type>' if name == 'Record'
|
27
|
+
name
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns 'general' prefix (in parts) that come before the RDATA.
|
31
|
+
# Used by all RR types, generates: `[<label>] [<ttl>] [<class>] <type>`
|
32
|
+
#
|
33
|
+
# @return [Array<String>] rr prefix parts
|
34
|
+
def general_prefix
|
35
|
+
parts = []
|
36
|
+
parts << label
|
37
|
+
parts << ttl if ttl
|
38
|
+
parts << 'IN'
|
39
|
+
parts << type
|
40
|
+
parts
|
41
|
+
end
|
42
|
+
|
43
|
+
# Build RR zone file output.
|
44
|
+
#
|
45
|
+
# @return [String] RR zone file output
|
46
|
+
def to_s
|
47
|
+
general_prefix.join(' ')
|
48
|
+
end
|
49
|
+
|
50
|
+
# @abstract Override to update instance with RR type spesific data.
|
51
|
+
# @param string [String] RR ASCII string data
|
52
|
+
# @param options [Hash] additional data required to correctly parse a 'whole' zone
|
53
|
+
# @option options [String] :last_label The last label used by the previous RR
|
54
|
+
# @return [Object]
|
55
|
+
def load(string, options = {})
|
56
|
+
raise 'must be implemented by subclass'
|
57
|
+
end
|
58
|
+
|
59
|
+
# Load 'general' RR data/params and return the remaining RDATA for further parsing.
|
60
|
+
#
|
61
|
+
# @param string [String] RR ASCII string data
|
62
|
+
# @param options [Hash] additional data required to correctly parse a 'whole' zone
|
63
|
+
# @return [String] remaining RDATA
|
64
|
+
def load_general_and_get_rdata(string, options = {})
|
65
|
+
# strip comments, unless its escaped.
|
66
|
+
string.gsub!(/(?<!\\);.*/o, "");
|
67
|
+
|
68
|
+
captures = string.match(DNS::Zone::RR::RX_RR)
|
69
|
+
return nil unless captures
|
70
|
+
|
71
|
+
if captures[:label] == ' '
|
72
|
+
@label = options[:last_label]
|
73
|
+
else
|
74
|
+
@label = captures[:label]
|
75
|
+
end
|
76
|
+
|
77
|
+
@ttl = captures[:ttl]
|
78
|
+
captures[:rdata]
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# `SRV` resource record.
|
2
|
+
#
|
3
|
+
# RFC xxxx
|
4
|
+
class DNS::Zone::RR::SOA < DNS::Zone::RR::Record
|
5
|
+
|
6
|
+
RX_SOA_RDATA = %r{
|
7
|
+
(?<nameserver>#{DNS::Zone::RR::RX_DOMAINNAME})\s* # get nameserver domainname
|
8
|
+
(?<email>#{DNS::Zone::RR::RX_DOMAINNAME})\s* # get mailbox domainname
|
9
|
+
(?<serial>\d+)\s*
|
10
|
+
(?<refresh_ttl>#{DNS::Zone::RR::RX_TTL})\s*
|
11
|
+
(?<retry_ttl>#{DNS::Zone::RR::RX_TTL})\s*
|
12
|
+
(?<expiry_ttl>#{DNS::Zone::RR::RX_TTL})\s*
|
13
|
+
(?<minimum_ttl>#{DNS::Zone::RR::RX_TTL})\s*
|
14
|
+
}mx
|
15
|
+
|
16
|
+
attr_accessor :nameserver, :email, :serial, :refresh_ttl, :retry_ttl, :expiry_ttl, :minimum_ttl
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
parts = general_prefix
|
20
|
+
parts << nameserver
|
21
|
+
parts << email
|
22
|
+
|
23
|
+
parts << '('
|
24
|
+
parts << serial
|
25
|
+
parts << refresh_ttl
|
26
|
+
parts << retry_ttl
|
27
|
+
parts << expiry_ttl
|
28
|
+
parts << minimum_ttl
|
29
|
+
parts << ')'
|
30
|
+
parts.join(' ')
|
31
|
+
end
|
32
|
+
|
33
|
+
def load(string, options = {})
|
34
|
+
rdata = load_general_and_get_rdata(string, options)
|
35
|
+
return nil unless rdata
|
36
|
+
|
37
|
+
captures = rdata.match(RX_SOA_RDATA)
|
38
|
+
return nil unless captures
|
39
|
+
|
40
|
+
@nameserver = captures[:nameserver]
|
41
|
+
@email = captures[:email]
|
42
|
+
@serial = captures[:serial].to_i
|
43
|
+
@refresh_ttl = captures[:refresh_ttl]
|
44
|
+
@retry_ttl = captures[:retry_ttl]
|
45
|
+
@expiry_ttl = captures[:expiry_ttl]
|
46
|
+
@minimum_ttl = captures[:minimum_ttl]
|
47
|
+
|
48
|
+
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# `SRV` resource record.
|
2
|
+
#
|
3
|
+
# RFC xxxx
|
4
|
+
class DNS::Zone::RR::SRV < DNS::Zone::RR::Record
|
5
|
+
|
6
|
+
attr_accessor :priority, :weight, :port, :target
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
parts = general_prefix
|
10
|
+
parts << priority
|
11
|
+
parts << weight
|
12
|
+
parts << port
|
13
|
+
parts << target
|
14
|
+
parts.join(' ')
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# `A` resource record.
|
2
|
+
#
|
3
|
+
# RFC 1035
|
4
|
+
class DNS::Zone::RR::TXT < DNS::Zone::RR::Record
|
5
|
+
|
6
|
+
attr_accessor :text
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
parts = general_prefix
|
10
|
+
parts << %Q{"#{text}"}
|
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
|
+
# extract text from within quotes; allow multiple quoted strings; ignore escaped quotes
|
18
|
+
@text = rdata.scan(/"((?:[^"\\]+|\\.)*)"/).join
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#
|
2
|
+
# test_case.rb - A file used to setup the testing enviroment for the library.
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
|
7
|
+
# --- code coverage on MRI 1.9 ruby only, but disabled by default --------------
|
8
|
+
if RUBY_VERSION >= '1.9' && RUBY_ENGINE == 'ruby' && ENV['COVERAGE']
|
9
|
+
require 'simplecov'
|
10
|
+
#SimpleCov.command_name 'test:unit'
|
11
|
+
SimpleCov.start do
|
12
|
+
# code coverage groups.
|
13
|
+
add_filter 'test/'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# --- load our dependencies using bundler --------------------------------------
|
18
|
+
require 'bundler/setup'
|
19
|
+
Bundler.setup
|
20
|
+
require 'minitest/autorun'
|
21
|
+
require 'minitest/pride'
|
22
|
+
|
23
|
+
# --- Load lib to test ---------------------------------------------------------
|
24
|
+
require 'dns/zone'
|
25
|
+
|
26
|
+
# --- Extend DNS::Zone::TestCase -------------------------------------------------
|
27
|
+
class DNS::Zone::TestCase < Minitest::Test
|
28
|
+
end
|
data/test/rr/a_test.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'dns/zone/test_case'
|
2
|
+
|
3
|
+
class RR_A_Test < DNS::Zone::TestCase
|
4
|
+
|
5
|
+
def test_build_rr__a
|
6
|
+
rr = DNS::Zone::RR::A.new
|
7
|
+
|
8
|
+
# ensure we can set address parameter
|
9
|
+
rr.address = '10.0.1.1'
|
10
|
+
assert_equal '@ IN A 10.0.1.1', rr.to_s
|
11
|
+
rr.address = '10.0.2.2'
|
12
|
+
assert_equal '@ IN A 10.0.2.2', rr.to_s
|
13
|
+
|
14
|
+
# with a label set
|
15
|
+
rr.label = 'labelname'
|
16
|
+
assert_equal 'labelname IN A 10.0.2.2', rr.to_s
|
17
|
+
|
18
|
+
# with a ttl
|
19
|
+
rr.ttl = '4w'
|
20
|
+
assert_equal 'labelname 4w IN A 10.0.2.2', rr.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_load
|
24
|
+
rr = DNS::Zone::RR::A.new.load('@ IN A 127.0.0.1')
|
25
|
+
assert_equal '@', rr.label
|
26
|
+
assert_equal 'A', rr.type
|
27
|
+
assert_equal '127.0.0.1', rr.address
|
28
|
+
|
29
|
+
rr = DNS::Zone::RR::A.new.load('www IN A 127.0.0.1')
|
30
|
+
assert_equal 'www', rr.label
|
31
|
+
assert_equal 'A', rr.type
|
32
|
+
assert_equal 'IN', rr.klass
|
33
|
+
assert_equal '127.0.0.1', rr.address
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'dns/zone/test_case'
|
2
|
+
|
3
|
+
class RR_AAAA_Test < DNS::Zone::TestCase
|
4
|
+
|
5
|
+
def test_build_rr__aaaa
|
6
|
+
rr = DNS::Zone::RR::AAAA.new
|
7
|
+
|
8
|
+
# ensure we can set address parameter
|
9
|
+
rr.address = '2001:db8::3'
|
10
|
+
assert_equal '@ IN AAAA 2001:db8::3', rr.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'dns/zone/test_case'
|
2
|
+
|
3
|
+
class RR_CNAME_Test < DNS::Zone::TestCase
|
4
|
+
|
5
|
+
def test_build_rr__cname
|
6
|
+
rr = DNS::Zone::RR::CNAME.new
|
7
|
+
rr.label = 'google9d97d7f266ee521d'
|
8
|
+
rr.domainname = 'google.com.'
|
9
|
+
assert_equal 'google9d97d7f266ee521d IN CNAME google.com.', rr.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
data/test/rr/mx_test.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'dns/zone/test_case'
|
2
|
+
|
3
|
+
class RR_MX_Test < DNS::Zone::TestCase
|
4
|
+
|
5
|
+
def test_build_rr__mx
|
6
|
+
rr = DNS::Zone::RR::MX.new
|
7
|
+
|
8
|
+
# ensure we can set preference and exchange parameter
|
9
|
+
rr.preference = '10'
|
10
|
+
rr.exchange = 'mx0.lividpenguin.com.'
|
11
|
+
assert_equal '@ IN MX 10 mx0.lividpenguin.com.', rr.to_s
|
12
|
+
|
13
|
+
rr.preference = '20'
|
14
|
+
rr.exchange = 'mx1.lividpenguin.com.'
|
15
|
+
assert_equal '@ IN MX 20 mx1.lividpenguin.com.', rr.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/test/rr/ns_test.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'dns/zone/test_case'
|
2
|
+
|
3
|
+
class RR_Record_Test < DNS::Zone::TestCase
|
4
|
+
|
5
|
+
def test_rr_record_defaults
|
6
|
+
rr = DNS::Zone::RR::Record.new
|
7
|
+
assert_equal '@', rr.label, 'label is @, by default'
|
8
|
+
assert_equal nil, rr.ttl, 'ttl is nil, by default'
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_rr_record_with_label
|
12
|
+
rr = DNS::Zone::RR::Record.new
|
13
|
+
rr.label = 'labelname'
|
14
|
+
assert_equal 'labelname IN <type>', rr.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_rr_record_with_label_and_ttl
|
18
|
+
rr = DNS::Zone::RR::Record.new
|
19
|
+
rr.label = 'labelname'
|
20
|
+
rr.ttl = '2d'
|
21
|
+
assert_equal 'labelname 2d IN <type>', rr.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_rr_record_with_ttl
|
25
|
+
rr = DNS::Zone::RR::Record.new
|
26
|
+
rr.ttl = '2d'
|
27
|
+
assert_equal '@ 2d IN <type>', rr.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/test/rr/soa_test.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'dns/zone/test_case'
|
2
|
+
|
3
|
+
class RR_SRV_Test < DNS::Zone::TestCase
|
4
|
+
|
5
|
+
EXAMPLE_SOA_IN = '@ IN SOA ns0.lividpenguin.com. luke.lividpenguin.com. 2014021601 3h 15m 4w 30m'
|
6
|
+
EXAMPLE_SOA_OUT = '@ IN SOA ns0.lividpenguin.com. luke.lividpenguin.com. ( 2014021601 3h 15m 4w 30m )'
|
7
|
+
|
8
|
+
def test_build
|
9
|
+
rr = DNS::Zone::RR::SOA.new
|
10
|
+
rr.nameserver = 'ns0.lividpenguin.com.'
|
11
|
+
rr.email = 'luke.lividpenguin.com.'
|
12
|
+
rr.serial = 2014_02_16_01
|
13
|
+
rr.refresh_ttl = '3h'
|
14
|
+
rr.retry_ttl = '15m'
|
15
|
+
rr.expiry_ttl = '4w'
|
16
|
+
rr.minimum_ttl = '30m'
|
17
|
+
assert_equal EXAMPLE_SOA_OUT, rr.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_load
|
21
|
+
rr = DNS::Zone::RR::SOA.new.load(EXAMPLE_SOA_IN)
|
22
|
+
assert_equal '@', rr.label
|
23
|
+
assert_equal 'SOA', rr.type
|
24
|
+
|
25
|
+
assert_equal 'ns0.lividpenguin.com.', rr.nameserver
|
26
|
+
assert_equal 'luke.lividpenguin.com.', rr.email
|
27
|
+
assert_equal 2014_02_16_01, rr.serial
|
28
|
+
assert_equal '3h', rr.refresh_ttl
|
29
|
+
assert_equal '15m', rr.retry_ttl
|
30
|
+
assert_equal '4w', rr.expiry_ttl
|
31
|
+
assert_equal '30m', rr.minimum_ttl
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/test/rr/srv_test.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'dns/zone/test_case'
|
2
|
+
|
3
|
+
class RR_SRV_Test < DNS::Zone::TestCase
|
4
|
+
|
5
|
+
def test_build_rr__srv
|
6
|
+
rr = DNS::Zone::RR::SRV.new
|
7
|
+
rr.priority = 5
|
8
|
+
rr.weight = 0
|
9
|
+
rr.port = 5269
|
10
|
+
rr.target = 'xmpp-server.l.google.com.'
|
11
|
+
assert_equal '@ IN SRV 5 0 5269 xmpp-server.l.google.com.', rr.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/test/rr/txt_test.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'dns/zone/test_case'
|
2
|
+
|
3
|
+
class RR_TXT_Test < DNS::Zone::TestCase
|
4
|
+
|
5
|
+
def test_build_rr__txt
|
6
|
+
rr = DNS::Zone::RR::TXT.new
|
7
|
+
|
8
|
+
# ensure we can set text parameter
|
9
|
+
rr.text = 'test text'
|
10
|
+
assert_equal '@ IN TXT "test text"', rr.to_s
|
11
|
+
|
12
|
+
# with a label set
|
13
|
+
rr.label = 'labelname'
|
14
|
+
assert_equal 'labelname IN TXT "test text"', rr.to_s
|
15
|
+
|
16
|
+
# with a ttl
|
17
|
+
rr.ttl = '2w'
|
18
|
+
assert_equal 'labelname 2w IN TXT "test text"', rr.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_load
|
22
|
+
rr = DNS::Zone::RR::TXT.new.load('txtrecord IN TXT "test text"')
|
23
|
+
assert_equal 'txtrecord', rr.label
|
24
|
+
assert_equal 'IN', rr.klass
|
25
|
+
assert_equal 'TXT', rr.type
|
26
|
+
assert_equal 'test text', rr.text
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_load_multiple_quoted_strings
|
30
|
+
rr = DNS::Zone::RR::TXT.new.load('txtrecord IN TXT "part1 yo" " part2 yo"')
|
31
|
+
assert_equal 'part1 yo part2 yo', rr.text
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_load_string_with_quotes
|
35
|
+
rr = DNS::Zone::RR::TXT.new.load('txtrecord IN TXT "we have \"double\" quotes"')
|
36
|
+
assert_equal %q{we have \"double\" quotes}, rr.text
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_load_multiple_strings_with_quotes
|
40
|
+
rr = DNS::Zone::RR::TXT.new.load('txtrecord IN TXT "part1 " "we have \"double\" quotes" " part3"')
|
41
|
+
assert_equal %q{part1 we have \"double\" quotes part3}, rr.text
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/test/rr_test.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'dns/zone/test_case'
|
2
|
+
|
3
|
+
class RRTest < DNS::Zone::TestCase
|
4
|
+
|
5
|
+
def test_load_ignores_comments
|
6
|
+
rr = DNS::Zone::RR.load('; comment lines are ignored')
|
7
|
+
assert_nil rr, 'should be nil when its a comment line'
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_load_ignores_unparsable_input
|
11
|
+
rr = DNS::Zone::RR.load('invalid input should not be parsed')
|
12
|
+
assert_nil rr, 'should be nil when input cant be parsed at all'
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_load_a_rr
|
16
|
+
rr = DNS::Zone::RR.load('www IN A 10.2.3.1')
|
17
|
+
assert_instance_of DNS::Zone::RR::A, rr, 'should be instance of A RR'
|
18
|
+
assert_equal 'www', rr.label
|
19
|
+
assert_equal 'A', rr.type
|
20
|
+
assert_equal '10.2.3.1', rr.address
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_load_txt_rr
|
24
|
+
rr = DNS::Zone::RR.load('mytxt IN TXT "test text"')
|
25
|
+
assert_instance_of DNS::Zone::RR::TXT, rr, 'should be instance of TXT RR'
|
26
|
+
assert_equal 'mytxt', rr.label
|
27
|
+
assert_equal 'TXT', rr.type
|
28
|
+
assert_equal 'test text', rr.text
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_load_a_rr_with_options_hash
|
32
|
+
rr = DNS::Zone::RR.load(' IN A 10.2.3.1', { last_label: 'www' })
|
33
|
+
assert_equal 'www', rr.label
|
34
|
+
|
35
|
+
rr = DNS::Zone::RR.load('blog IN A 10.2.3.1', { last_label: 'www' })
|
36
|
+
assert_equal 'blog', rr.label
|
37
|
+
|
38
|
+
rr = DNS::Zone::RR.load('@ IN A 10.2.3.1', { last_label: 'mail' })
|
39
|
+
assert_equal '@', rr.label
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/test/zone_test.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'dns/zone/test_case'
|
2
|
+
|
3
|
+
class ZoneTest < DNS::Zone::TestCase
|
4
|
+
|
5
|
+
ZONE_EXAMPLE_SIMPLE =<<-EOL
|
6
|
+
$ORIGIN lividpenguin.com.
|
7
|
+
$TTL 3d
|
8
|
+
@ IN SOA ns0.lividpenguin.com. luke.lividpenguin.com. (
|
9
|
+
2013101406 ; zone serial number
|
10
|
+
12h ; refresh ttl
|
11
|
+
15m ; retry ttl
|
12
|
+
3w ; expiry ttl
|
13
|
+
3h ; minimum ttl
|
14
|
+
)
|
15
|
+
|
16
|
+
; a more difficult ; comment ( its trying to break things!
|
17
|
+
|
18
|
+
@ IN NS ns0.lividpenguin.com.
|
19
|
+
@ IN NS ns1.lividpenguin.com.
|
20
|
+
@ IN NS ns2.lividpenguin.com.
|
21
|
+
|
22
|
+
@ IN A 77.75.105.197
|
23
|
+
@ IN AAAA 2a01:348::6:4d4b:69c5:0:1
|
24
|
+
|
25
|
+
foo IN TXT "part1""part2"
|
26
|
+
bar IN TXT ("part1 "
|
27
|
+
"part2 "
|
28
|
+
"part3")
|
29
|
+
EOL
|
30
|
+
|
31
|
+
def test_create_new_instance
|
32
|
+
assert DNS::Zone.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_load_simple_zone
|
36
|
+
zone = DNS::Zone.load(ZONE_EXAMPLE_SIMPLE)
|
37
|
+
assert_equal '3d', zone.ttl, 'check ttl matches example input'
|
38
|
+
assert_equal 'lividpenguin.com.', zone.origin, 'check origin matches example input'
|
39
|
+
assert_equal 8, zone.records.length, 'we should have 8 records (including SOA)'
|
40
|
+
|
41
|
+
p ''
|
42
|
+
zone.records.each do |rec|
|
43
|
+
p rec
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_extract_entry_from_one_line
|
48
|
+
entries = DNS::Zone.extract_entries(%Q{maiow IN TXT "purr"})
|
49
|
+
assert_equal 1, entries.length, 'we should have 1 entry'
|
50
|
+
assert_equal 'maiow IN TXT "purr"', entries[0], 'entry should match expected'
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_extract_entry_should_ignore_comments
|
54
|
+
entries = DNS::Zone.extract_entries(%Q{maiow IN TXT "purr"; this is a comment})
|
55
|
+
assert_equal 1, entries.length, 'we should have 1 entry'
|
56
|
+
assert_equal 'maiow IN TXT "purr"', entries[0], 'entry should match expected'
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_extract_entry_should_ignore_empty_lines
|
60
|
+
entries = DNS::Zone.extract_entries(%Q{\n\nmaiow IN TXT "purr";\n\n})
|
61
|
+
assert_equal 1, entries.length, 'we should have 1 entry'
|
62
|
+
assert_equal 'maiow IN TXT "purr"', entries[0], 'entry should match expected'
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_extract_entry_using_parentheses_but_not_crossing_line_boundary
|
66
|
+
entries = DNS::Zone.extract_entries(%Q{maiow IN TXT ("part1" "part2")})
|
67
|
+
assert_equal 1, entries.length, 'we should have 1 entry'
|
68
|
+
assert_equal 'maiow IN TXT "part1" "part2"', entries[0], 'entry should match expected'
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_extract_entry_crossing_line_boundary
|
72
|
+
entries = DNS::Zone.extract_entries(%Q{maiow1 IN TXT ("part1"\n "part2" )})
|
73
|
+
assert_equal 1, entries.length, 'we should have 1 entry'
|
74
|
+
assert_equal 'maiow1 IN TXT "part1" "part2"', entries[0], 'entry should match expected'
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_extract_entry_soa_crossing_line_boundary
|
78
|
+
entries = DNS::Zone.extract_entries(%Q{
|
79
|
+
@ IN SOA ns0.lividpenguin.com. luke.lividpenguin.com. (
|
80
|
+
2013101406 ; zone serial number
|
81
|
+
12h ; refresh ttl
|
82
|
+
15m ; retry ttl
|
83
|
+
3w ; expiry ttl
|
84
|
+
3h ; minimum ttl
|
85
|
+
)})
|
86
|
+
assert_equal 1, entries.length, 'we should have 1 entry'
|
87
|
+
|
88
|
+
expected_soa = '@ IN SOA ns0.lividpenguin.com. luke.lividpenguin.com. 2013101406 12h 15m 3w 3h'
|
89
|
+
assert_equal expected_soa, entries[0], 'entry should match expected'
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_extract_entries_with_parentheses_crossing_multiple_line_boundaries
|
93
|
+
entries = DNS::Zone.extract_entries(%Q{maiow1 IN TXT (\n"part1"\n "part2"\n)})
|
94
|
+
assert_equal 1, entries.length, 'we should have 1 entry'
|
95
|
+
assert_equal 'maiow1 IN TXT "part1" "part2"', entries[0], 'entry should match expected'
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_extract_entries_with_legal_but_crazy_parentheses_used
|
99
|
+
entries = DNS::Zone.extract_entries(%Q{maiow IN TXT (\n(\n("part1")\n \n("part2" \n("part3"\n)\n)\n)\n)})
|
100
|
+
assert_equal 1, entries.length, 'we should have 1 entry'
|
101
|
+
assert_equal 'maiow IN TXT "part1" "part2" "part3"', entries[0], 'entry should match expected'
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_extract_entry_with_parentheses_in_character_string
|
105
|
+
entries = DNS::Zone.extract_entries(%Q{maiow IN TXT ("purr((maiow)")})
|
106
|
+
assert_equal 1, entries.length, 'we should have 1 entry'
|
107
|
+
assert_equal 'maiow IN TXT "purr((maiow)"', entries[0], 'entry should match expected'
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
end
|
metadata
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dns-zone
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0.alpha
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Luke Antins
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.7'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.7'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: inch
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: guard-minitest
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: guard-bundler
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: |
|
126
|
+
A Ruby library for building and parsing DNS zone files for use with
|
127
|
+
Bind and PowerDNS (with Bind backend) DNS servers.
|
128
|
+
email:
|
129
|
+
- luke@lividpenguin.com
|
130
|
+
executables: []
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- Gemfile
|
135
|
+
- Guardfile
|
136
|
+
- HISTORY.md
|
137
|
+
- README.md
|
138
|
+
- Rakefile
|
139
|
+
- dns-zone.gemspec
|
140
|
+
- lib/dns/zone.rb
|
141
|
+
- lib/dns/zone/rr.rb
|
142
|
+
- lib/dns/zone/rr/a.rb
|
143
|
+
- lib/dns/zone/rr/aaaa.rb
|
144
|
+
- lib/dns/zone/rr/cname.rb
|
145
|
+
- lib/dns/zone/rr/mx.rb
|
146
|
+
- lib/dns/zone/rr/ns.rb
|
147
|
+
- lib/dns/zone/rr/record.rb
|
148
|
+
- lib/dns/zone/rr/soa.rb
|
149
|
+
- lib/dns/zone/rr/srv.rb
|
150
|
+
- lib/dns/zone/rr/txt.rb
|
151
|
+
- lib/dns/zone/test_case.rb
|
152
|
+
- lib/dns/zone/version.rb
|
153
|
+
- test/rr/a_test.rb
|
154
|
+
- test/rr/aaaa_test.rb
|
155
|
+
- test/rr/cname_test.rb
|
156
|
+
- test/rr/mx_test.rb
|
157
|
+
- test/rr/ns_test.rb
|
158
|
+
- test/rr/record_test.rb
|
159
|
+
- test/rr/soa_test.rb
|
160
|
+
- test/rr/srv_test.rb
|
161
|
+
- test/rr/txt_test.rb
|
162
|
+
- test/rr_test.rb
|
163
|
+
- test/version_test.rb
|
164
|
+
- test/zone_test.rb
|
165
|
+
homepage: https://github.com/lantins/dns-zone
|
166
|
+
licenses:
|
167
|
+
- MIT
|
168
|
+
metadata: {}
|
169
|
+
post_install_message:
|
170
|
+
rdoc_options: []
|
171
|
+
require_paths:
|
172
|
+
- lib
|
173
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
174
|
+
requirements:
|
175
|
+
- - "~>"
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
version: '1.9'
|
178
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
|
+
requirements:
|
180
|
+
- - ">"
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
version: 1.3.1
|
183
|
+
requirements: []
|
184
|
+
rubyforge_project:
|
185
|
+
rubygems_version: 2.2.0
|
186
|
+
signing_key:
|
187
|
+
specification_version: 4
|
188
|
+
summary: A Ruby library for building and parsing DNS zone files.
|
189
|
+
test_files: []
|
190
|
+
has_rdoc:
|