rrs 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/lib/rrs/{a.rb → in/a.rb} +1 -1
- data/lib/rrs/in.rb +4 -0
- data/lib/rrs/inflector.rb +5 -0
- data/lib/rrs/label.rb +39 -0
- data/lib/rrs/maps/qclass_map.rb +11 -0
- data/lib/rrs/maps/qtype_map.rb +28 -0
- data/lib/rrs/maps.rb +6 -0
- data/lib/rrs/message.rb +352 -0
- data/lib/rrs/name.rb +109 -0
- data/lib/rrs/opt.rb +16 -0
- data/lib/rrs/resource.rb +51 -0
- data/lib/rrs.rb +5 -1
- metadata +12 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a851d73be2407a245945dd33ac1e669bd66ccad17f7f073212bf00e8105f358
|
4
|
+
data.tar.gz: c5c4d8f7c52bfad9aa0fe4cc85aff98b515f870c146242a5f263f82c10c75fe3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 789a9f105dbaf0f00c93cd0811e75fbc6956130ede2482bba8720ed207cc14e27d53889824f284dbde05c3131b8e61a69883deaf4dc52d4035684ed8d7a94936
|
7
|
+
data.tar.gz: ca30ac41e67c5f55bebdeba2d9c7b28acde254385d838eca61c579a998bb64e2852de7407582f1e74e078c8b694440fdeab77cb33e59a38a0cfd9fbcda241bc6
|
data/lib/rrs/{a.rb → in/a.rb}
RENAMED
data/lib/rrs/in.rb
ADDED
data/lib/rrs/inflector.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require_relative "maps/qtype_map"
|
1
2
|
module RRs
|
2
3
|
class Inflector < Zeitwerk::Inflector
|
3
4
|
def camelize(basename, abspath)
|
@@ -5,6 +6,10 @@ module RRs
|
|
5
6
|
"RRs" + super($1, abspath)
|
6
7
|
elsif basename =~ /\Aipv(.*)/
|
7
8
|
"IPv" + super($1, abspath)
|
9
|
+
elsif basename =~ /\Ain(.*)/ && basename != "inflector"
|
10
|
+
"IN" + super($1, abspath)
|
11
|
+
elsif ::RRs::Maps::QTYPE_MAP.values.any? { |v| basename.upcase == v.to_s }
|
12
|
+
basename.upcase
|
8
13
|
else
|
9
14
|
super
|
10
15
|
end
|
data/lib/rrs/label.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module RRs
|
2
|
+
module Label # :nodoc:
|
3
|
+
def self.split(arg)
|
4
|
+
labels = []
|
5
|
+
arg.scan(/[^\.]+/) {labels << Str.new($&)}
|
6
|
+
return labels
|
7
|
+
end
|
8
|
+
|
9
|
+
class Str # :nodoc:
|
10
|
+
def initialize(string)
|
11
|
+
@string = string
|
12
|
+
# case insensivity of DNS labels doesn't apply non-ASCII characters. [RFC 4343]
|
13
|
+
# This assumes @string is given in ASCII compatible encoding.
|
14
|
+
@downcase = string.b.downcase
|
15
|
+
end
|
16
|
+
attr_reader :string, :downcase
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
return @string
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
return "#<#{self.class} #{self}>"
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(other)
|
27
|
+
return self.class == other.class && @downcase == other.downcase
|
28
|
+
end
|
29
|
+
|
30
|
+
def eql?(other)
|
31
|
+
return self == other
|
32
|
+
end
|
33
|
+
|
34
|
+
def hash
|
35
|
+
return @downcase.hash
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RRs
|
2
|
+
module Maps
|
3
|
+
QTYPE_MAP = Ractor.make_shareable({
|
4
|
+
1 => :A,
|
5
|
+
2 => :NS,
|
6
|
+
3 => :MD,
|
7
|
+
4 => :MF,
|
8
|
+
5 => :CNAME,
|
9
|
+
6 => :SOA,
|
10
|
+
7 => :MB,
|
11
|
+
8 => :MG,
|
12
|
+
9 => :MR,
|
13
|
+
10 => :NULL,
|
14
|
+
11 => :WKS,
|
15
|
+
12 => :PTR,
|
16
|
+
13 => :HINFO,
|
17
|
+
14 => :MINFO,
|
18
|
+
15 => :MX,
|
19
|
+
16 => :TXT,
|
20
|
+
28 => :AAAA,
|
21
|
+
41 => :OPT,
|
22
|
+
252 => :AXFR,
|
23
|
+
253 => :MAILB,
|
24
|
+
254 => :MAILA,
|
25
|
+
255 => :*
|
26
|
+
}.freeze)
|
27
|
+
end
|
28
|
+
end
|
data/lib/rrs/maps.rb
ADDED
data/lib/rrs/message.rb
ADDED
@@ -0,0 +1,352 @@
|
|
1
|
+
module RRs
|
2
|
+
class Message
|
3
|
+
@@identifier = -1
|
4
|
+
|
5
|
+
def initialize(id = (@@identifier += 1) & 0xffff)
|
6
|
+
@id = id
|
7
|
+
@qr = 0
|
8
|
+
@opcode = 0
|
9
|
+
@aa = 0
|
10
|
+
@tc = 0
|
11
|
+
@rd = 0 # recursion desired
|
12
|
+
@ra = 0 # recursion available
|
13
|
+
@rcode = 0
|
14
|
+
@question = []
|
15
|
+
@answer = []
|
16
|
+
@authority = []
|
17
|
+
@additional = []
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode
|
21
|
+
attr_reader :question, :answer, :authority, :additional
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
return @id == other.id &&
|
25
|
+
@qr == other.qr &&
|
26
|
+
@opcode == other.opcode &&
|
27
|
+
@aa == other.aa &&
|
28
|
+
@tc == other.tc &&
|
29
|
+
@rd == other.rd &&
|
30
|
+
@ra == other.ra &&
|
31
|
+
@rcode == other.rcode &&
|
32
|
+
@question == other.question &&
|
33
|
+
@answer == other.answer &&
|
34
|
+
@authority == other.authority &&
|
35
|
+
@additional == other.additional
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_question(name, typeclass)
|
39
|
+
@question << [Name.create(name), typeclass]
|
40
|
+
end
|
41
|
+
|
42
|
+
def each_question
|
43
|
+
@question.each {|name, typeclass|
|
44
|
+
yield name, typeclass
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_answer(name, ttl, data)
|
49
|
+
@answer << [Name.create(name), ttl, data]
|
50
|
+
end
|
51
|
+
|
52
|
+
def each_answer
|
53
|
+
@answer.each {|name, ttl, data|
|
54
|
+
yield name, ttl, data
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_authority(name, ttl, data)
|
59
|
+
@authority << [Name.create(name), ttl, data]
|
60
|
+
end
|
61
|
+
|
62
|
+
def each_authority
|
63
|
+
@authority.each {|name, ttl, data|
|
64
|
+
yield name, ttl, data
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_additional(name, ttl, data)
|
69
|
+
@additional << [Name.create(name), ttl, data]
|
70
|
+
end
|
71
|
+
|
72
|
+
def each_additional
|
73
|
+
@additional.each {|name, ttl, data|
|
74
|
+
yield name, ttl, data
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def each_resource
|
79
|
+
each_answer {|name, ttl, data| yield name, ttl, data}
|
80
|
+
each_authority {|name, ttl, data| yield name, ttl, data}
|
81
|
+
each_additional {|name, ttl, data| yield name, ttl, data}
|
82
|
+
end
|
83
|
+
|
84
|
+
def encode
|
85
|
+
return MessageEncoder.new {|msg|
|
86
|
+
msg.put_pack('nnnnnn',
|
87
|
+
@id,
|
88
|
+
(@qr & 1) << 15 |
|
89
|
+
(@opcode & 15) << 11 |
|
90
|
+
(@aa & 1) << 10 |
|
91
|
+
(@tc & 1) << 9 |
|
92
|
+
(@rd & 1) << 8 |
|
93
|
+
(@ra & 1) << 7 |
|
94
|
+
(@rcode & 15),
|
95
|
+
@question.length,
|
96
|
+
@answer.length,
|
97
|
+
@authority.length,
|
98
|
+
@additional.length)
|
99
|
+
@question.each {|q|
|
100
|
+
name, typeclass = q
|
101
|
+
msg.put_name(name)
|
102
|
+
msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue)
|
103
|
+
}
|
104
|
+
[@answer, @authority, @additional].each {|rr|
|
105
|
+
rr.each {|r|
|
106
|
+
name, ttl, data = r
|
107
|
+
msg.put_name(name)
|
108
|
+
msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl)
|
109
|
+
msg.put_length16 {data.encode_rdata(msg)}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}.to_s
|
113
|
+
end
|
114
|
+
|
115
|
+
class MessageEncoder # :nodoc:
|
116
|
+
def initialize
|
117
|
+
@data = ''.dup
|
118
|
+
@names = {}
|
119
|
+
yield self
|
120
|
+
end
|
121
|
+
|
122
|
+
def to_s
|
123
|
+
return @data
|
124
|
+
end
|
125
|
+
|
126
|
+
def put_bytes(d)
|
127
|
+
@data << d
|
128
|
+
end
|
129
|
+
|
130
|
+
def put_pack(template, *d)
|
131
|
+
@data << d.pack(template)
|
132
|
+
end
|
133
|
+
|
134
|
+
def put_length16
|
135
|
+
length_index = @data.length
|
136
|
+
@data << "\0\0"
|
137
|
+
data_start = @data.length
|
138
|
+
yield
|
139
|
+
data_end = @data.length
|
140
|
+
@data[length_index, 2] = [data_end - data_start].pack("n")
|
141
|
+
end
|
142
|
+
|
143
|
+
def put_string(d)
|
144
|
+
self.put_pack("C", d.length)
|
145
|
+
@data << d
|
146
|
+
end
|
147
|
+
|
148
|
+
def put_string_list(ds)
|
149
|
+
ds.each {|d|
|
150
|
+
self.put_string(d)
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
def put_name(d, compress: true)
|
155
|
+
put_labels(d.to_a, compress: compress)
|
156
|
+
end
|
157
|
+
|
158
|
+
def put_labels(d, compress: true)
|
159
|
+
d.each_index {|i|
|
160
|
+
domain = d[i..-1]
|
161
|
+
if compress && idx = @names[domain]
|
162
|
+
self.put_pack("n", 0xc000 | idx)
|
163
|
+
return
|
164
|
+
else
|
165
|
+
if @data.length < 0x4000
|
166
|
+
@names[domain] = @data.length
|
167
|
+
end
|
168
|
+
self.put_label(d[i])
|
169
|
+
end
|
170
|
+
}
|
171
|
+
@data << "\0"
|
172
|
+
end
|
173
|
+
|
174
|
+
def put_label(d)
|
175
|
+
self.put_string(d.to_s)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def Message.decode(m)
|
180
|
+
o = Message.new(0)
|
181
|
+
MessageDecoder.new(m) {|msg|
|
182
|
+
id, flag, qdcount, ancount, nscount, arcount =
|
183
|
+
msg.get_unpack('nnnnnn')
|
184
|
+
o.id = id
|
185
|
+
o.tc = (flag >> 9) & 1
|
186
|
+
o.rcode = flag & 15
|
187
|
+
return o unless o.tc.zero?
|
188
|
+
|
189
|
+
o.qr = (flag >> 15) & 1
|
190
|
+
o.opcode = (flag >> 11) & 15
|
191
|
+
o.aa = (flag >> 10) & 1
|
192
|
+
o.rd = (flag >> 8) & 1
|
193
|
+
o.ra = (flag >> 7) & 1
|
194
|
+
(1..qdcount).each {
|
195
|
+
name, typeclass = msg.get_question
|
196
|
+
o.add_question(name, typeclass)
|
197
|
+
}
|
198
|
+
(1..ancount).each {
|
199
|
+
name, ttl, data = msg.get_rr
|
200
|
+
o.add_answer(name, ttl, data)
|
201
|
+
}
|
202
|
+
(1..nscount).each {
|
203
|
+
name, ttl, data = msg.get_rr
|
204
|
+
o.add_authority(name, ttl, data)
|
205
|
+
}
|
206
|
+
(1..arcount).each {
|
207
|
+
name, ttl, data = msg.get_rr
|
208
|
+
o.add_additional(name, ttl, data)
|
209
|
+
}
|
210
|
+
}
|
211
|
+
return o
|
212
|
+
end
|
213
|
+
|
214
|
+
class MessageDecoder # :nodoc:
|
215
|
+
def initialize(data)
|
216
|
+
@data = data
|
217
|
+
@index = 0
|
218
|
+
@limit = data.bytesize
|
219
|
+
yield self
|
220
|
+
end
|
221
|
+
|
222
|
+
def inspect
|
223
|
+
"\#<#{self.class}: #{@data.byteslice(0, @index).inspect} #{@data.byteslice(@index..-1).inspect}>"
|
224
|
+
end
|
225
|
+
|
226
|
+
def get_length16
|
227
|
+
len, = self.get_unpack('n')
|
228
|
+
save_limit = @limit
|
229
|
+
@limit = @index + len
|
230
|
+
d = yield(len)
|
231
|
+
if @index < @limit
|
232
|
+
raise DecodeError.new("junk exists")
|
233
|
+
elsif @limit < @index
|
234
|
+
raise DecodeError.new("limit exceeded")
|
235
|
+
end
|
236
|
+
@limit = save_limit
|
237
|
+
return d
|
238
|
+
end
|
239
|
+
|
240
|
+
def get_bytes(len = @limit - @index)
|
241
|
+
raise DecodeError.new("limit exceeded") if @limit < @index + len
|
242
|
+
d = @data.byteslice(@index, len)
|
243
|
+
@index += len
|
244
|
+
return d
|
245
|
+
end
|
246
|
+
|
247
|
+
def get_unpack(template)
|
248
|
+
len = 0
|
249
|
+
template.each_byte {|byte|
|
250
|
+
byte = "%c" % byte
|
251
|
+
case byte
|
252
|
+
when ?c, ?C
|
253
|
+
len += 1
|
254
|
+
when ?n
|
255
|
+
len += 2
|
256
|
+
when ?N
|
257
|
+
len += 4
|
258
|
+
else
|
259
|
+
raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
|
260
|
+
end
|
261
|
+
}
|
262
|
+
raise DecodeError.new("limit exceeded") if @limit < @index + len
|
263
|
+
arr = @data.unpack("@#{@index}#{template}")
|
264
|
+
@index += len
|
265
|
+
return arr
|
266
|
+
end
|
267
|
+
|
268
|
+
def get_string
|
269
|
+
raise DecodeError.new("limit exceeded") if @limit <= @index
|
270
|
+
len = @data.getbyte(@index)
|
271
|
+
raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
|
272
|
+
d = @data.byteslice(@index + 1, len)
|
273
|
+
@index += 1 + len
|
274
|
+
return d
|
275
|
+
end
|
276
|
+
|
277
|
+
def get_string_list
|
278
|
+
strings = []
|
279
|
+
while @index < @limit
|
280
|
+
strings << self.get_string
|
281
|
+
end
|
282
|
+
strings
|
283
|
+
end
|
284
|
+
|
285
|
+
def get_list
|
286
|
+
[].tap do |values|
|
287
|
+
while @index < @limit
|
288
|
+
values << yield
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def get_name
|
294
|
+
return Name.new(self.get_labels)
|
295
|
+
end
|
296
|
+
|
297
|
+
def get_labels
|
298
|
+
prev_index = @index
|
299
|
+
save_index = nil
|
300
|
+
d = []
|
301
|
+
while true
|
302
|
+
raise DecodeError.new("limit exceeded") if @limit <= @index
|
303
|
+
case @data.getbyte(@index)
|
304
|
+
when 0
|
305
|
+
@index += 1
|
306
|
+
if save_index
|
307
|
+
@index = save_index
|
308
|
+
end
|
309
|
+
return d
|
310
|
+
when 192..255
|
311
|
+
idx = self.get_unpack('n')[0] & 0x3fff
|
312
|
+
if prev_index <= idx
|
313
|
+
raise DecodeError.new("non-backward name pointer")
|
314
|
+
end
|
315
|
+
prev_index = idx
|
316
|
+
if !save_index
|
317
|
+
save_index = @index
|
318
|
+
end
|
319
|
+
@index = idx
|
320
|
+
else
|
321
|
+
d << self.get_label
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def get_label
|
327
|
+
return Label::Str.new(self.get_string)
|
328
|
+
end
|
329
|
+
|
330
|
+
def get_question
|
331
|
+
name = self.get_name
|
332
|
+
type, klass = self.get_unpack("nn")
|
333
|
+
return name, Resource.get_class(type, klass)
|
334
|
+
end
|
335
|
+
|
336
|
+
def get_rr
|
337
|
+
name = self.get_name
|
338
|
+
type, klass, ttl = self.get_unpack('nnN')
|
339
|
+
typeclass = Resource.get_class(type, klass)
|
340
|
+
res = self.get_length16 do
|
341
|
+
begin
|
342
|
+
typeclass.decode_rdata self
|
343
|
+
rescue => e
|
344
|
+
raise DecodeError, e.message, e.backtrace
|
345
|
+
end
|
346
|
+
end
|
347
|
+
res.instance_variable_set :@ttl, ttl
|
348
|
+
return name, ttl, res
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
data/lib/rrs/name.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
module RRs
|
2
|
+
##
|
3
|
+
# A representation of a DNS name.
|
4
|
+
|
5
|
+
class Name
|
6
|
+
|
7
|
+
##
|
8
|
+
# Creates a new DNS name from +arg+. +arg+ can be:
|
9
|
+
#
|
10
|
+
# Name:: returns +arg+.
|
11
|
+
# String:: Creates a new Name.
|
12
|
+
|
13
|
+
def self.create(arg)
|
14
|
+
case arg
|
15
|
+
when Name
|
16
|
+
return arg
|
17
|
+
when String
|
18
|
+
return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
|
19
|
+
else
|
20
|
+
raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(labels, absolute=true) # :nodoc:
|
25
|
+
labels = labels.map {|label|
|
26
|
+
case label
|
27
|
+
when String then Label::Str.new(label)
|
28
|
+
when Label::Str then label
|
29
|
+
else
|
30
|
+
raise ArgumentError, "unexpected label: #{label.inspect}"
|
31
|
+
end
|
32
|
+
}
|
33
|
+
@labels = labels
|
34
|
+
@absolute = absolute
|
35
|
+
end
|
36
|
+
|
37
|
+
def inspect # :nodoc:
|
38
|
+
"#<#{self.class}: #{self}#{@absolute ? '.' : ''}>"
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# True if this name is absolute.
|
43
|
+
|
44
|
+
def absolute?
|
45
|
+
return @absolute
|
46
|
+
end
|
47
|
+
|
48
|
+
def ==(other) # :nodoc:
|
49
|
+
return false unless Name === other
|
50
|
+
return false unless @absolute == other.absolute?
|
51
|
+
return @labels == other.to_a
|
52
|
+
end
|
53
|
+
|
54
|
+
alias eql? == # :nodoc:
|
55
|
+
|
56
|
+
##
|
57
|
+
# Returns true if +other+ is a subdomain.
|
58
|
+
#
|
59
|
+
# Example:
|
60
|
+
#
|
61
|
+
# domain = Resolv::DNS::Name.create("y.z")
|
62
|
+
# p Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
|
63
|
+
# p Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true
|
64
|
+
# p Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false
|
65
|
+
# p Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false
|
66
|
+
# p Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
|
67
|
+
# p Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
|
68
|
+
#
|
69
|
+
|
70
|
+
def subdomain_of?(other)
|
71
|
+
raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
|
72
|
+
return false if @absolute != other.absolute?
|
73
|
+
other_len = other.length
|
74
|
+
return false if @labels.length <= other_len
|
75
|
+
return @labels[-other_len, other_len] == other.to_a
|
76
|
+
end
|
77
|
+
|
78
|
+
def hash # :nodoc:
|
79
|
+
return @labels.hash ^ @absolute.hash
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_a # :nodoc:
|
83
|
+
return @labels
|
84
|
+
end
|
85
|
+
|
86
|
+
def length # :nodoc:
|
87
|
+
return @labels.length
|
88
|
+
end
|
89
|
+
|
90
|
+
def [](i) # :nodoc:
|
91
|
+
return @labels[i]
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# returns the domain name as a string.
|
96
|
+
#
|
97
|
+
# The domain name doesn't have a trailing dot even if the name object is
|
98
|
+
# absolute.
|
99
|
+
#
|
100
|
+
# Example:
|
101
|
+
#
|
102
|
+
# p Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
|
103
|
+
# p Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
|
104
|
+
|
105
|
+
def to_s
|
106
|
+
return @labels.join('.')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/rrs/opt.rb
ADDED
data/lib/rrs/resource.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
class RRs::Resource
|
2
|
+
##
|
3
|
+
# Remaining Time To Live for this Resource.
|
4
|
+
|
5
|
+
attr_reader :ttl
|
6
|
+
|
7
|
+
def encode_rdata(msg) # :nodoc:
|
8
|
+
raise NotImplementedError.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.decode_rdata(msg) # :nodoc:
|
12
|
+
raise NotImplementedError.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other) # :nodoc:
|
16
|
+
return false unless self.class == other.class
|
17
|
+
s_ivars = self.instance_variables
|
18
|
+
s_ivars.sort!
|
19
|
+
s_ivars.delete :@ttl
|
20
|
+
o_ivars = other.instance_variables
|
21
|
+
o_ivars.sort!
|
22
|
+
o_ivars.delete :@ttl
|
23
|
+
return s_ivars == o_ivars &&
|
24
|
+
s_ivars.collect {|name| self.instance_variable_get name} ==
|
25
|
+
o_ivars.collect {|name| other.instance_variable_get name}
|
26
|
+
end
|
27
|
+
|
28
|
+
def eql?(other) # :nodoc:
|
29
|
+
return self == other
|
30
|
+
end
|
31
|
+
|
32
|
+
def hash # :nodoc:
|
33
|
+
h = 0
|
34
|
+
vars = self.instance_variables
|
35
|
+
vars.delete :@ttl
|
36
|
+
vars.each {|name|
|
37
|
+
h ^= self.instance_variable_get(name).hash
|
38
|
+
}
|
39
|
+
return h
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get_class(type_value, class_value) # :nodoc:
|
43
|
+
path = []
|
44
|
+
if class_value
|
45
|
+
path.push(::RRs::Maps::QCLASS_MAP[class_value.to_i].to_s) if ::RRs::Maps::QCLASS_MAP[class_value.to_i]
|
46
|
+
end
|
47
|
+
path.push(::RRs::Maps::QTYPE_MAP[type_value.to_i].to_s)
|
48
|
+
RRs.const_get(path.join("::"))
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/lib/rrs.rb
CHANGED
@@ -1,15 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "zeitwerk"
|
3
3
|
require_relative "rrs/inflector"
|
4
|
+
require_relative "rrs/maps"
|
4
5
|
|
5
6
|
loader = Zeitwerk::Loader.for_gem
|
6
7
|
loader.inflector = RRs::Inflector.new
|
8
|
+
loader.ignore("#{__dir__}/rrs/maps")
|
7
9
|
loader.setup
|
8
10
|
|
9
11
|
module RRs
|
10
|
-
VERSION = "0.
|
12
|
+
VERSION = "0.2.0"
|
11
13
|
|
12
14
|
class Error < StandardError; end
|
15
|
+
class DecodeError < StandardError; end
|
16
|
+
class EncodeError < StandardError; end
|
13
17
|
end
|
14
18
|
|
15
19
|
loader.eager_load
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rrs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- reesericci
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: "RRs is a set of self-contained DNS primitives that do not share state,
|
14
14
|
which makes them suitable for use in Ractors. \n\nThis is a contrast to the Resolv::DNS
|
@@ -23,10 +23,19 @@ files:
|
|
23
23
|
- README.md
|
24
24
|
- Rakefile
|
25
25
|
- lib/rrs.rb
|
26
|
-
- lib/rrs/
|
26
|
+
- lib/rrs/in.rb
|
27
|
+
- lib/rrs/in/a.rb
|
27
28
|
- lib/rrs/inflector.rb
|
28
29
|
- lib/rrs/ipv4.rb
|
29
30
|
- lib/rrs/ipv6.rb
|
31
|
+
- lib/rrs/label.rb
|
32
|
+
- lib/rrs/maps.rb
|
33
|
+
- lib/rrs/maps/qclass_map.rb
|
34
|
+
- lib/rrs/maps/qtype_map.rb
|
35
|
+
- lib/rrs/message.rb
|
36
|
+
- lib/rrs/name.rb
|
37
|
+
- lib/rrs/opt.rb
|
38
|
+
- lib/rrs/resource.rb
|
30
39
|
- sig/rrs.rbs
|
31
40
|
homepage: https://codeberg.org/reesericci/rrs
|
32
41
|
licenses: []
|