Class: Dnsruby::RR

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/dnsruby/resource/A.rb,
lib/dnsruby/resource/DS.rb,
lib/dnsruby/resource/IN.rb,
lib/dnsruby/resource/KX.rb,
lib/dnsruby/resource/MX.rb,
lib/dnsruby/resource/PX.rb,
lib/dnsruby/resource/RP.rb,
lib/dnsruby/resource/RR.rb,
lib/dnsruby/resource/RT.rb,
lib/dnsruby/resource/APL.rb,
lib/dnsruby/resource/CAA.rb,
lib/dnsruby/resource/CDS.rb,
lib/dnsruby/resource/DLV.rb,
lib/dnsruby/resource/HIP.rb,
lib/dnsruby/resource/LOC.rb,
lib/dnsruby/resource/NXT.rb,
lib/dnsruby/resource/OPT.rb,
lib/dnsruby/resource/SOA.rb,
lib/dnsruby/resource/SPF.rb,
lib/dnsruby/resource/SRV.rb,
lib/dnsruby/resource/TXT.rb,
lib/dnsruby/resource/URI.rb,
lib/dnsruby/resource/X25.rb,
lib/dnsruby/resource/AAAA.rb,
lib/dnsruby/resource/CERT.rb,
lib/dnsruby/resource/GPOS.rb,
lib/dnsruby/resource/ISDN.rb,
lib/dnsruby/resource/NSAP.rb,
lib/dnsruby/resource/NSEC.rb,
lib/dnsruby/resource/TKEY.rb,
lib/dnsruby/resource/TLSA.rb,
lib/dnsruby/resource/TSIG.rb,
lib/dnsruby/resource/AFSDB.rb,
lib/dnsruby/resource/DHCID.rb,
lib/dnsruby/resource/HINFO.rb,
lib/dnsruby/resource/MINFO.rb,
lib/dnsruby/resource/NAPTR.rb,
lib/dnsruby/resource/NSEC3.rb,
lib/dnsruby/resource/RRSIG.rb,
lib/dnsruby/resource/SSHFP.rb,
lib/dnsruby/resource/DNSKEY.rb,
lib/dnsruby/resource/CDNSKEY.rb,
lib/dnsruby/resource/generic.rb,
lib/dnsruby/resource/IPSECKEY.rb,
lib/dnsruby/resource/NSEC3PARAM.rb,
lib/dnsruby/resource/domain_name.rb

Defined Under Namespace

Modules: IN Classes: ANY, CAA, CDNSKEY, CDS, CERT, CNAME, DHCID, DLV, DNAME, DNSKEY, DS, DomainName, GPOS, Generic, HINFO, HIP, IPSECKEY, ISDN, KX, LOC, MB, MG, MINFO, MR, MX, NAPTR, NS, NSAP, NSEC, NSEC3, NSEC3PARAM, NXT, OPT, PTR, RP, RRSIG, RT, SOA, SPF, SSHFP, TKEY, TSIG, TXT, URI, X25

Constant Summary collapse

ClassInsensitiveTypes =
{
  Types::NS => NS,
  Types::CNAME => CNAME,
  Types::DNAME => DNAME,
  Types::URI => URI,
  Types::DS => DS,
  Types::CDS => CDS,
  Types::DNSKEY => DNSKEY,
  Types::CDNSKEY => CDNSKEY,
  Types::SOA => SOA,
  Types::PTR => PTR,
  Types::HINFO => HINFO,
  Types::MINFO => MINFO,
  Types::MX => MX,
  Types::TXT => TXT,
  Types::ISDN => ISDN,
  Types::MB => MB,
  Types::MG => MG,
  Types::MR => MR,
  Types::NAPTR => NAPTR,
  Types::NSAP => NSAP,
  Types::OPT => OPT,
  Types::RP => RP,
  Types::RT => RT,
  Types::X25 => X25,
  Types::KX => KX,
  Types::SPF => SPF,
  Types::CERT => CERT,
  Types::LOC => LOC,
  Types::TSIG => TSIG,
  Types::TKEY => TKEY,
  Types::ANY => ANY,
  Types::RRSIG => RRSIG,
  Types::NSEC => NSEC,
  Types::NSEC3 => NSEC3,
  Types::NSEC3PARAM => NSEC3PARAM,
  Types::DLV => DLV,
  Types::SSHFP => SSHFP,
  Types::IPSECKEY => IPSECKEY,
  Types::HIP => HIP,
  Types::DHCID => DHCID,
  Types::GPOS => GPOS,
  Types::NXT => NXT,
  Types::CAA => CAA,
}
@@RR_REGEX =

A regular expression which catches any valid resource record.

Regexp.new("^\\s*(\\S+)\\s*(\\d+)?\\s*(#{Classes.regexp +
"|CLASS\\d+"})?\\s*(#{Types.regexp + '|TYPE\\d+'})?\\s*([\\s\\S]*)\$")
@@implemented_rr_map =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#klassObject

The Resource class



46
47
48
# File 'lib/dnsruby/resource/RR.rb', line 46

def klass
  @klass
end

#nameObject

The Resource’s domain name



40
41
42
# File 'lib/dnsruby/resource/RR.rb', line 40

def name
  @name
end

#rdataObject

The Resource data section



52
53
54
# File 'lib/dnsruby/resource/RR.rb', line 52

def rdata
  @rdata
end

#ttlObject

The Resource Time-To-Live



49
50
51
# File 'lib/dnsruby/resource/RR.rb', line 49

def ttl
  @ttl
end

#typeObject Also known as: rr_type

The Resource type



43
44
45
# File 'lib/dnsruby/resource/RR.rb', line 43

def type
  @type
end

Class Method Details

.create(*args) ⇒ Object

Create a new RR from the arguments, which can be either a String or a Hash. See new_from_string and new_from_hash for details

a     = Dnsruby::RR.create('foo.example.com. 86400 A 10.1.2.3')
mx    = Dnsruby::RR.create('example.com. 7200 MX 10 mailhost.example.com.')
cname = Dnsruby::RR.create('www.example.com 300 IN CNAME www1.example.com')
txt   = Dnsruby::RR.create('baz.example.com 3600 HS TXT 'text record'')

rr = Dnsruby::RR.create({:name => 'example.com'})
rr = Dnsruby::RR.create({:name => 'example.com', :type => 'MX', :ttl => 10,
                               :preference => 5, :exchange => 'mx1.example.com'})


399
400
401
402
403
404
405
406
407
408
# File 'lib/dnsruby/resource/RR.rb', line 399

def RR.create(*args)
  case args[0]
    when String
      new_from_string(args[0])
    when Hash
      new_from_hash(args[0])
    else
      new_from_data(args)
  end
end

.decode_rdata(msg) ⇒ Object

:nodoc: all

Raises:



317
318
319
320
# File 'lib/dnsruby/resource/RR.rb', line 317

def self.decode_rdata(msg) #:nodoc: all
  #  to be implemented by subclasses
  raise DecodeError.new("#{self.class} is RR.")
end

.find_class(type_value, class_value) ⇒ Object

:nodoc: all



359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/dnsruby/resource/RR.rb', line 359

def self.find_class(type_value, class_value) # :nodoc: all
  if !! (ret = ClassHash[[type_value, class_value]])
    return ret
  elsif !! (val = ClassInsensitiveTypes[type_value])
    klass = Class.new(val)
    klass.const_set(:TypeValue, type_value)
    klass.const_set(:ClassValue, class_value)
    return klass
  else
    return Generic.create(type_value, class_value)
  end
end

.get_class(type_value, class_value) ⇒ Object

Get an RR of the specified type and class



373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/dnsruby/resource/RR.rb', line 373

def self.get_class(type_value, class_value) #:nodoc: all
  if type_value == Types::OPT
    return Class.new(OPT)
  elsif type_value.class == Class
    type_value = type_value.const_get(:TypeValue)
    return find_class(type_value, Classes.to_code(class_value))
  else
    type_value = (type_value.class == Types) ? type_value.code : Types.new(type_value).code
    class_value = (class_value.class == Classes) ? class_value.code : Classes.new(class_value).code
    return find_class(type_value, class_value)
  end
end

.get_num(bytes) ⇒ Object



410
411
412
413
414
415
416
417
418
# File 'lib/dnsruby/resource/RR.rb', line 410

def self.get_num(bytes)
  ret = 0
  shift = (bytes.length - 1) * 8
  bytes.each_byte do |byte|
    ret += byte.to_i << shift
    shift -= 8
  end
  ret
end

.implemented_rrsObject

Return an array of all the currently implemented RR types



274
275
276
# File 'lib/dnsruby/resource/RR.rb', line 274

def RR.implemented_rrs
  @@implemented_rr_map ||= ClassHash.keys.map { |key| Dnsruby::Types.to_string(key[0]) }
end

.new_from_data(*args) ⇒ Object

:nodoc: all



261
262
263
264
265
266
267
268
269
270
271
# File 'lib/dnsruby/resource/RR.rb', line 261

def RR.new_from_data(*args) #:nodoc: all
  name, rrtype, rrclass, ttl, rdlength, data, offset = args
  rdata = data ? data[offset, rdlength] : []
  decoder = MessageDecoder.new(rdata)
  record = get_class(rrtype, rrclass).decode_rdata(decoder)
  record.name = Name.create(name)
  record.ttl = ttl
  record.type = rrtype
  record.klass = Classes.new(rrclass)
  record
end

.new_from_hash(inhash) ⇒ Object

Create a new RR from the hash. The name is required; all other fields are optional. Type defaults to ANY and the Class defaults to IN. The TTL defaults to 0.

If the type is specified, then it is necessary to provide ALL of the resource record fields which are specific to that record; i.e. for an MX record, you would need to specify the exchange and the preference

require 'Dnsruby'
rr = Dnsruby::RR.new_from_hash({:name => "example.com"})
rr = Dnsruby::RR.new_from_hash({:name => "example.com", :type => Types.MX, :ttl => 10, :preference => 5, :exchange => "mx1.example.com"})


141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/dnsruby/resource/RR.rb', line 141

def RR.new_from_hash(inhash)
  hash = inhash.clone
  type = hash[:type] || Types::ANY
  klass = Classes.new(hash[:klass] || Classes::IN)
  ttl = hash[:ttl] || 0
  record_class = get_class(type, klass)
  record = record_class.new
  record.name = hash[:name]
  unless record.name.kind_of?(Name)
    record.name = Name.create(record.name)
  end
  record.ttl = ttl
  record.type = type
  record.klass = Classes.new(klass)
  hash.delete(:name)
  hash.delete(:type)
  hash.delete(:ttl)
  hash.delete(:klass)
  record.from_hash(hash)
  record
end

.new_from_string(rrstring) ⇒ Object

Returns a Dnsruby::RR object of the appropriate type and initialized from the string passed by the user. The format of the string is that used in zone files, and is compatible with the string returned by Net::DNS::RR.inspect

The name and RR type are required; all other information is optional. If omitted, the TTL defaults to 0 and the RR class defaults to IN.

All names must be fully qualified. The trailing dot (.) is optional.

a     = Dnsruby::RR.new_from_string("foo.example.com. 86400 A 10.1.2.3")
mx    = Dnsruby::RR.new_from_string("example.com. 7200 MX 10 mailhost.example.com.")
cname = Dnsruby::RR.new_from_string("www.example.com 300 IN CNAME www1.example.com")
txt   = Dnsruby::RR.new_from_string('baz.example.com 3600 HS TXT "text record"')


180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/dnsruby/resource/RR.rb', line 180

def RR.new_from_string(rrstring)
  #  strip out comments
  #  Test for non escaped ";" by means of the look-behind assertion
  #  (the backslash is escaped)
  rrstring = rrstring.gsub(/(\?<!\\);.*/o, '')

  matches = (/#{@@RR_REGEX}/xo).match(rrstring)
  unless matches
    raise "#{rrstring} did not match RR pattern. Please report this to the author!"
  end

  name    = matches[1]
  ttl     = matches[2].to_i || 0
  rrclass = matches[3] || ''
  rrtype  = matches[4] || ''
  rdata   = matches[5] || ''

  rdata.gsub!(/\s+$/o, '') if rdata

  #  RFC3597 tweaks
  #  This converts to known class and type if specified as TYPE###
  if rrtype  =~/^TYPE\d+/o
    rrtype  = Dnsruby::Types.typesbyval(Dnsruby::Types::typesbyname(rrtype))
  end
  if rrclass =~/^CLASS\d+/o
    rrclass = Dnsruby::Classes.classesbyval(Dnsruby::Classes::classesbyname(rrclass))
  end

  if rrtype == '' && rrclass == 'ANY'
    rrtype  = 'ANY'
    rrclass = 'IN'
  elsif rrclass == ''
    rrclass = 'IN'
  end

  if rrtype == ''
    rrtype = 'ANY'
  end

  unless %w(NAPTR TXT).include?(rrtype)
    if rdata
      rdata.gsub!('(', '')
      rdata.gsub!(')', '')
    end
  end

  test_length = ->(hexdump, rdlength) do
    if hexdump.length != rdlength * 2
      raise "#{rdata} is inconsistent; length should be #{rdlength * 2} but is #{hexdump.length}."
    end
  end

  pack_rdata = ->(regex) do
    rdata =~ regex
    matches = regex.match(rdata)
    rdlength = matches[1].to_i
    hexdump  = matches[2].gsub(/\s*/, '')

    test_length.(hexdump, rdlength)
    packed_rdata = [hexdump].pack('H*')

    [packed_rdata, rdlength]
  end

  if implemented_rrs.include?(rrtype) && rdata !~/^\s*\\#/o
    return _get_subclass(name, rrtype, rrclass, ttl, rdata)
  elsif implemented_rrs.include?(rrtype)   # A known RR type starting with \#
    packed_rdata, rdlength = pack_rdata.(/\\#\s+(\d+)\s+(.*)$/o)
    return new_from_data(name, rrtype, rrclass, ttl, rdlength, packed_rdata, 0) # rdata.length() - rdlength);
  elsif rdata =~ /\s*\\#\s+\d+\s+/o
    regex = /\\#\s+(\d+)\s+(.*)$/o
    # We are now dealing with the truly unknown.
    raise 'Expected RFC3597 representation of RDATA' unless rdata =~ regex
    packed_rdata, rdlength = pack_rdata.(regex)
    return new_from_data(name, rrtype, rrclass, ttl, rdlength, packed_rdata, 0) # rdata.length() - rdlength);
  else
    # God knows how to handle these...
    return _get_subclass(name, rrtype, rrclass, ttl, '')
  end
end

Instance Method Details

#<=>(other) ⇒ Object



23
24
25
26
27
28
29
30
31
# File 'lib/dnsruby/resource/RR.rb', line 23

def <=>(other)
  #       return 1 if ((!other) || !(other.name) || !(other.type))
  #       return -1 if (!@name)
  if @name.canonical == other.name.canonical
    @type.code != other.type.code ? (@type.code <=> other.type.code) : (@rdata <=> other.rdata)
  else
    @name <=> other.name
  end
end

#==(other) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/dnsruby/resource/RR.rb', line 322

def ==(other)
  return false unless self.class == other.class

  ivars_to_compare = ->(object) do
    ivars = object.instance_variables.map { |var| var.to_s }
    ivars.delete '@ttl' # RFC 2136 section 1.1
    ivars.delete '@rdata'
    if self.type == Types.DS
      ivars.delete '@digest'
    end
    ivars.sort
  end

  get_instance_var_values = ->(object, ivar_names) do
    ivar_names.map { |ivar_name| object.instance_variable_get(ivar_name) }
  end

  self_ivars  = ivars_to_compare.(self)
  other_ivars = ivars_to_compare.(other)
  return false unless self_ivars == other_ivars

  self_values  = get_instance_var_values.(self, self_ivars)
  other_values = get_instance_var_values.(other, other_ivars)
  self_values == other_values
end

#cloneObject



75
76
77
78
# File 'lib/dnsruby/resource/RR.rb', line 75

def clone
  encoded = MessageEncoder.new { |encoder| encoder.put_rr(self, true) }.to_s
  MessageDecoder.new(encoded).get_rr
end

#encode_rdata(msg, canonical = false) ⇒ Object

:nodoc: all

Raises:



312
313
314
315
# File 'lib/dnsruby/resource/RR.rb', line 312

def encode_rdata(msg, canonical=false) #:nodoc: all
  #  to be implemented by subclasses
  raise EncodeError.new("#{self.class} is RR.")
end

#eql?(other) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


348
349
350
# File 'lib/dnsruby/resource/RR.rb', line 348

def eql?(other) #:nodoc:
  self == other
end

#from_data(data) ⇒ Object

:nodoc: all

Raises:

  • (NotImplementedError)


302
303
304
305
# File 'lib/dnsruby/resource/RR.rb', line 302

def from_data(data) #:nodoc: all
  #  to be implemented by subclasses
  raise NotImplementedError.new
end

#from_hash(hash) ⇒ Object

:nodoc: all



125
126
127
128
129
# File 'lib/dnsruby/resource/RR.rb', line 125

def from_hash(hash) #:nodoc: all
  hash.keys.each do |param|
    send("#{param}=", hash[param])
  end
end

#from_string(input) ⇒ Object

:nodoc: all



307
308
309
310
# File 'lib/dnsruby/resource/RR.rb', line 307

def from_string(input) #:nodoc: all
  #  to be implemented by subclasses
  #       raise NotImplementedError.new
end

#hashObject

:nodoc:



352
353
354
355
356
357
# File 'lib/dnsruby/resource/RR.rb', line 352

def hash # :nodoc:
  vars = (self.instance_variables - [:@ttl, :@rdata]).sort
  vars.inject(0) do |hash_value, var_name|
    hash_value ^ self.instance_variable_get(var_name).hash
  end
end

#init_defaultsObject



98
99
100
# File 'lib/dnsruby/resource/RR.rb', line 98

def init_defaults
  #  Default to do nothing
end

#rdata_to_stringObject

Get a string representation of the data section of the RR (in zone file format)



298
299
300
# File 'lib/dnsruby/resource/RR.rb', line 298

def rdata_to_string
  (@rdata && @rdata.length > 0) ? @rdata : 'no rdata'
end

#rdlengthObject



54
55
56
# File 'lib/dnsruby/resource/RR.rb', line 54

def rdlength
  rdata.length
end

#sameRRset(rec) ⇒ Object

Determines if two Records could be part of the same RRset.

This compares the name, type, and class of the Records; the ttl and
rdata are not compared.


84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/dnsruby/resource/RR.rb', line 84

def sameRRset(rec)
  if @klass != rec.klass || @name.downcase != rec.name.downcase
    return false
  elsif (rec.type == Types.RRSIG) && (@type == Types.RRSIG)
    return rec.type_covered == self.type_covered
  end
  [rec, self].each do |rr|
    if rr.type == Types::RRSIG
      return (@type == rr.type_covered) || (rec.type == rr.type_covered)
    end
  end
  @type == rec.type
end

#to_sObject

Returns a string representation of the RR in zone file format



292
293
294
295
# File 'lib/dnsruby/resource/RR.rb', line 292

def to_s
  s = name ? (name.to_s(true) + "\t") : ''
  s << [ttl, klass, type, rdata_to_string].map(&:to_s).join("\t")
end