bind-zone-parser 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +33 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/bind-zone-parser.gemspec +63 -0
- data/lib/bind-zone-parser.rb +2 -0
- data/lib/bind/zone.rb +11 -0
- data/lib/bind/zone_parser.rb +1495 -0
- data/lib/bind/zone_parser.rl +107 -0
- data/test/examples/example.com.db +21 -0
- data/test/helper.rb +10 -0
- data/test/test_zone.rb +13 -0
- data/test/test_zone_parser.rb +164 -0
- metadata +111 -0
@@ -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
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
|