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.
- 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
|