Class: Dnsruby::RR

Inherits:
Object
  • Object
show all
Defined in:
lib/Dnsruby/resource/resource.rb,
lib/Dnsruby/resource/A.rb,
lib/Dnsruby/resource/DS.rb,
lib/Dnsruby/resource/IN.rb,
lib/Dnsruby/resource/MX.rb,
lib/Dnsruby/resource/PX.rb,
lib/Dnsruby/resource/RP.rb,
lib/Dnsruby/resource/RT.rb,
lib/Dnsruby/resource/DLV.rb,
lib/Dnsruby/resource/LOC.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/X25.rb,
lib/Dnsruby/resource/AAAA.rb,
lib/Dnsruby/resource/CERT.rb,
lib/Dnsruby/resource/ISDN.rb,
lib/Dnsruby/resource/NSAP.rb,
lib/Dnsruby/resource/NSEC.rb,
lib/Dnsruby/resource/TKEY.rb,
lib/Dnsruby/resource/TSIG.rb,
lib/Dnsruby/resource/AFSDB.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/DNSKEY.rb,
lib/Dnsruby/resource/generic.rb,
lib/Dnsruby/resource/NSEC3PARAM.rb,
lib/Dnsruby/resource/domain_name.rb

Overview

Superclass for all Dnsruby resource records.

Represents a DNS RR (resource record) [RFC1035, section 3.2]

Use Dnsruby::RR::create(…) to create a new RR record.

mx = Dnsruby::RR.create("example.com. 7200 MX 10 mailhost.example.com.")

rr = Dnsruby::RR.create({:name => "example.com", :type => "MX", :ttl => 7200, 
                               :preference => 10, :exchange => "mailhost.example.com"})

s = rr.to_s # Get a String representation of the RR (in zone file format)
rr_again = Dnsruby::RR.create(s)

Defined Under Namespace

Modules: IN Classes: ANY, CERT, CNAME, DLV, DNAME, DNSKEY, DS, DomainName, Generic, HINFO, ISDN, LOC, MB, MG, MINFO, MR, MX, NAPTR, NS, NSAP, NSEC, NSEC3, NSEC3PARAM, OPT, PTR, RP, RRSIG, RT, SOA, SPF, TKEY, TSIG, TXT, X25

Constant Summary collapse

ClassInsensitiveTypes =
[
  NS, CNAME, DNAME, DNSKEY, SOA, PTR, HINFO, MINFO, MX, TXT,
  ISDN, MB, MG, MR, NAPTR, NSAP, OPT, RP, RT, X25,
  SPF, CERT, LOC, TSIG, TKEY, ANY, RRSIG, NSEC, DS, NSEC3,
  NSEC3PARAM, DLV
]
@@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]*)\$")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#klassObject

The Resource class



203
204
205
# File 'lib/Dnsruby/resource/resource.rb', line 203

def klass
  @klass
end

#nameObject

The Resource’s domain name



199
200
201
# File 'lib/Dnsruby/resource/resource.rb', line 199

def name
  @name
end

#rdataObject

The Resource data section



207
208
209
# File 'lib/Dnsruby/resource/resource.rb', line 207

def rdata
  @rdata
end

#ttlObject

The Resource Time-To-Live



205
206
207
# File 'lib/Dnsruby/resource/resource.rb', line 205

def ttl
  @ttl
end

#typeObject Also known as: rr_type

The Resource type



201
202
203
# File 'lib/Dnsruby/resource/resource.rb', line 201

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"})


565
566
567
568
569
570
571
572
573
# File 'lib/Dnsruby/resource/resource.rb', line 565

def RR.create(*args)
  if (args.length == 1) && (args[0].class == String) 
    return new_from_string(args[0])
  elsif (args.length == 1) && (args[0].class == Hash) 
    return new_from_hash(args[0])
  else 
    return new_from_data(args)
  end
end

.decode_rdata(msg) ⇒ Object

:nodoc: all

Raises:



488
489
490
491
# File 'lib/Dnsruby/resource/resource.rb', line 488

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

.get_class(type_value, class_value) ⇒ Object

Get an RR of the specified type and class



527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
# File 'lib/Dnsruby/resource/resource.rb', line 527

def self.get_class(type_value, class_value) #:nodoc: all
  if (type_value == Types.OPT)
     return Class.new(OPT)
  end
  if (type_value.class == Class)
    type_value = type_value.const_get(:TypeValue)
    return ClassHash[[type_value, Classes.to_code(class_value)]] ||
      Generic.create(type_value, Classes.to_code(class_value))
  else
    if (type_value.class == Types)
      type_value = type_value.code
    else
      type_value = Types.new(type_value).code
    end
    if (class_value.class == Classes)
      class_value = class_value.code
    else
      class_value = Classes.new(class_value).code
    end
    return ClassHash[[type_value, class_value]] ||
       Generic.create(type_value, class_value)
  end
  return ret
end

.get_num(bytes) ⇒ Object



575
576
577
578
579
580
581
582
583
# File 'lib/Dnsruby/resource/resource.rb', line 575

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

.implemented_rrsObject

Return an array of all the currently implemented RR types



443
444
445
# File 'lib/Dnsruby/resource/resource.rb', line 443

def RR.implemented_rrs
  return ClassHash.keys.map {|k| Dnsruby::Types.to_string(k[0])}
end

.new_from_data(*args) ⇒ Object

:nodoc: all



417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/Dnsruby/resource/resource.rb', line 417

def RR.new_from_data(*args) #:nodoc: all
  name = args[0]
  rrtype = args[1]
  rrclass = args[2]
  ttl = args[3]
  rdlength = args[4]
  data = args[5]
  offset = args[6]
  rdata = []
  if (data != nil)
     rdata = data[offset, rdlength]
  end
  
  record = nil
  MessageDecoder.new(rdata) {|msg|
    record = get_class(rrtype, rrclass).decode_rdata(msg)
  }
  record.name = Name.create(name)
  record.ttl = ttl
  record.type = rrtype
  record.klass = rrclass        
  
  return 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"})


296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/Dnsruby/resource/resource.rb', line 296

def RR.new_from_hash(inhash)
  hash = inhash.clone        
  type = hash[:type] || Types::ANY
  klass = hash[:klass] || Classes::IN
  ttl = hash[:ttl] || 0
  recordclass = get_class(type, klass)
  record = recordclass.new
  record.name=hash[:name]
  if !(record.name.kind_of?Name)
    record.name = Name.create(record.name)
  end
  record.ttl=ttl
  record.type = type
  record.klass = klass
  hash.delete(:name)
  hash.delete(:type)
  hash.delete(:ttl)
  hash.delete(:klass)
  record.from_hash(hash)
  return 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"')


335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/Dnsruby/resource/resource.rb', line 335

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.gsub!(/(\?<!\\);.*/o, "");
  
  if ((rrstring =~/#{@@RR_REGEX}/xo) == nil)
    raise Exception, "#{rrstring} did not match RR pat.\nPlease report this to the author!\n"
  end
  
  name    = $1;
  ttl     = $2.to_i || 0;
  rrclass = $3 || '';
  
  
  rrtype  = $4 || '';
  rdata   = $5 || '';
  
  if rdata 
    rdata.gsub!(/\s+$/o, "")
  end
  
  # 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 && rrclass == 'ANY')
    rrtype  = 'ANY';
    rrclass = 'IN';
  elsif (rrclass=='')
    rrclass = 'IN';
  end
  
  if (rrtype == '')
    rrtype = 'ANY';
  end
  
  if (implemented_rrs.include?(rrtype) && rdata !~/^\s*\\#/o )
    subclass = _get_subclass(name, rrtype, rrclass, ttl, rdata)
    return subclass
  elsif (implemented_rrs.include?(rrtype))   # A known RR type starting with \#
    rdata =~ /\\\#\s+(\d+)\s+(.*)$/o;
    
    rdlength = $1.to_i;
    hexdump  = $2;    
    hexdump.gsub!(/\s*/, "");
    
    if hexdump.length() != rdlength*2
      raise Exception, "#{rdata} is inconsistent; length does not match content"
    end
    
    rdata = [hexdump].pack('H*');
    
    return new_from_data(name, rrtype, rrclass, ttl, rdlength, rdata, 0) # rdata.length() - rdlength);
  elsif (rdata=~/\s*\\\#\s+\d+\s+/o)
    #We are now dealing with the truly unknown.
    raise Exception, 'Expected RFC3597 representation of RDATA' unless rdata =~/\\\#\s+(\d+)\s+(.*)$/o;
    
    rdlength = $1.to_i;
    hexdump  = $2;    
    hexdump.gsub!(/\s*/o, "");
    
    if hexdump.length() != rdlength*2
      raise Exception, "#{rdata} is inconsistent; length does not match content" ;
    end
    
    rdata = [hexdump].pack('H*');
    
    return new_from_data(name,rrtype,rrclass,ttl,rdlength,rdata,0) # rdata.length() - rdlength);
  else
    #God knows how to handle these...
    subclass = _get_subclass(name, rrtype, rrclass, ttl, "")
    return subclass
  end
end

Instance Method Details

#==(other) ⇒ Object



493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
# File 'lib/Dnsruby/resource/resource.rb', line 493

def ==(other)
  return false unless self.class == other.class
  ivars = self.instance_variables
  s_ivars = []
  ivars.each {|i| s_ivars << i.to_s} # Ruby 1.9
  s_ivars.sort!
  s_ivars.delete "@ttl" # RFC 2136 section 1.1
  
  ivars = other.instance_variables
  o_ivars = []
  ivars.each {|i| o_ivars << i.to_s} # Ruby 1.9
  o_ivars.sort!
  o_ivars.delete "@ttl" # RFC 2136 section 1.1
  
  return s_ivars == o_ivars &&
    s_ivars.collect {|name| self.instance_variable_get name} == 
    o_ivars.collect {|name| other.instance_variable_get name}
end

#encode_rdata(msg, canonical = false) ⇒ Object

:nodoc: all

Raises:



483
484
485
486
# File 'lib/Dnsruby/resource/resource.rb', line 483

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)


512
513
514
# File 'lib/Dnsruby/resource/resource.rb', line 512

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

#from_data(data) ⇒ Object

:nodoc: all

Raises:

  • (NotImplementedError)


473
474
475
476
# File 'lib/Dnsruby/resource/resource.rb', line 473

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

#from_hash(hash) ⇒ Object

:nodoc: all



280
281
282
283
284
# File 'lib/Dnsruby/resource/resource.rb', line 280

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

#from_string(input) ⇒ Object

:nodoc: all



478
479
480
481
# File 'lib/Dnsruby/resource/resource.rb', line 478

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

#hashObject

:nodoc:



516
517
518
519
520
521
522
523
524
# File 'lib/Dnsruby/resource/resource.rb', line 516

def hash # :nodoc:
  h = 0      
  vars = self.instance_variables
  vars.delete "@ttl"
  vars.each {|name|
    h ^= self.instance_variable_get(name).hash
  }
  return h
end

#init_defaultsObject



253
254
255
# File 'lib/Dnsruby/resource/resource.rb', line 253

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)



465
466
467
468
469
470
471
# File 'lib/Dnsruby/resource/resource.rb', line 465

def rdata_to_string
  if (@rdata && @rdata.length > 0)
    return @rdata
  else 
    return "no rdata"
  end
end

#rdlengthObject



209
210
211
# File 'lib/Dnsruby/resource/resource.rb', line 209

def rdlength
  return 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.



241
242
243
244
245
246
247
248
249
250
251
# File 'lib/Dnsruby/resource/resource.rb', line 241

def sameRRset(rec)
  if (@klass != rec.klass || @name != rec.name)
    return false
  end
  [rec, self].each { |rr|
    if (rr.type == Types.RRSIG)
      return ((@type == rr.type_covered) || (rec.type == rr.type_covered))
    end
  }
  return (@type == rec.type)
end

#to_sObject

Returns a string representation of the RR in zone file format



460
461
462
# File 'lib/Dnsruby/resource/resource.rb', line 460

def to_s
  return (@name ? @name.to_s():"") + ".\t" +(@ttl ? @ttl.to_s():"") + "\t" + (klass() ? klass.to_s():"") + "\t" + (type() ? type.to_s():"") + "\t" + rdata_to_string
end