CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/net/dns/rr.rb
Views: 11778
1
# -*- coding: binary -*-
2
#
3
# $Id: RR.rb,v 1.19 2006/07/28 07:33:36 bluemonk Exp $
4
#
5
6
require 'net/dns/names/names'
7
require 'net/dns/rr/types'
8
require 'net/dns/rr/classes'
9
10
11
%w[a ns mx cname txt hinfo soa ptr aaaa mr srv].each do |file|
12
require "net/dns/rr/#{file}"
13
end
14
15
module Net # :nodoc:
16
module DNS
17
18
# =Name
19
#
20
# Net::DNS::RR - DNS Resource Record class
21
#
22
# =Synopsis
23
#
24
# require 'net/dns/rr'
25
#
26
# =Description
27
#
28
# The Net::DNS::RR is the base class for DNS Resource
29
# Record (RR) objects. A RR is a pack of data that represents
30
# resources for a DNS zone. The form in which this data is
31
# shows can be drawn as follow:
32
#
33
# "name ttl class type data"
34
#
35
# The +name+ is the name of the resource, like an canonical
36
# name for an +A+ record (internet ip address). The +ttl+ is the
37
# time to live, expressed in seconds. +type+ and +class+ are
38
# respectively the type of resource (+A+ for ip addresses, +NS+
39
# for nameservers, and so on) and the class, which is almost
40
# always +IN+, the Internet class. At the end, +data+ is the
41
# value associated to the name for that particular type of
42
# resource record. An example:
43
#
44
# # A record for IP address
45
# "www.example.com 86400 IN A 172.16.100.1"
46
#
47
# # NS record for name server
48
# "www.example.com 86400 IN NS ns.example.com"
49
#
50
# A new RR object can be created in 2 ways: passing a string
51
# such the ones above, or specifying each field as the pair
52
# of an hash. See the Net::DNS::RR.new method for details.
53
#
54
# =Error classes
55
#
56
# Some error classes has been defined for the Net::DNS::RR class,
57
# which are listed here to keep a light and browsable main documentation.
58
# We have:
59
#
60
# * RRArgumentError: Generic argument error for class Net::DNS::RR
61
# * RRDataError: Error in parsing binary data, maybe from a malformed packet
62
#
63
# =Copyright
64
#
65
# Copyright (c) 2006 Marco Ceresa
66
#
67
# All rights reserved. This program is free software; you may redistribute
68
# it and/or modify it under the same terms as Ruby itself.
69
#
70
class RR
71
include Net::DNS::Names
72
73
# Regexp matching an RR string
74
RR_REGEXP = Regexp.new("^\\s*(\\S+)\\s*(\\d+)?\\s+(" +
75
Net::DNS::RR::Classes.regexp +
76
"|CLASS\\d+)?\\s*(" +
77
Net::DNS::RR::Types.regexp +
78
"|TYPE\\d+)?\\s*(.*)$", Regexp::IGNORECASE | Regexp::NOENCODING)
79
80
# Dimension of the sum of class, type, TTL and rdlength fields in a
81
# RR portion of the packet, in bytes
82
RRFIXEDSZ = 10
83
84
# Name of the RR
85
attr_reader :name
86
# TTL time (in seconds) of the RR
87
attr_reader :ttl
88
# Data belonging to that appropriate class,
89
# not to be used (use real accessors instead)
90
attr_reader :rdata
91
92
# Create a new instance of Net::DNS::RR class, or an instance of
93
# any of the subclass of the appropriate type.
94
#
95
# Argument can be a string or an hash. With a string, we can pass
96
# a RR resource record in the canonical format:
97
#
98
# a = Net::DNS::RR.new("foo.example.com. 86400 A 10.1.2.3")
99
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
100
# cname = Net::DNS::RR.new("www.example.com 300 IN CNAME www1.example.com")
101
# txt = Net::DNS::RR.new('baz.example.com 3600 HS TXT "text record"')
102
#
103
# Incidentally, +a+, +mx+, +cname+ and +txt+ objects will be instances of
104
# respectively Net::DNS::RR::A, Net::DNS::RR::MX, Net::DNS::RR::CNAME and
105
# Net::DNS::RR::TXT classes.
106
#
107
# The name and RR data are required; all other information are optional.
108
# If omitted, the +TTL+ defaults to 10800, +type+ default to +A+ and the RR class
109
# defaults to +IN+. Omitting the optional fields is useful for creating the
110
# empty RDATA sections required for certain dynamic update operations.
111
# All names must be fully qualified. The trailing dot (.) is optional.
112
#
113
# The preferred method is however passing an hash with keys and values:
114
#
115
# rr = Net::DNS::RR.new(
116
# :name => "foo.example.com",
117
# :ttl => 86400,
118
# :cls => "IN",
119
# :type => "A",
120
# :address => "10.1.2.3"
121
# )
122
#
123
# rr = Net::DNS::RR.new(
124
# :name => "foo.example.com",
125
# :rdata => "10.1.2.3"
126
# )
127
#
128
# Name and data are required; all the others fields are optionals like
129
# we've seen before. The data field can be specified either with the
130
# right name of the resource (+:address+ in the example above) or with
131
# the generic key +:rdata+. Consult documentation to find the exact name
132
# for the resource in each subclass.
133
#
134
def initialize(arg)
135
case arg
136
when String
137
instance = new_from_string(arg)
138
when Hash
139
instance = new_from_hash(arg)
140
else
141
raise RRArgumentError, "Invalid argument, must be a RR string or an hash of values"
142
end
143
144
if @type.to_s == "ANY"
145
@cls = Net::DNS::RR::Classes.new("IN")
146
end
147
148
build_pack
149
set_type
150
151
instance
152
end
153
154
# Return a new RR object of the correct type (like Net::DNS::RR::A
155
# if the type is A) from a binary string, usually obtained from
156
# network stream.
157
#
158
# This method is used when parsing a binary packet by the Packet
159
# class.
160
#
161
def RR.parse(data)
162
o = allocate
163
obj,offset = o.send(:new_from_binary, data, 0)
164
return obj
165
end
166
167
# Same as RR.parse, but takes an entire packet binary data to
168
# perform name expansion. Default when analyzing a packet
169
# just received from a network stream.
170
#
171
# Return an instance of appropriate class and the offset
172
# pointing at the end of the data parsed.
173
#
174
def RR.parse_packet(data,offset)
175
o = allocate
176
o.send(:new_from_binary,data,offset)
177
end
178
179
# Return the RR object in binary data format, suitable
180
# for using in network streams, with names compressed.
181
# Must pass as arguments the offset inside the packet
182
# and an hash of compressed names.
183
#
184
# This method is to be used in other classes and is
185
# not intended for user space programs.
186
#
187
# TO FIX in one of the future releases
188
#
189
def comp_data(offset,compnames)
190
type,cls = @type.to_i, @cls.to_i
191
str,offset,names = dn_comp(@name,offset,compnames)
192
str += [type,cls,@ttl,@rdlength].pack("n2 N n")
193
offset += Net::DNS::RRFIXEDSZ
194
return str,offset,names
195
end
196
197
# Return the RR object in binary data format, suitable
198
# for using in network streams.
199
#
200
# raw_data = rr.data
201
# puts "RR is #{raw_data.size} bytes long"
202
#
203
def data
204
type,cls = @type.to_i, @cls.to_i
205
str = pack_name(@name)
206
return str + [type,cls,@ttl,@rdlength].pack("n2 N n") + get_data
207
end
208
209
# Canonical inspect method
210
#
211
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
212
# #=> example.com. 7200 IN MX 10 mailhost.example.com.
213
#
214
def inspect
215
data = get_inspect
216
# Returns the preformatted string
217
if @name.size < 24
218
[@name, @ttl.to_s, @cls.to_s, @type.to_s,
219
data].pack("A24 A8 A8 A8 A*")
220
else
221
to_a.join(" ")
222
end
223
end
224
225
# Returns the RR in a string format.
226
#
227
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
228
# mx.to_s
229
# #=> "example.com. 7200 IN MX 10 mailhost.example.com."
230
#
231
def to_s
232
"#{self.inspect}"
233
end
234
235
# Returns an array with all the fields for the RR record.
236
#
237
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
238
# mx.to_a
239
# #=> ["example.com.",7200,"IN","MX","10 mailhost.example.com."]
240
#
241
def to_a
242
[@name,@ttl,@cls.to_s,@type.to_s,get_inspect]
243
end
244
245
# Type accessor
246
def type
247
@type.to_s
248
end
249
250
# Class accessor
251
def cls
252
@cls.to_s
253
end
254
255
private
256
257
#---
258
# New RR with argument in string form
259
#---
260
def new_from_string(rrstring)
261
262
unless rrstring =~ RR_REGEXP
263
raise RRArgumentError,
264
"Format error for RR string (maybe CLASS and TYPE not valid?)"
265
end
266
267
# Name of RR - mandatory
268
begin
269
@name = $1.downcase
270
rescue NoMethodError
271
raise RRArgumentError, "Missing name field in RR string #{rrstring}"
272
end
273
274
# Time to live for RR, default 3 hours
275
@ttl = $2 ? $2.to_i : 10800
276
277
# RR class, default to IN
278
@cls = Net::DNS::RR::Classes.new $3
279
280
# RR type, default to A
281
@type = Net::DNS::RR::Types.new $4
282
283
# All the rest is data
284
@rdata = $5 ? $5.strip : ""
285
286
if self.class == Net::DNS::RR
287
(eval "Net::DNS::RR::#@type").new(rrstring)
288
else
289
subclass_new_from_string(@rdata)
290
self.class
291
end
292
end
293
294
def new_from_hash(args)
295
296
# Name field is mandatory
297
unless args.has_key? :name
298
raise RRArgumentError, "RR argument error: need at least RR name"
299
end
300
301
@name = args[:name].downcase
302
@ttl = args[:ttl] ? args[:ttl].to_i : 10800 # Default 3 hours
303
@type = Net::DNS::RR::Types.new args[:type]
304
@cls = Net::DNS::RR::Classes.new args[:cls]
305
306
@rdata = args[:rdata] ? args[:rdata].strip : ""
307
@rdlength = args[:rdlength] || @rdata.size
308
309
if self.class == Net::DNS::RR
310
(eval "Net::DNS::RR::#@type").new(args)
311
else
312
hash = args - [:name,:ttl,:type,:cls]
313
if hash.has_key? :rdata
314
subclass_new_from_string(hash[:rdata])
315
else
316
subclass_new_from_hash(hash)
317
end
318
self.class
319
end
320
end # new_from_hash
321
322
def new_from_binary(data,offset)
323
if self.class == Net::DNS::RR
324
temp = dn_expand(data,offset)[1]
325
type = Net::DNS::RR::Types.new data.unpack("@#{temp} n")[0]
326
return unless Net::DNS::RR.const_defined?(type.to_s)
327
(eval "Net::DNS::RR::#{type}").parse_packet(data,offset)
328
else
329
@name,offset = dn_expand(data,offset)
330
rrtype,cls,@ttl,@rdlength = data.unpack("@#{offset} n2 N n")
331
@type = Net::DNS::RR::Types.new rrtype
332
@cls = Net::DNS::RR::Classes.new cls
333
offset += RRFIXEDSZ
334
offset = subclass_new_from_binary(data,offset)
335
build_pack
336
set_type
337
return [self,offset]
338
end
339
# rescue StandardError => err
340
# raise RRDataError, "Caught exception, maybe packet malformed: #{err}"
341
end
342
343
# Methods to be overridden by subclasses
344
def subclass_new_from_array(arr)
345
end
346
def subclass_new_from_string(str)
347
end
348
def subclass_new_from_hash(hash)
349
end
350
def subclass_new_from_binary(data,offset)
351
end
352
def build_pack
353
end
354
def set_type
355
end
356
def get_inspect
357
@rdata
358
end
359
def get_data
360
@rdata
361
end
362
363
# NEW new method :)
364
def self.new(*args)
365
o = allocate
366
obj = o.send(:initialize,*args)
367
if self == Net::DNS::RR
368
return obj
369
else
370
return o
371
end
372
end
373
374
end # class RR
375
376
end # module DNS
377
end # module Net
378
379
class RRArgumentError < ArgumentError # :nodoc:
380
end
381
class RRDataError < StandardError # :nodoc:
382
end
383
384
module ExtendHash # :nodoc:
385
386
# Performs a sort of group difference
387
# operation on hashes or arrays
388
#
389
# a = {:a=>1,:b=>2,:c=>3}
390
# b = {:a=>1,:b=>2}
391
# c = [:a,:c]
392
# a-b #=> {:c=>3}
393
# a-c #=> {:b=>2}
394
#
395
def -(oth)
396
case oth
397
when Hash
398
delete_if {|k,v| oth.has_key? k}
399
when Array
400
delete_if {|k,v| oth.include? k}
401
end
402
end
403
end
404
405
class Hash # :nodoc:
406
include ExtendHash
407
end
408
409