Class: Net::DNS::RR

Inherits:
Object
  • Object
show all
Defined in:
lib/Net/DNS/RR.rb,
lib/Net/DNS/RR/A.rb,
lib/Net/DNS/RR/MB.rb,
lib/Net/DNS/RR/MG.rb,
lib/Net/DNS/RR/MR.rb,
lib/Net/DNS/RR/MX.rb,
lib/Net/DNS/RR/NS.rb,
lib/Net/DNS/RR/PX.rb,
lib/Net/DNS/RR/RP.rb,
lib/Net/DNS/RR/RT.rb,
lib/Net/DNS/RR/EID.rb,
lib/Net/DNS/RR/LOC.rb,
lib/Net/DNS/RR/OPT.rb,
lib/Net/DNS/RR/PTR.rb,
lib/Net/DNS/RR/SOA.rb,
lib/Net/DNS/RR/SPF.rb,
lib/Net/DNS/RR/SRV.rb,
lib/Net/DNS/RR/TXT.rb,
lib/Net/DNS/RR/X25.rb,
lib/Net/DNS/RR/AAAA.rb,
lib/Net/DNS/RR/CERT.rb,
lib/Net/DNS/RR/ISDN.rb,
lib/Net/DNS/RR/NSAP.rb,
lib/Net/DNS/RR/NULL.rb,
lib/Net/DNS/RR/TKEY.rb,
lib/Net/DNS/RR/TSIG.rb,
lib/Net/DNS/RR/AFSDB.rb,
lib/Net/DNS/RR/CNAME.rb,
lib/Net/DNS/RR/DNAME.rb,
lib/Net/DNS/RR/HINFO.rb,
lib/Net/DNS/RR/MINFO.rb,
lib/Net/DNS/RR/NAPTR.rb,
lib/Net/DNS/RR/SSHFP.rb,
lib/Net/DNS/RR/NIMLOC.rb,
lib/Net/DNS/RR/UNKNOWN.rb

Overview

NAME

Net::DNS::RR - DNS Resource Record class

DESCRIPTION

Net::DNS::RR is the base class for DNS Resource Record (RR) objects. See also the manual pages for each RR type.

*WARNING!!!* Don’t assume the RR objects you receive from a query are of a particular type – always check an object’s type before calling any of its methods. If you call an unknown method, you’ll get a nasty warning message and Net::DNS::RR will return nil to the caller.

Sorting of RR arrays

As of version 0.55 there is functionality to help you sort RR arrays. The sorting is done by Net::DNS::rrsort(), see the Net::DNS documentation. This package provides class methods to set the sorting functions used for a particular RR based on a particular attribute.

BUGS

This version of Net::DNS::RR does little sanity checking on user-created RR objects.

COPYRIGHT

Copyright © 1997-2002 Michael Fuhr.

Portions Copyright © 2002-2004 Chris Reinhardt.

Portions Copyright © 2005 Olaf Kolkman

Ruby version Copyright © 2006 AlexD (Nominet UK)

All rights reserved. This program is free software; you may redistribute it and/or modify it under the same terms as Perl itself.

EDNS0 extensions by Olaf Kolkman.

SEE ALSO

Net::DNS, Net::DNS::Resolver, Net::DNS::Packet, Net::DNS::Update, Net::DNS::Header, Net::DNS::Question, RFC 1035 Section 4.1.3

Direct Known Subclasses

Question, A, AAAA, AFSDB, CERT, CNAME, DNAME, EID, HINFO, ISDN, LOC, MB, MG, MINFO, MR, MX, NAPTR, NIMLOC, NS, NSAP, NULL, OPT, PTR, PX, RP, RT, SOA, SRV, SSHFP, TKEY, TSIG, TXT, UNKNOWN, X25

Defined Under Namespace

Classes: A, AAAA, AFSDB, CERT, CNAME, DNAME, EID, HINFO, ISDN, LOC, MB, MG, MINFO, MR, MX, NAPTR, NIMLOC, NS, NSAP, NULL, OPT, PTR, PX, RP, RT, SOA, SPF, SRV, SSHFP, TKEY, TSIG, TXT, UNKNOWN, X25

Constant Summary collapse

RRS =
[
"A",
"AAAA",
"AFSDB",
"CNAME",
"CERT",
"DNAME",
"EID",
"HINFO",
"ISDN",
"LOC",
"MB",
"MG",
"MINFO",
"MR",
"MX",
"NAPTR",
"NIMLOC",
"NS",
"NSAP",
"NULL",
"PTR",
"PX",
"RP",
"RT",
"SOA",
"SRV",
"TKEY",
"TSIG",
"TXT",
"X25",
"OPT",
"SSHFP",
"SPF"]
@@rr_regex =

@TODO Only load DNSSEC if available

nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#nameObject

The record’s domain name.



102
103
104
# File 'lib/Net/DNS/RR.rb', line 102

def name
  @name
end

#rdata(*args) ⇒ Object



395
396
397
398
399
400
401
402
403
404
405
# File 'lib/Net/DNS/RR.rb', line 395

def rdata(*args)
  if (args.length == 2)
    packet, offset = args;
    ret = rr_rdata(packet, offset);
    return ret
    # rr_rdata
  elsif (@rdata != nil)
    return @rdata;
  end
  return nil;
end

#rdlengthObject

Returns the length of the record’s data section.



114
115
116
# File 'lib/Net/DNS/RR.rb', line 114

def rdlength
  @rdlength
end

#rrclassObject

The record’s class.



108
109
110
# File 'lib/Net/DNS/RR.rb', line 108

def rrclass
  @rrclass
end

#ttlObject

Returns the record’s time-to-live (TTL).



111
112
113
# File 'lib/Net/DNS/RR.rb', line 111

def ttl
  @ttl
end

#typeObject

The record’s type.



105
106
107
# File 'lib/Net/DNS/RR.rb', line 105

def type
  @type
end

Class Method Details

._get_subclass(name, type, rrclass, ttl, rdlength = 0) ⇒ Object



524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/Net/DNS/RR.rb', line 524

def RR._get_subclass(name, type, rrclass, ttl, rdlength=0)
  return unless (type!=nil)
  if RRS.include?(type)
    klass = Net::DNS::RR.const_get(type)
  else
    klass = Net::DNS::RR::UNKNOWN
  end
  ret = klass.new
  ret.name=(name)
  ret.type=(type)
  ret.rrclass=(rrclass)
  ret.rdlength=(rdlength)
  ret.ttl=(ttl)
  ret.create_rrsort_func
  return ret
end

._name2wire(name) ⇒ Object



511
512
513
514
515
516
517
518
519
520
521
522
# File 'lib/Net/DNS/RR.rb', line 511

def RR._name2wire(name)
  rdata="";
  compname = "";
  dname = Net::DNS::name2labels(name);
  
  dname.each { |i|
    rdata += [i.length].pack('C');
    rdata += i ;
  }    
  rdata += [0].pack('C');
  return rdata;
end

.build_regexObject



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/Net/DNS/RR.rb', line 124

def RR.build_regex
  if (@@rr_regex!=nil)
    return @@rr_regex
  end
  #   Types = join('|', sort { length $b <=> length $a } keys Net::DNS::TypesByName)
  # Longest ones go first, so the regex engine will match AAAA before A.
  types = (Net::DNS::Typesbyname.keys.sort { |a, b| b.length <=> a.length }).join('|')
  types += '|TYPE\\d+';
  
  #	my $classes = join('|', keys %Net::DNS::classesbyname, 'CLASS\\d+');
  classes = Net::DNS::Classesbyname.keys.join('|') + "|CLASS\\d+"
  
  #        #        @rr_regex = Regexp.new("^\s*(\S+)\s*(\d+)?\s*(#{classes})?\s*(#{types})?\s*(.*)$")
  #        @rr_regex = Regexp.new("^\\s*(\\S+)\\s*(\\d+)?\\s*(#{classes})?\\s*(#{types})?\\s*(.*)\$"); 
  @@rr_regex = Regexp.new("^\\s*(\\S+)\\s*(\\d+)?\\s*(#{classes})?\\s*(#{types})?\\s*([\\s\\S]*)\$"); 
  return @@rr_regex
end

.create(*args) ⇒ Object

String version

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

Returns a Net::DNS::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. Omitting the optional fields is useful for creating the empty RDATA sections required for certain dynamic update operations. See the Net::DNS::Update manual page for additional examples.

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

Hash version

rr = Net::DNS::RR.create({

"name"    => "foo.example.com",
"ttl"     => 86400,
"rrclass"   => "IN",
"type"    => "A",
"address" => "10.1.2.3"
})

rr = Net::DNS::RR.create(

"name" => "foo.example.com",
"type" => "A"

)

Returns an RR object of the appropriate type, or a Net::DNS::RR object if the type isn’t implemented. See the manual pages for each RR type to see what fields the type requires.

The name and type fields are required; all others are optional. If omitted, ttl defaults to 0 and rrclass defaults to IN. Omitting the optional fields is useful for creating the empty RDATA sections required for certain dynamic update operations.

The fields are case-insensitive, but starting each with uppercase is recommended.



205
206
207
208
209
210
211
212
213
# File 'lib/Net/DNS/RR.rb', line 205

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

.new_from_data(args) ⇒ Object



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/Net/DNS/RR.rb', line 215

def RR.new_from_data(args)
  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]
  if (RRS.include?(rrtype))
    subclass = _get_subclass(name, rrtype, rrclass, ttl, rdlength);          
  else
    subclass = _get_subclass(name, rrtype, rrclass, ttl, rdlength);          
  end
  subclass.init(data, offset);
  return subclass
end

.new_from_hash(values) ⇒ Object

Raises:

  • (ArgumentError)


233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/Net/DNS/RR.rb', line 233

def RR.new_from_hash(values)
  raise ArgumentError, 'RR name not specified' if !(values.has_key?(:name))
  raise ArgumentError, 'RR type not specified' if !(values.has_key?(:type))
  name = values[:name]
  rrtype = values[:type]
  rrclass = 'IN' 
  if (values.has_key?:class)
    rrclass = values[:class] 
  end
  ttl = 0
  if (values.has_key?:ttl)
    ttl = values[:ttl] 
  end
  rdata = ""
  if (values.has_key?:data)
    rdata = values[:data] 
  end
  rdlength = rdata.length
  
  subclass = _get_subclass(name, rrtype, rrclass, ttl, rdlength);          
  
  subclass.init(values)          
  return subclass
end

.new_from_string(rrstring, update_type = nil) ⇒ Object



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
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
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
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
# File 'lib/Net/DNS/RR.rb', line 258

def RR.new_from_string(rrstring, update_type=nil)        
  build_regex()
  
  # 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}/xso) == nil)
  if ((rrstring =~@@rr_regex) == 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  = Net::DNS::typesbyval(Net::DNS::typesbyname(rrtype))
  end
  if rrclass =~/^CLASS\d+/o
    rrclass = Net::DNS::classesbyval(Net::DNS::classesbyname(rrclass))
  end
  
  
  if (rrtype=='' && rrclass && rrclass == 'ANY')
    rrtype  = 'ANY';
    rrclass = 'IN';
  elsif (rrclass=='')
    rrclass = 'IN';
  end
  
  if (rrtype == '')
    rrtype = 'ANY';
  end
  
  if (update_type)
    update_type.downcase!;
    
    if (update_type == 'yxrrset')
      ttl     = 0;
      rrclass = 'ANY' unless rdata!=nil && rdata.length > 0
    elsif (update_type == 'nxrrset')
      ttl     = 0;
      rrclass = 'NONE';
      rdata   = '';
    elsif (update_type == 'yxdomain')
      ttl     = 0;
      rrclass = 'ANY';
      rrtype  = 'ANY';
      rdata   = '';
    elsif (update_type == 'nxdomain')
      ttl     = 0;
      rrclass = 'NONE';
      rrtype  = 'ANY';
      rdata   = '';
    elsif (update_type =~/^(rr_)?add$/o)
      ttl = 86400 unless ttl!=nil
    elsif (update_type =~/^(rr_)?del(ete)?$/o)
      ttl     = 0;
      rrclass = (rdata != nil && rdata.length > 0) ? 'NONE' : 'ANY';
    end
  end
  
  
  if (RRS.include?(rrtype) && rdata !~/^\s*\\#/o )
    #          subclass = _get_subclass(rrtype);
    subclass = _get_subclass(name, rrtype, rrclass, ttl);          
    
    subclass.init(rdata);
    return subclass
  elsif (RRS.include?(rrtype))   # A RR type known to Net::DNS 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... bless them in the RR class.
    subclass = _get_subclass(name, rrtype, rrclass, ttl);          
    return subclass
  end
end

Instance Method Details

#_canonicaldataObject


This method is called by SIG objects verify method. 
It is almost the same as data but needed to get an representation of the
packets in wire format withoud domain name compression.
It is essential to DNSSEC RFC 2535 section 8



471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
# File 'lib/Net/DNS/RR.rb', line 471

def _canonicaldata
  data=''
  dname=Net::DNS::name2labels(@name)
  #	    for (my $i=0;$i<dname;$i++){
  i = 0
  dname.length.times do 
    data += [dname[i].length].pack('C')
    data += dname[i].downcase
    i += 1
  end
  data += [0].pack('C')
  data += [Net::DNS::typesbyname(@type.upcase)].pack('n')
  data += [Net::DNS::classesbyname(@rrclass.upcase)].pack('n')
  data += [@ttl].pack('N')
  
  rdata = _canonicalRdata
  
  data += [rdata.length].pack('n')
  data += rdata
  return data
end

#_canonicalRdataObject

These are methods that are used in the DNSSEC context… Some RR have domain names in them. Verification works only on RRs with uncompressed domain names. (Canonical format as in sect 8 of RFC2535) _canonicalRdata is overwritten in those RR objects that have domain names in the RDATA and _name2wire is used to convert a domain name to “wire format”



501
502
503
504
505
# File 'lib/Net/DNS/RR.rb', line 501

def _canonicalRdata
  packet=Net::DNS::Packet.new()
  rdata = rr_rdata(packet,0)
  return rdata
end

#_name2wire(name) ⇒ Object



507
508
509
# File 'lib/Net/DNS/RR.rb', line 507

def _name2wire(name)
  return RR._name2wire(name)
end

#create_rrsort_funcObject



541
542
543
544
# File 'lib/Net/DNS/RR.rb', line 541

def create_rrsort_func
  @rrsortfunc=Hash.new()
  init_rrsort_func
end

#data(packet, offset) ⇒ Object


sub data

This method is called by Net::DNS::Packet->data to get the binary representation of an RR.




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
# File 'lib/Net/DNS/RR.rb', line 419

def data(packet, offset)
  # Don't compress TSIG or TKEY names and don't mess with EDNS0 packets
  if (@type.upcase == 'TSIG' || @type.upcase == 'TKEY')
    tmp_packet = Net::DNS::Packet.new_from_binary()
    data = tmp_packet.dn_comp(@name, 0)
  elsif (@type.upcase == 'OPT')
    tmp_packet = Net::DNS::Packet.new_from_binary()
    data = tmp_packet.dn_comp('', 0)
  else
    data = packet.dn_comp(@name, offset)
  end
  qtype     = @type.upcase;
  ret = (qtype =~ /^\d+$/o)
  qtype_val = (ret != nil) ? qtype : Net::DNS::typesbyname(qtype)
  qtype_val    = 0 if (qtype_val==nil)
  
  qclass_val = 0
  if (@rrclass != nil) 
    qclass     = @rrclass.to_s.upcase
    ret = qclass =~ /^\d+$/o
    qclass_val = (ret != nil) ? qclass : Net::DNS::classesbyname(qclass)
    qclass_val    = 0 if (qclass_val==nil)
  end        
  data += [qtype_val].pack('n')
  
  # If the type is OPT then class will need to contain a decimal number
  # containing the UDP payload size. (RFC2671 section 4.3)
  if (@type != 'OPT') 
    data += [qclass_val].pack('n')
  else
    data += [@rrclass].pack('n')
  end
  
  data += [@ttl].pack('N')
  
  offset += data.length + Net::DNS::INT16SZ	# allow for rdlength
  
  rd = rdata(packet, offset)
  
  data += [rd.length].pack('n')
  data+=rd
  
  return data
end

#get_rrsort_func(attribute = nil) ⇒ Object



572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
# File 'lib/Net/DNS/RR.rb', line 572

def get_rrsort_func(attribute=nil)
  if (@rrsortfunc != nil)
    if (attribute!=nil &&  @rrsortfunc[attribute]!=nil)
      #  The default overwritten by the class variable in Net::DNS
      return @rrsortfunc[attribute];
    elsif((attribute==nil) &&  (@rrsortfunc[:default_sort]!=nil))
      #  The default overwritten by the class variable in Net::DNS
      return @rrsortfunc[:default_sort];
    end    
  end
  if( attribute!=nil && attribute != "")           
    ret = Proc.new { |a,b| (a.respond_to?(attribute) ? (a.send(attribute) <=> b.send(attribute)) : (a._canonicaldata() <=> b._canonicaldata())) }
    return ret
  else
    ret = Proc.new { |a,b| a._canonicaldata() <=> b._canonicaldata() }
    return ret           
  end
  
  return sortsub;
end

#init(*args) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/Net/DNS/RR.rb', line 142

def init(*args)
  if (args.length == 2)
    #          @rdata = args[0]
    #          @rdlength = @rdata.length
    new_from_data(args[0], args[1])
  elsif (args.length == 1)
    if (args[0].class == String)
      @rdatastr = args[0]
      new_from_string(args[0])
    elsif (args[0].class == Hash)
      new_from_hash(args[0])
    end
  end
end

#init_rrsort_funcObject



546
547
548
# File 'lib/Net/DNS/RR.rb', line 546

def init_rrsort_func
  # empty implementation for interested subclasses to fill out
end

#inspectObject

Returns a string representation of the RR. Calls the rdatastr method to get the RR-specific data.

print rr.inspect, "\n"


383
384
385
# File 'lib/Net/DNS/RR.rb', line 383

def inspect 
  return @name + ".\t" +@ttl.to_s + "\t" + @rrclass.to_s + "\t" + @type + "\t" + ((rdatastr()!=nil && rdatastr().length>0) ? rdatastr() : '; no data')
end

#rdatastrObject

Returns a string containing RR-specific data.

s = rr.rdatastr


390
391
392
393
# File 'lib/Net/DNS/RR.rb', line 390

def rdatastr 
  # For subclasses to implement themselves
  @rdlength!=nil ? "; rdlength = #{@rdlength}" : '';
end

#rr_rdata(*args) ⇒ Object



407
408
409
# File 'lib/Net/DNS/RR.rb', line 407

def rr_rdata(*args)
  return (@rdata!=nil ? @rdata : '');
end

#set_rrsort_func(attribute, func) ⇒ Object

set_rrsort_func needs to be called as a class method. The first argument is the attribute name on which the sorting will need to take place. If you specify “default_sort” than that is the sort algorithm that will be used in the case that rrsort() is called without an RR attribute as argument. The second argument is a Proc to do the sort.

The following example is the sorting function that actually is implemented in SRV.

Net::DNS::RR::SRV.set_rrsort_func("priority", Proc.new {  |a,b|

a.priority <=> b.priority || b.weight <=> a.weight})

Net::DNS::RR::SRV.set_rrsort_func("default_sort", Proc.new { |a,b|

a.priority <=> b.priority || b.weight <=> a.weight})



568
569
570
# File 'lib/Net/DNS/RR.rb', line 568

def set_rrsort_func(attribute, func)
  @rrsortfunc[attribute]=func;
end