bind-zone-parser 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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