zonesync 0.6.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,323 @@
1
+ require "treetop"
2
+ Treetop.load File.join(__dir__, "zonefile")
3
+
4
+ module Zonesync
5
+ module Zonefile
6
+ class << self
7
+ def parse(zone_string)
8
+ parser = ZonefileParser.new
9
+ result = parser.parse(zone_string)
10
+ return result if result
11
+ puts zone_string
12
+ raise ParsingError, parser.failure_reason
13
+ end
14
+
15
+ def load(zone_string)
16
+ parsed = parse(zone_string)
17
+ Zone.new(parsed.entries,
18
+ origin: parsed.variables["ORIGIN"],
19
+ )
20
+ end
21
+ end
22
+
23
+ class ParsingError < RuntimeError; end
24
+ class UnknownRecordType < RuntimeError; end
25
+ class Zone
26
+ attr_reader :origin
27
+ attr_reader :records
28
+
29
+ def initialize(entries, origin: nil, alternate_origin: ".")
30
+ @origin = origin
31
+ @records = []
32
+ @vars = {"origin" => alternate_origin, :last_host => "."}
33
+ entries.each do |e|
34
+ case e.parse_type
35
+ when :variable
36
+ key = e.name.text_value.downcase
37
+ @vars[key] = case key
38
+ when "ttl"
39
+ e.value.text_value.to_i
40
+ else
41
+ e.value.text_value
42
+ end
43
+ when :soa
44
+ @records << SOA.new(@vars, e)
45
+ when :record
46
+ case e.record_type
47
+ when "A" then @records << A.new(@vars, e)
48
+ when "AAAA" then @records << AAAA.new(@vars, e)
49
+ when "CAA" then @records << CAA.new(@vars, e)
50
+ when "CNAME" then @records << CNAME.new(@vars, e)
51
+ when "MX" then @records << MX.new(@vars, e)
52
+ when "NAPTR" then @records << NAPTR.new(@vars, e)
53
+ when "NS" then @records << NS.new(@vars, e)
54
+ when "PTR" then @records << PTR.new(@vars, e)
55
+ when "SRV" then @records << SRV.new(@vars, e)
56
+ when "SPF" then @records << SPF.new(@vars, e)
57
+ when "SSHFP" then @records << SSHFP.new(@vars, e)
58
+ when "TXT" then @records << TXT.new(@vars, e)
59
+ when "SOA" then
60
+ # No-op
61
+ else
62
+ raise UnknownRecordType, "Unknown record type: #{e.record_type}"
63
+ end
64
+ end
65
+ end
66
+ @origin ||= soa.origin
67
+ end
68
+
69
+ def soa
70
+ records_of(SOA).first
71
+ end
72
+
73
+ def records_of(kl)
74
+ @records.select { |r| r.instance_of? kl }
75
+ end
76
+ end
77
+
78
+ class Record
79
+ # assign, with handling for global TTL
80
+ def self.writer_for_ttl(*attribs)
81
+ attribs.each do |attrib|
82
+ define_method "#{attrib}=" do |val|
83
+ instance_variable_set("@#{attrib}", val || @vars["ttl"])
84
+ end
85
+ end
86
+ end
87
+
88
+ attr_reader :ttl
89
+ attr_writer :klass
90
+ writer_for_ttl :ttl
91
+
92
+ def klass
93
+ @klass = nil if @klass == ""
94
+ @klass ||= "IN"
95
+ end
96
+
97
+ attr_accessor :comment
98
+
99
+ private
100
+
101
+ def qualify_host(host)
102
+ origin = vars["origin"]
103
+ host = vars[:last_host] if /^\s*$/.match?(host)
104
+ host = host.gsub(/@/, origin)
105
+ if /\.$/.match?(host)
106
+ host
107
+ elsif /^\./.match?(origin)
108
+ host + origin
109
+ else
110
+ host + "." + origin
111
+ end
112
+ end
113
+ attr_accessor :vars
114
+ end
115
+
116
+ class SOA < Record
117
+ attr_accessor :origin, :nameserver, :responsible_party, :serial, :refresh_time, :retry_time, :expiry_time, :nxttl
118
+
119
+ def initialize(vars, zonefile_soa = nil)
120
+ @vars = vars
121
+ if zonefile_soa
122
+ self.origin = qualify_host(zonefile_soa.origin.to_s)
123
+ @vars[:last_host] = origin
124
+ self.ttl = zonefile_soa.ttl.to_i
125
+ self.klass = zonefile_soa.klass.to_s
126
+ self.nameserver = qualify_host(zonefile_soa.ns.to_s)
127
+ self.responsible_party = qualify_host(zonefile_soa.rp.to_s)
128
+ self.serial = zonefile_soa.serial.to_i
129
+ self.refresh_time = zonefile_soa.refresh.to_i
130
+ self.retry_time = zonefile_soa.reretry.to_i
131
+ self.expiry_time = zonefile_soa.expiry.to_i
132
+ self.nxttl = zonefile_soa.nxttl.to_i
133
+ end
134
+ end
135
+ end
136
+
137
+ class A < Record
138
+ attr_accessor :host, :address
139
+
140
+ def initialize(vars, zonefile_record)
141
+ @vars = vars
142
+ if zonefile_record
143
+ self.host = qualify_host(zonefile_record.host.to_s)
144
+ @vars[:last_host] = host
145
+ self.ttl = zonefile_record.ttl.to_i
146
+ self.klass = zonefile_record.klass.to_s
147
+ self.address = zonefile_record.ip_address.to_s
148
+ self.comment = zonefile_record.comment&.to_s
149
+ end
150
+ end
151
+ end
152
+
153
+ class AAAA < A
154
+ end
155
+
156
+ class CAA < Record
157
+ attr_accessor :host, :flags, :tag, :value
158
+
159
+ def initialize(vars, zonefile_record)
160
+ @vars = vars
161
+ if zonefile_record
162
+ self.host = qualify_host(zonefile_record.host.to_s)
163
+ @vars[:last_host] = host
164
+ self.ttl = zonefile_record.ttl.to_i
165
+ self.klass = zonefile_record.klass.to_s
166
+ self.flags = zonefile_record.flags.to_i
167
+ self.tag = zonefile_record.tag.to_s
168
+ self.value = zonefile_record.value.to_s
169
+ self.comment = zonefile_record.comment&.to_s
170
+ end
171
+ end
172
+ end
173
+
174
+ class CNAME < Record
175
+ attr_accessor :host, :domainname
176
+
177
+ def initialize(vars, zonefile_record)
178
+ @vars = vars
179
+ if zonefile_record
180
+ self.host = qualify_host(zonefile_record.host.to_s)
181
+ @vars[:last_host] = host
182
+ self.ttl = zonefile_record.ttl.to_i
183
+ self.klass = zonefile_record.klass.to_s
184
+ self.domainname = qualify_host(zonefile_record.target.to_s)
185
+ self.comment = zonefile_record.comment&.to_s
186
+ end
187
+ end
188
+
189
+ alias target domainname
190
+ alias alias host
191
+ end
192
+
193
+ class MX < Record
194
+ attr_accessor :host, :priority, :domainname
195
+
196
+ def initialize(vars, zonefile_record)
197
+ @vars = vars
198
+ if zonefile_record
199
+ self.host = qualify_host(zonefile_record.host.to_s)
200
+ @vars[:last_host] = host
201
+ self.ttl = zonefile_record.ttl.to_i
202
+ self.klass = zonefile_record.klass.to_s
203
+ self.priority = zonefile_record.priority.to_i
204
+ self.domainname = qualify_host(zonefile_record.exchanger.to_s)
205
+ self.comment = zonefile_record.comment&.to_s
206
+ end
207
+ end
208
+
209
+ alias exchange domainname
210
+ alias exchanger domainname
211
+ end
212
+
213
+ class NAPTR < Record
214
+ attr_accessor :host, :data
215
+
216
+ def initialize(vars, zonefile_record)
217
+ @vars = vars
218
+ if zonefile_record
219
+ self.host = qualify_host(zonefile_record.host.to_s)
220
+ @vars[:last_host] = host
221
+ self.ttl = zonefile_record.ttl.to_i
222
+ self.klass = zonefile_record.klass.to_s
223
+ self.data = zonefile_record.data.to_s
224
+ self.comment = zonefile_record.comment&.to_s
225
+ end
226
+ end
227
+ end
228
+
229
+ class NS < Record
230
+ attr_accessor :host, :domainname
231
+
232
+ def initialize(vars, zonefile_record)
233
+ @vars = vars
234
+ if zonefile_record
235
+ self.host = qualify_host(zonefile_record.host.to_s)
236
+ @vars[:last_host] = host
237
+ self.ttl = zonefile_record.ttl.to_i
238
+ self.klass = zonefile_record.klass.to_s
239
+ self.domainname = qualify_host(zonefile_record.nameserver.to_s)
240
+ self.comment = zonefile_record.comment&.to_s
241
+ end
242
+ end
243
+
244
+ alias nameserver domainname
245
+ end
246
+
247
+ class PTR < Record
248
+ attr_accessor :host, :domainname
249
+
250
+ def initialize(vars, zonefile_record)
251
+ @vars = vars
252
+ if zonefile_record
253
+ self.host = qualify_host(zonefile_record.host.to_s)
254
+ @vars[:last_host] = host
255
+ self.ttl = zonefile_record.ttl.to_i
256
+ self.klass = zonefile_record.klass.to_s
257
+ self.domainname = qualify_host(zonefile_record.target.to_s)
258
+ self.comment = zonefile_record.comment&.to_s
259
+ end
260
+ end
261
+
262
+ alias target domainname
263
+ end
264
+
265
+ class SRV < Record
266
+ attr_accessor :host, :priority, :weight, :port, :domainname
267
+
268
+ def initialize(vars, zonefile_record)
269
+ @vars = vars
270
+ if zonefile_record
271
+ self.host = qualify_host(zonefile_record.host.to_s)
272
+ @vars[:last_host] = host
273
+ self.ttl = zonefile_record.ttl.to_i
274
+ self.klass = zonefile_record.klass.to_s
275
+ self.priority = zonefile_record.priority.to_i
276
+ self.weight = zonefile_record.weight.to_i
277
+ self.port = zonefile_record.port.to_i
278
+ self.domainname = qualify_host(zonefile_record.target.to_s)
279
+ self.comment = zonefile_record.comment&.to_s
280
+ end
281
+ end
282
+
283
+ alias target domainname
284
+ end
285
+
286
+ class SSHFP < Record
287
+ attr_accessor :host, :alg, :fptype, :fp
288
+
289
+ def initialize(vars, zonefile_record)
290
+ @vars = vars
291
+ if zonefile_record
292
+ self.host = qualify_host(zonefile_record.host.to_s)
293
+ @vars[:last_host] = host
294
+ self.ttl = zonefile_record.ttl.to_i
295
+ self.klass = zonefile_record.klass.to_s
296
+ self.alg = zonefile_record.alg.to_i
297
+ self.fptype = zonefile_record.fptype.to_i
298
+ self.fp = zonefile_record.fp.to_s
299
+ self.comment = zonefile_record.comment&.to_s
300
+ end
301
+ end
302
+ end
303
+
304
+ class TXT < Record
305
+ attr_accessor :host, :data
306
+
307
+ def initialize(vars, zonefile_record)
308
+ @vars = vars
309
+ if zonefile_record
310
+ self.host = qualify_host(zonefile_record.host.to_s)
311
+ @vars[:last_host] = host
312
+ self.ttl = zonefile_record.ttl.to_i
313
+ self.klass = zonefile_record.klass.to_s
314
+ self.data = zonefile_record.data.to_s
315
+ self.comment = zonefile_record.comment&.to_s
316
+ end
317
+ end
318
+ end
319
+
320
+ class SPF < TXT
321
+ end
322
+ end
323
+ end