Class: Dnsruby::RR

Inherits:
Object
  • Object
show all
Defined in:
lib/Dnsruby/resource/resource.rb,
lib/Dnsruby/resource/A.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/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/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/generic.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, DNAME, DomainName, Generic, HINFO, ISDN, LOC, MB, MG, MINFO, MR, MX, NAPTR, NS, NSAP, OPT, PTR, RP, RT, SOA, SPF, TKEY, TSIG, TXT, X25

Constant Summary collapse

ClassInsensitiveTypes =
[
  NS, CNAME, DNAME, SOA, PTR, HINFO, MINFO, MX, TXT,
  ISDN, MB, MG, MR, NAPTR, NSAP, OPT, RP, RT, X25,
  SPF, CERT, LOC, TSIG, TKEY, ANY
]
@@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



106
107
108
# File 'lib/Dnsruby/resource/resource.rb', line 106

def klass
  @klass
end

#nameObject

The Resource’s domain name



102
103
104
# File 'lib/Dnsruby/resource/resource.rb', line 102

def name
  @name
end

#rdataObject

The Resource data section



110
111
112
# File 'lib/Dnsruby/resource/resource.rb', line 110

def rdata
  @rdata
end

#ttlObject

The Resource Time-To-Live



108
109
110
# File 'lib/Dnsruby/resource/resource.rb', line 108

def ttl
  @ttl
end

#typeObject Also known as: rr_type

The Resource type



104
105
106
# File 'lib/Dnsruby/resource/resource.rb', line 104

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


448
449
450
451
452
453
454
455
456
# File 'lib/Dnsruby/resource/resource.rb', line 448

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:



375
376
377
378
# File 'lib/Dnsruby/resource/resource.rb', line 375

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



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'lib/Dnsruby/resource/resource.rb', line 410

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

.implemented_rrsObject

Return an array of all the currently implemented RR types



330
331
332
# File 'lib/Dnsruby/resource/resource.rb', line 330

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

.new_from_data(*args) ⇒ Object

:nodoc: all



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/Dnsruby/resource/resource.rb', line 307

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 = data[offset, rdlength]
  
  record = nil
  MessageDecoder.new(rdata) {|msg|
    record = get_class(rrtype, rrclass).decode_rdata(msg)
  }
  record.name = 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"})


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/Dnsruby/resource/resource.rb', line 182

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


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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/Dnsruby/resource/resource.rb', line 221

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
  if name 
    name.gsub!(/\.$/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



380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/Dnsruby/resource/resource.rb', line 380

def ==(other)
  return false unless self.class == other.class
  s_ivars = self.instance_variables
  s_ivars.sort!
  s_ivars.delete "@ttl" # RFC 2136 section 1.1
  
  o_ivars = other.instance_variables
  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) ⇒ Object

:nodoc: all

Raises:



370
371
372
373
# File 'lib/Dnsruby/resource/resource.rb', line 370

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

#eql?(other) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


395
396
397
# File 'lib/Dnsruby/resource/resource.rb', line 395

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

#from_data(data) ⇒ Object

:nodoc: all

Raises:

  • (NotImplementedError)


360
361
362
363
# File 'lib/Dnsruby/resource/resource.rb', line 360

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

#from_hash(hash) ⇒ Object

:nodoc: all



166
167
168
169
170
# File 'lib/Dnsruby/resource/resource.rb', line 166

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



365
366
367
368
# File 'lib/Dnsruby/resource/resource.rb', line 365

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

#hashObject

:nodoc:



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

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



140
141
142
# File 'lib/Dnsruby/resource/resource.rb', line 140

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)



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

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

#rdlengthObject



112
113
114
# File 'lib/Dnsruby/resource/resource.rb', line 112

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.



136
137
138
# File 'lib/Dnsruby/resource/resource.rb', line 136

def sameRRset(rec)
  return (@type == rec.type && @klass == rec.klass && @name== rec.name)
end

#to_sObject

Returns a string representation of the RR in zone file format



347
348
349
# File 'lib/Dnsruby/resource/resource.rb', line 347

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