bind-zone-parser 0.0.0

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.
@@ -0,0 +1,107 @@
1
+ %%{
2
+ machine bind_parser;
3
+
4
+ action read_number { num = (num * 10) + (fc - ?0) }
5
+ action clear_number { num = 0 }
6
+ action clear_str_p { str_p = p }
7
+
8
+ action store_dist { record[:dist] = num }
9
+ action store_ttl { record[:ttl] = num }
10
+ action store_priority { record[:priority] = num }
11
+ action store_weight { record[:weight] = num }
12
+ action store_port { record[:port] = num }
13
+ action store_serial { record[:serial] = num }
14
+ action store_refresh { record[:refresh] = num }
15
+ action store_retry { record[:retry] = num }
16
+ action store_expire { record[:expire] = num }
17
+ action store_minimum { record[:minimum] = num }
18
+
19
+ action store_owner { record[:owner] = data[str_p..p].pack('c*').strip }
20
+ action store_target { record[:target] = data[str_p..p].pack('c*').strip }
21
+ action store_mbox { record[:mbox] = data[str_p..p].pack('c*').strip }
22
+ action store_text { record[:text] = data[(str_p+1)..(p-2)].pack('c*').strip }
23
+ action store_ipv4 { record[:address] = data[str_p..p].pack('c*').strip }
24
+ action store_name { record[:domain] = data[str_p..p].pack('c*').strip }
25
+
26
+ action a { record[:type] = :a }
27
+ action ns { record[:type] = :ns }
28
+ action mx { record[:type] = :mx }
29
+ action txt { record[:type] = :txt }
30
+ action ptr { record[:type] = :ptr }
31
+ action soa { record[:type] = :soa }
32
+ action srv { record[:type] = :srv }
33
+ action cname { record[:type] = :cname }
34
+
35
+ sp = space+;
36
+ newline = "\n";
37
+ comment = space* ";" [^\n]* newline;
38
+ endofline = comment | newline @{ records << record; record = Hash.new };
39
+
40
+ escapes = ('\\' [^0-9\n]) | ('\\' digit{3});
41
+ quotedstr = ('"' ([^"\n\\]|escapes)* '"') >clear_str_p %store_text;
42
+
43
+ ipv4_octet = ("25" [0-5] | "2" [01234] [0-9] | [01]? [0-9]{1,2});
44
+ ipv4_address = (ipv4_octet "." ipv4_octet "." ipv4_octet "." ipv4_octet) >clear_str_p %store_ipv4;
45
+
46
+ origin = "@";
47
+ alnumhyphen = (alnum | "-");
48
+ dname = (alnumhyphen+ ".")* alnumhyphen+ "."?;
49
+ fqdname = dname >clear_str_p %store_name;
50
+ owner = (sp | origin | dname) >clear_str_p %store_owner;
51
+ dist = (digit @read_number)+ >clear_number %store_dist;
52
+ ttl = (digit @read_number)+ >clear_number %store_ttl;
53
+ priority = (digit @read_number)+ >clear_number %store_priority;
54
+ weight = (digit @read_number)+ >clear_number %store_weight;
55
+ port = (digit @read_number)+ >clear_number %store_port;
56
+ serial = (digit @read_number)+ >clear_number %store_serial;
57
+ refresh = (digit @read_number)+ >clear_number %store_refresh;
58
+ retry = (digit @read_number)+ >clear_number %store_retry;
59
+ expire = (digit @read_number)+ >clear_number %store_expire;
60
+ minimum = (digit @read_number)+ >clear_number %store_minimum;
61
+ target = ("." | dname) >clear_str_p %store_target;
62
+ srv_o = ("_" alnumhyphen+ "." ("_tcp" | "_udp") ("." dname)?) >clear_str_p %store_owner;
63
+ mbox = dname >clear_str_p %store_mbox;
64
+
65
+ eol_or_sp = sp | (endofline sp);
66
+ soa_rdata = "(" sp? serial eol_or_sp refresh eol_or_sp retry eol_or_sp expire eol_or_sp minimum sp? ")";
67
+
68
+ host_record = owner (sp ttl)? (sp "IN")? sp ("A" @a) sp ipv4_address endofline;
69
+ ns_record = owner (sp ttl)? (sp "IN")? sp ("NS" @ns) sp fqdname endofline;
70
+ mx_record = owner (sp ttl)? (sp "IN")? sp ("MX" @mx) sp dist sp fqdname endofline;
71
+ txt_record = owner (sp ttl)? (sp "IN")? sp ("TXT" @txt) sp quotedstr endofline;
72
+ ptr_record = owner (sp ttl)? (sp "IN")? sp ("PTR" @ptr) sp fqdname endofline;
73
+ cname_record = owner (sp ttl)? (sp "IN")? sp ("CNAME" @cname) sp fqdname endofline;
74
+ soa_record = owner (sp ttl)? sp "IN" sp ("SOA" @soa) sp fqdname sp mbox sp soa_rdata endofline;
75
+ srv_record = srv_o (sp ttl)? (sp "IN")? sp ("SRV" @srv) sp priority sp weight sp port sp target endofline;
76
+
77
+ record = host_record
78
+ | ns_record
79
+ | mx_record
80
+ | txt_record
81
+ | ptr_record
82
+ | cname_record
83
+ | soa_record
84
+ | srv_record;
85
+
86
+ main := (newline | comment | record)*;
87
+ }%%
88
+
89
+ module Bind
90
+ class ZoneParser
91
+ def self.parse(zone)
92
+ new.parse(zone)
93
+ end
94
+ def parse(zone)
95
+ data = zone.unpack('c*')
96
+ records = []
97
+ record = {}
98
+
99
+ %% write data;
100
+ %% write init;
101
+ %% write exec;
102
+
103
+ return records if p == pe && cs == bind_parser_first_final
104
+ raise "cs: #{cs} p: #{p} pe: #{pe} data[p]: #{data[p] ? data[p].chr.inspect : 'nil'}"
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,21 @@
1
+ ; Zone database file for example.com zone
2
+
3
+ @ 86400 IN SOA ns1.example.com. hostmaster (
4
+ 2010080201 ; Serial number
5
+ 3600 ; Refresh
6
+ 900 ; Retry
7
+ 604800 ; Expire
8
+ 14400 ) ; Minimum TTL
9
+ NS ns1.example.com.
10
+ NS ns2.example.com.
11
+ A 172.16.147.66
12
+ MX 100 mx1.example-mail.com.
13
+ MX 200 mx2.example-mail.com.
14
+ TXT "v=spf1 a mx ip4:192.168.1.75 ?all"
15
+ www A 172.16.147.66
16
+ sso A 172.16.169.185
17
+ jabber A 172.16.157.212
18
+ _jabber._tcp SRV 10 10 5269 jabber ; Jabber Server
19
+ _xmpp-client._tcp SRV 10 10 5222 jabber ; Jabber Server
20
+ _xmpp-server._tcp SRV 10 10 5269 jabber ; Jabber Server
21
+ xmpp CNAME jabber
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'bind-zone-parser'
8
+
9
+ class Test::Unit::TestCase
10
+ end
data/test/test_zone.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ class TestBindZone < Test::Unit::TestCase
4
+ context ".load_file" do
5
+ setup do
6
+ @zone_file = File.expand_path('../examples/example.com.db', __FILE__)
7
+ end
8
+ should "return new zone with the records from the file" do
9
+ @zone = Bind::Zone.load_file(@zone_file)
10
+ assert !@zone.records.empty?
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,164 @@
1
+ require 'helper'
2
+
3
+ class TestBindZoneParser < Test::Unit::TestCase
4
+ context "#parse" do
5
+ setup do
6
+ @zone_parser = Bind::ZoneParser.new
7
+ end
8
+ context "Comments" do
9
+ should "parse comment" do
10
+ assert @zone_parser.parse(";$; Updated with Simple DNS Plus record editor 2 Aug 2010 13:57:42\n; Zone database file for m247.com zone\n")
11
+ end
12
+ end
13
+ context "Host Record" do
14
+ should "parse subdomain" do
15
+ assert @zone_parser.parse("www IN A 192.168.1.68\n")
16
+ end
17
+ should "parse dotted subdomain" do
18
+ assert @zone_parser.parse("a.mx IN A 192.168.1.69\n")
19
+ end
20
+ should "parse qualified owner" do
21
+ assert @zone_parser.parse("www.example.com. IN A 192.168.1.70\n")
22
+ end
23
+ should "parse @ origin owner" do
24
+ assert @zone_parser.parse("@ IN A 192.168.1.71\n")
25
+ end
26
+ should "parse blank owner" do
27
+ assert @zone_parser.parse(" IN A 192.168.1.72\n")
28
+ end
29
+ should "parse with ttl" do
30
+ assert @zone_parser.parse(" 300 IN A 192.168.1.73\n")
31
+ end
32
+ should "parse with ending comment" do
33
+ assert @zone_parser.parse(" IN A 192.168.1.74 ; Example record\n")
34
+ end
35
+ should "parse with out IN defined" do
36
+ assert @zone_parser.parse(" A 192.168.1.76\n")
37
+ end
38
+ end
39
+ context "Name Server Record" do
40
+ should "parse @ origin owner, subdomain host" do
41
+ assert @zone_parser.parse("@ IN NS ns1\n")
42
+ end
43
+ should "parse @ origin owner, dotted subdomain host" do
44
+ assert @zone_parser.parse("@ IN NS a.ns\n")
45
+ end
46
+ should "parse @ origin owner, qualified host" do
47
+ assert @zone_parser.parse("@ IN NS a.ns.example.com.\n")
48
+ end
49
+ should "parse blank owner, subdomain host" do
50
+ assert @zone_parser.parse(" IN NS ns1\n")
51
+ end
52
+ should "parse blank owner, dotted subdomain host" do
53
+ assert @zone_parser.parse(" IN NS a.ns\n")
54
+ end
55
+ should "parse blank owner, qualified host" do
56
+ assert @zone_parser.parse(" IN NS a.ns.example.com.\n")
57
+ end
58
+ should "parse sub delegation, subdomain host" do
59
+ assert @zone_parser.parse("hr IN NS hr-ns1\n")
60
+ end
61
+ should "parse sub delegation, dotted subdomain host" do
62
+ assert @zone_parser.parse("hr IN NS a.ns-hr\n")
63
+ end
64
+ should "parse sub delegation, qualified host" do
65
+ assert @zone_parser.parse("hr IN NS hr-ns1.example.com\n")
66
+ end
67
+ should "parse qualified delegation" do
68
+ assert @zone_parser.parse("another.com. IN NS ns1\n")
69
+ end
70
+ end
71
+ context "Mail Exchange Record" do
72
+ should "parse @ origin owner, subdomain host" do
73
+ assert @zone_parser.parse("@ IN MX 10 mx1\n")
74
+ end
75
+ should "parse @ origin owner, dotted subdomain host" do
76
+ assert @zone_parser.parse("@ IN MX 10 a.mx\n")
77
+ end
78
+ should "parse @ origin owner, qualified host" do
79
+ assert @zone_parser.parse("@ IN MX 10 a.mx.example.com.\n")
80
+ end
81
+ should "parse blank owner, subdomain host" do
82
+ assert @zone_parser.parse(" IN MX 10 mx1\n")
83
+ end
84
+ should "parse blank owner, dotted subdomain host" do
85
+ assert @zone_parser.parse(" IN MX 10 a.mx\n")
86
+ end
87
+ should "parse blank owner, qualified host" do
88
+ assert @zone_parser.parse(" IN MX 10 a.mx.example.com.\n")
89
+ end
90
+ end
91
+ context "Text Record" do
92
+ should "parse subdomain owner" do
93
+ assert @zone_parser.parse("mail IN TXT \"v=spf1 a mx ip4:192.168.1.75 ?all\"\n")
94
+ end
95
+ should "parse dotted subdomain owner" do
96
+ assert @zone_parser.parse("a.mx IN TXT \"v=spf1 a mx ip4:192.168.1.75 ?all\"\n")
97
+ end
98
+ should "parse qualified owner" do
99
+ assert @zone_parser.parse("example.com. IN TXT \"v=spf1 a mx ip4:192.168.1.75 ?all\"\n")
100
+ end
101
+ should "parse @ origin owner" do
102
+ assert @zone_parser.parse("@ IN TXT \"v=spf1 a mx ip4:192.168.1.75 ?all\"\n")
103
+ end
104
+ should "parse blank owner" do
105
+ assert @zone_parser.parse(" IN TXT \"v=spf1 a mx ip4:192.168.1.75 ?all\"\n")
106
+ end
107
+ end
108
+ context "Pointer Record" do
109
+ should "parse octet owner" do
110
+ assert @zone_parser.parse("69 IN PTR a.mx.example.com.\n")
111
+ end
112
+ should "parse multiple octet owner" do
113
+ assert @zone_parser.parse("69.1 IN PTR a.mx.example.com.\n")
114
+ end
115
+ should "parse qualified in-addr.arpa" do
116
+ assert @zone_parser.parse("69.1.168.192.in-addr.arpa. IN PTR a.mx.example.com.\n")
117
+ end
118
+ end
119
+ context "CNAME Record" do
120
+ should "parse subdomain to subdomain" do
121
+ assert @zone_parser.parse("webmail IN CNAME mail\n")
122
+ end
123
+ should "parse subdomain to qualified domain" do
124
+ assert @zone_parser.parse("webmail IN CNAME mail.example.com.\n")
125
+ end
126
+ end
127
+ context "SRV Record" do
128
+ should "parse subdomain to subdomain" do
129
+ assert @zone_parser.parse("_jabber._tcp IN SRV 10 10 5269 jabber\n")
130
+ end
131
+ should "parse qualified to subdomain" do
132
+ assert @zone_parser.parse("_xmpp-client._tcp.example.com. IN SRV 10 10 5222 jabber\n")
133
+ end
134
+ should "parse qualified to qualified" do
135
+ assert @zone_parser.parse("_xmpp-server._tcp.example.com. IN SRV 10 10 5269 jabber.example.com.\n")
136
+ end
137
+ end
138
+ context "SOA Record" do
139
+ should "parse single, @ origin owner, short source, short mbox" do
140
+ assert @zone_parser.parse("@ IN SOA ns1 hostmaster (1282731423 16384 2048 1048576 2560)\n")
141
+ end
142
+ should "parse single, @ origin owner, short source, qualified mbox" do
143
+ assert @zone_parser.parse("@ IN SOA ns1 dns.example.com. (1282731423 16384 2048 1048576 2560)\n")
144
+ end
145
+ should "parse single, @ origin owner, qualified source, qualified mbox" do
146
+ assert @zone_parser.parse("@ IN SOA ns1.dns.com. dns.example.com. (1282731423 16384 2048 1048576 2560)\n")
147
+ end
148
+ should "parse single, qualified origin, source and mbox" do
149
+ assert @zone_parser.parse("example.com. IN SOA ns1.dns.com. dns.example.com. (1282731423 16384 2048 1048576 2560)\n")
150
+ end
151
+ should "parse multiline rdata" do
152
+ multiline = <<-EOF
153
+ @ 86400 IN SOA ns1.example.com. hostmaster (
154
+ 1282731423 ; Serial number
155
+ 16384 ; Refresh
156
+ 2048 ; Retry
157
+ 1048576 ; Expire
158
+ 2560 ) ; Minimum TTL
159
+ EOF
160
+ assert @zone_parser.parse(multiline)
161
+ end
162
+ end
163
+ end
164
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bind-zone-parser
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 0
10
+ version: 0.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Geoff Garside
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-25 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: shoulda
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: yard
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
49
+ description: Helps with parsing records and data from BIND Zone files
50
+ email: geoff@geoffgarside.co.uk
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files:
56
+ - LICENSE
57
+ - README.rdoc
58
+ files:
59
+ - .document
60
+ - .gitignore
61
+ - LICENSE
62
+ - README.rdoc
63
+ - Rakefile
64
+ - VERSION
65
+ - bind-zone-parser.gemspec
66
+ - lib/bind-zone-parser.rb
67
+ - lib/bind/zone.rb
68
+ - lib/bind/zone_parser.rb
69
+ - lib/bind/zone_parser.rl
70
+ - test/examples/example.com.db
71
+ - test/helper.rb
72
+ - test/test_zone.rb
73
+ - test/test_zone_parser.rb
74
+ has_rdoc: true
75
+ homepage: http://github.com/geoffgarside/bind-zone-parser
76
+ licenses: []
77
+
78
+ post_install_message:
79
+ rdoc_options:
80
+ - --charset=UTF-8
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ hash: 3
98
+ segments:
99
+ - 0
100
+ version: "0"
101
+ requirements: []
102
+
103
+ rubyforge_project:
104
+ rubygems_version: 1.3.7
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: BIND Zone file parser
108
+ test_files:
109
+ - test/helper.rb
110
+ - test/test_zone.rb
111
+ - test/test_zone_parser.rb