Class: Dnsruby::RR

Inherits:
Object
  • Object
show all
Includes:
Comparable
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/KX.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/HIP.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/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/generic.rb,
lib/Dnsruby/resource/IPSECKEY.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, DHCID, DLV, DNAME, DNSKEY, DS, DomainName, Generic, HINFO, HIP, IPSECKEY, ISDN, KX, LOC, MB, MG, MINFO, MR, MX, NAPTR, NS, NSAP, NSEC, NSEC3, NSEC3PARAM, OPT, PTR, RP, RRSIG, RT, SOA, SPF, SSHFP, TKEY, TSIG, TXT, X25

Constant Summary collapse

ClassInsensitiveTypes =
{
  Types::NS => NS,
  Types::CNAME => CNAME,
  Types::DNAME => DNAME,
  Types::DNSKEY => DNSKEY,
  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::DS => DS,
  Types::NSEC3 => NSEC3,
  Types::NSEC3PARAM => NSEC3PARAM,
  Types::DLV => DLV,
  Types::SSHFP => SSHFP,
  Types::IPSECKEY => IPSECKEY,
  Types::HIP => HIP,
  Types::DHCID => DHCID
}
@@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



250
251
252
# File 'lib/Dnsruby/resource/resource.rb', line 250

def klass
  @klass
end

#nameObject

The Resource’s domain name



246
247
248
# File 'lib/Dnsruby/resource/resource.rb', line 246

def name
  @name
end

#rdataObject

The Resource data section



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

def rdata
  @rdata
end

#ttlObject

The Resource Time-To-Live



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

def ttl
  @ttl
end

#typeObject Also known as: rr_type

The Resource type



248
249
250
# File 'lib/Dnsruby/resource/resource.rb', line 248

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


655
656
657
658
659
660
661
662
663
# File 'lib/Dnsruby/resource/resource.rb', line 655

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:



558
559
560
561
# File 'lib/Dnsruby/resource/resource.rb', line 558

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



604
605
606
607
608
609
610
611
612
613
614
615
616
# File 'lib/Dnsruby/resource/resource.rb', line 604

def self.find_class(type_value, class_value) # :nodoc: all
  klass = nil
  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



619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
# File 'lib/Dnsruby/resource/resource.rb', line 619

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 find_class(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 find_class(type_value, class_value)
  end
  return ret
end

.get_num(bytes) ⇒ Object



665
666
667
668
669
670
671
672
673
# File 'lib/Dnsruby/resource/resource.rb', line 665

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



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

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

.new_from_data(*args) ⇒ Object

:nodoc: all



484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# File 'lib/Dnsruby/resource/resource.rb', line 484

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


355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/Dnsruby/resource/resource.rb', line 355

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


394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
# File 'lib/Dnsruby/resource/resource.rb', line 394

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, "");
  
  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 ((rrtype == "NAPTR") || (rrtype == "TXT"))
  else
    if (rdata)
      rdata.gsub!("(", "")
      rdata.gsub!(")", "")
    end
  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



225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/Dnsruby/resource/resource.rb', line 225

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

#==(other) ⇒ Object



563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
# File 'lib/Dnsruby/resource/resource.rb', line 563

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.delete "@ttl" # RFC 2136 section 1.1
  s_ivars.delete "@rdata"
  if (self.type == Types.DS)
    s_ivars.delete "@digest"
  end
  s_ivars.sort!
  
  ivars = other.instance_variables
  o_ivars = []
  ivars.each {|i| o_ivars << i.to_s} # Ruby 1.9
  o_ivars.delete "@ttl" # RFC 2136 section 1.1
  o_ivars.delete "@rdata"
  if (other.type == Types.DS)
    o_ivars.delete "@digest"
  end
  o_ivars.sort!
  
  return s_ivars == o_ivars &&
    s_ivars.collect {|name| self.instance_variable_get name} ==
    o_ivars.collect {|name| other.instance_variable_get name}
end

#cloneObject



285
286
287
288
289
290
291
292
# File 'lib/Dnsruby/resource/resource.rb', line 285

def clone
  MessageDecoder.new(MessageEncoder.new {|msg|
      msg.put_rr(self, true)}.to_s) {|msg|
    r = msg.get_rr
    return r
  }

end

#encode_rdata(msg, canonical = false) ⇒ Object

:nodoc: all

Raises:



553
554
555
556
# File 'lib/Dnsruby/resource/resource.rb', line 553

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:



590
591
592
# File 'lib/Dnsruby/resource/resource.rb', line 590

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

#from_data(data) ⇒ Object

:nodoc: all

Raises:

  • (NotImplementedError)


543
544
545
546
# File 'lib/Dnsruby/resource/resource.rb', line 543

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

#from_hash(hash) ⇒ Object

:nodoc: all



339
340
341
342
343
# File 'lib/Dnsruby/resource/resource.rb', line 339

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



548
549
550
551
# File 'lib/Dnsruby/resource/resource.rb', line 548

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

#hashObject

:nodoc:



594
595
596
597
598
599
600
601
602
# File 'lib/Dnsruby/resource/resource.rb', line 594

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



312
313
314
# File 'lib/Dnsruby/resource/resource.rb', line 312

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)



535
536
537
538
539
540
541
# File 'lib/Dnsruby/resource/resource.rb', line 535

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

#rdlengthObject



256
257
258
# File 'lib/Dnsruby/resource/resource.rb', line 256

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.



297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/Dnsruby/resource/resource.rb', line 297

def sameRRset(rec)
  if (@klass != rec.klass || @name.downcase != rec.name.downcase)
    return false
  end
  if (rec.type == Types.RRSIG) && (@type == Types.RRSIG)
    return rec.type_covered == self.type_covered
  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



530
531
532
# File 'lib/Dnsruby/resource/resource.rb', line 530

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