Class Dnsruby::Message
In: lib/Dnsruby/message.rb
Parent: Object
Message Update ResolvError EncodeError OtherResolvError ServFail FormErr DecodeError NXRRSet YXDomain NotImp NXDomain VerifyError NotAuth YXRRSet NotZone Refused TsigError CodeMapper Types MetaTypes QTypes Nsec3HashAlgorithms Algorithms OpCode Classes ExtendedRCode Modes RCode Comparable Name RRSet TsigNotSignedResponseError Resolver SingleResolver StandardError TimeoutError ResolvTimeout DNS Dnssec Hosts RR\n[lib/Dnsruby/resource/A.rb\nlib/Dnsruby/resource/AAAA.rb\nlib/Dnsruby/resource/AFSDB.rb\nlib/Dnsruby/resource/CERT.rb\nlib/Dnsruby/resource/DHCID.rb\nlib/Dnsruby/resource/DLV.rb\nlib/Dnsruby/resource/DNSKEY.rb\nlib/Dnsruby/resource/DS.rb\nlib/Dnsruby/resource/HINFO.rb\nlib/Dnsruby/resource/HIP.rb\nlib/Dnsruby/resource/IN.rb\nlib/Dnsruby/resource/IPSECKEY.rb\nlib/Dnsruby/resource/ISDN.rb\nlib/Dnsruby/resource/KX.rb\nlib/Dnsruby/resource/LOC.rb\nlib/Dnsruby/resource/MINFO.rb\nlib/Dnsruby/resource/MX.rb\nlib/Dnsruby/resource/NAPTR.rb\nlib/Dnsruby/resource/NSAP.rb\nlib/Dnsruby/resource/NSEC.rb\nlib/Dnsruby/resource/NSEC3.rb\nlib/Dnsruby/resource/NSEC3PARAM.rb\nlib/Dnsruby/resource/OPT.rb\nlib/Dnsruby/resource/PX.rb\nlib/Dnsruby/resource/RP.rb\nlib/Dnsruby/resource/RRSIG.rb\nlib/Dnsruby/resource/RT.rb\nlib/Dnsruby/resource/SOA.rb\nlib/Dnsruby/resource/SPF.rb\nlib/Dnsruby/resource/SRV.rb\nlib/Dnsruby/resource/SSHFP.rb\nlib/Dnsruby/resource/TKEY.rb\nlib/Dnsruby/resource/TSIG.rb\nlib/Dnsruby/resource/TXT.rb\nlib/Dnsruby/resource/X25.rb\nlib/Dnsruby/resource/domain_name.rb\nlib/Dnsruby/resource/generic.rb\nlib/Dnsruby/resource/resource.rb] Recursor IPv6 IPv4 ZoneTransfer MessageDecoder MessageEncoder Question Header TheLog ValidatorThread PacketSender ResolverRuby Config KeyCache Cache SingleVerifier SelectThread Resolv ZoneReader lib/Dnsruby/DNS.rb lib/Dnsruby/dnssec.rb lib/Dnsruby/Hosts.rb lib/Dnsruby/resource/PX.rb lib/Dnsruby/Recursor.rb lib/Dnsruby/update.rb lib/Dnsruby/ipv6.rb lib/Dnsruby/ipv4.rb lib/Dnsruby/code_mapper.rb lib/Dnsruby/zone_transfer.rb lib/Dnsruby/message.rb lib/Dnsruby/TheLog.rb lib/Dnsruby/resource/resource.rb lib/Dnsruby/validator_thread.rb lib/Dnsruby/PacketSender.rb lib/Dnsruby/Resolver.rb lib/Dnsruby/Config.rb lib/Dnsruby/key_cache.rb lib/Dnsruby/Cache.rb lib/Dnsruby/single_verifier.rb lib/Dnsruby/SingleResolver.rb lib/Dnsruby/select_thread.rb lib/Dnsruby/name.rb lib/dnsruby.rb lib/Dnsruby/resource/TKEY.rb lib/Dnsruby/zone_reader.rb Dnsruby dot/m_61_0.png

Defines a DNS packet.

RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 2845

Sections

Message objects have five sections:

  • The header section, a Dnsruby::Header object.
        msg.header=Header.new(...)
        header = msg.header
    
  • The question section, an array of Dnsruby::Question objects.
        msg.add_question(Question.new(domain, type, klass))
        msg.each_question do |question|  ....   end
    
  • The answer section, an array of Dnsruby::RR objects.
        msg.add_answer(RR.create({:name    => "a2.example.com",
                     :type    => "A", :address => "10.0.0.2"}))
        msg.each_answer {|answer| ... }
    
  • The authority section, an array of Dnsruby::RR objects.
        msg.add_authority(rr)
        msg.each_authority {|rr| ... }
    
  • The additional section, an array of Dnsruby::RR objects.
        msg.add_additional(rr)
        msg.each_additional {|rr| ... }
    

In addition, each_resource iterates the answer, additional and authority sections :

      msg.each_resource {|rr| ... }

Packet format encoding

      Dnsruby::Message#encode
      Dnsruby::Message::decode(data)

Additional information

security_level records the current DNSSEC status of this Message. answerfrom records the server which this Message was received from. cached records whether this response came from the cache.

Methods

Classes and Modules

Class Dnsruby::Message::Section
Class Dnsruby::Message::SecurityLevel

External Aliases

question -> zone
  In dynamic update packets, the question section is known as zone and specifies the zone to be updated.
answer -> pre
  In dynamic update packets, the answer section is known as pre or prerequisite and specifies the RRs or RRsets which must or must not preexist.
add_answer -> add_pre
pre -> prerequisite
  In dynamic update packets, the answer section is known as pre or prerequisite and specifies the RRs or RRsets which must or must not preexist.
add_pre -> add_prerequisite
authority -> update
  In dynamic update packets, the authority section is known as update and specifies the RRs or RRsets to be added or delted.
add_authority -> add_update

Attributes

additional  [R]  The additional section, an array of Dnsruby::RR objects.
answer  [R]  The answer section, an array of Dnsruby::RR objects.
answerfrom  [RW]  If this Message is a response from a server, then answerfrom contains the address of the server
answerip  [RW]  If this Message is a response from a server, then answerfrom contains the IP address of the server
answersize  [RW]  If this Message is a response from a server, then answersize contains the size of the response
authority  [R]  The authority section, an array of Dnsruby::RR objects.
cached  [RW]  If the Message was returned from the cache, the cached flag will be set true. It will be false otherwise.
do_caching  [RW]  do_caching is set by default. If you do not wish dnsruby to inspect the cache before sending the query, nor cache the result of the query, then set do_caching to false.
do_validation  [RW]  do_validation is set by default. If you do not wish dnsruby to validate this message (on a Resolver with @dnssec==true), then set do_validation to false. This option does not affect caching, or the header options
header  [RW]  The header section, a Dnsruby::Header object.
question  [R]  The question section, an array of Dnsruby::Question objects.
security_error  [RW]  If there was a problem verifying this message with DNSSEC, then securiy_error will hold a description of the problem. It defaults to ""
security_level  [RW]  If dnssec is set on, then each message will have the security level set To find the precise error (if any), call Dnsruby::Dnssec::validate(msg) - the resultant exception will define the error.
send_raw  [RW]  Set send_raw if you wish to send and receive the response to this Message with no additional processing. In other words, if set, then Dnsruby will not touch the Header of the outgoing Message. This option does not affect caching or dnssec validation

This option should not normally be set.

tsigerror  [RW]  If this message has been verified using a TSIG RR then tsigerror contains the error code returned by the TSIG verification. The error will be an RCode
tsigstart  [RW] 
tsigstate  [RW]  Can be
  • :Unsigned - the default state
  • :Signed - the outgoing message has been signed
  • :Verified - the incoming message has been verified by TSIG
  • :Intermediate - the incoming message is an intermediate envelope in a TCP session

in which only every 100th envelope must be signed

  • :Failed - the incoming response failed verification

Public Class methods

Decode the encoded message

[Source]

     # File lib/Dnsruby/message.rb, line 592
592:     def Message.decode(m)
593:       o = Message.new()
594:       begin
595:         MessageDecoder.new(m) {|msg|
596:           o.header = Header.new(msg)
597:           o.header.qdcount.times {
598:             question = msg.get_question
599:             o.question << question
600:           }
601:           o.header.ancount.times {
602:             rr = msg.get_rr
603:             o.answer << rr
604:           }
605:           o.header.nscount.times {
606:             rr = msg.get_rr
607:             o.authority << rr
608:           }
609:           o.header.arcount.times { |count|
610:             start = msg.index
611:             rr = msg.get_rr
612:             if (rr.type == Types::TSIG)
613:               if (count!=o.header.arcount-1)
614:                 Dnsruby.log.Error("Incoming message has TSIG record before last record")
615:                 raise DecodeError.new("TSIG record present before last record")
616:               end
617:               o.tsigstart = start # needed for TSIG verification
618:             end
619:             o.additional << rr
620:           }
621:         }
622:       rescue DecodeError => e
623:         # So we got a decode error
624:         # However, we might have been able to fill in many parts of the message
625:         # So let's raise the DecodeError, but add the partially completed message
626:         e.partial_message = o
627:         raise e
628:       end
629:       return o
630:     end

Create a new Message. Takes optional name, type and class

type defaults to A, and klass defaults to IN

[Source]

     # File lib/Dnsruby/message.rb, line 189
189:     def initialize(*args)
190:       @header = Header.new()
191:       #      @question = Section.new(self)
192:       @question = []
193:       @answer = Section.new(self)
194:       @authority = Section.new(self)
195:       @additional = Section.new(self)
196:       @tsigstate = :Unsigned
197:       @signing = false
198:       @tsigkey = nil
199:       @answerfrom = nil
200:       @answerip = nil
201:       @send_raw = false
202:       @do_validation = true
203:       @do_caching = true
204:       @security_level = SecurityLevel.UNCHECKED
205:       @security_error = nil
206:       @cached = false
207:       type = Types::A
208:       klass = Classes::IN
209:       if (args.length > 0)
210:         name = args[0]
211:         if (args.length > 1)
212:           type = Types.new(args[1])
213:           if (args.length > 2)
214:             klass = Classes.new(args[2])
215:           end
216:         end
217:         add_question(name, type, klass)
218:       end
219:     end

Public Instance methods

[Source]

     # File lib/Dnsruby/message.rb, line 305
305:     def ==(other)
306:       ret = false
307:       if (other.kind_of?Message)
308:         ret = @header == other.header &&
309:           @question[0] == other.question[0] &&
310:           @answer == other.answer &&
311:           @authority == other.authority &&
312:           @additional == other.additional
313:       end
314:       return ret
315:     end

Add a new Question to the Message. Takes either a Question, or a name, and an optional type and class.

  • msg.add_question(Question.new("example.com", ‘MX’))
  • msg.add_question("example.com") # defaults to Types.A, Classes.IN
  • msg.add_question("example.com", Types.LOC)

[Source]

     # File lib/Dnsruby/message.rb, line 356
356:     def add_question(question, type=Types.A, klass=Classes.IN)
357:       if (!question.kind_of?Question)
358:         question = Question.new(question, type, klass)
359:       end
360:       @question << question
361:       update_counts
362:     end
add_zone(question, type=Types.A, klass=Classes.IN)

Alias for add_question

[Source]

     # File lib/Dnsruby/message.rb, line 411
411:     def each_additional
412:       @additional.each {|rec|
413:         yield rec
414:       }
415:     end

[Source]

     # File lib/Dnsruby/message.rb, line 385
385:     def each_answer
386:       @answer.each {|rec|
387:         yield rec
388:       }
389:     end

[Source]

     # File lib/Dnsruby/message.rb, line 398
398:     def each_authority
399:       @authority.each {|rec|
400:         yield rec
401:       }
402:     end
each_pre()

Alias for each_answer

each_prerequisite()

Alias for each_pre

[Source]

     # File lib/Dnsruby/message.rb, line 364
364:     def each_question
365:       @question.each {|rec|
366:         yield rec
367:       }
368:     end

Calls each_answer, each_authority, each_additional

[Source]

     # File lib/Dnsruby/message.rb, line 423
423:     def each_resource
424:       each_answer {|rec| yield rec}
425:       each_authority {|rec| yield rec}
426:       each_additional {|rec| yield rec}
427:     end

Yields each section (question, answer, authority, additional)

[Source]

     # File lib/Dnsruby/message.rb, line 418
418:     def each_section
419:       [@answer, @authority, @additional].each {|section| yield section}
420:     end
each_update()

Alias for each_authority

each_zone()

Alias for each_question

Return the encoded form of the message

 If there is a TSIG record present and the record has not been signed
 then sign it

[Source]

     # File lib/Dnsruby/message.rb, line 570
570:     def encode
571:       if ((@tsigkey) && @tsigstate == :Unsigned && !@signing)
572:         @signing = true
573:         sign!
574:         @signing = false
575:       end
576:       return MessageEncoder.new {|msg|
577:         header = @header
578:         header.encode(msg)
579:         @question.each {|q|
580:           msg.put_name(q.qname)
581:           msg.put_pack('nn', q.qtype.code, q.qclass.code)
582:         }
583:         [@answer, @authority, @additional].each {|rr|
584:           rr.each { |r|
585:             msg.put_rr(r)
586:           }
587:         }
588:       }.to_s
589:     end

[Source]

     # File lib/Dnsruby/message.rb, line 277
277:     def get_exception
278:       exception = nil
279:       if (rcode==RCode.NXDOMAIN)
280:         exception = NXDomain.new
281:       elsif (rcode==RCode.SERVFAIL)
282:         exception = ServFail.new
283:       elsif (rcode==RCode.FORMERR)
284:         exception = FormErr.new
285:       elsif (rcode==RCode.NOTIMP)
286:         exception = NotImp.new
287:       elsif (rcode==RCode.REFUSED)
288:         exception = Refused.new
289:       elsif (rcode==RCode.NOTZONE)
290:         exception = NotZone.new
291:       elsif (rcode==RCode.NOTAUTH)
292:         exception = NotAuth.new
293:       elsif (rcode==RCode.NXRRSET)
294:         exception = NXRRSet.new
295:       elsif (rcode==RCode.YXRRSET)
296:         exception = YXRRSet.new
297:       elsif (rcode==RCode.YXDOMAIN)
298:         exception = YXDomain.new
299:       elsif (rcode >= RCode.BADSIG && rcode <= RCode.BADALG)
300:         return VerifyError.new # @TODO@
301:       end
302:       return exception
303:     end

[Source]

     # File lib/Dnsruby/message.rb, line 470
470:     def get_opt
471:       each_additional do |r|
472:         if (r.type == Types::OPT)
473:           return r
474:         end
475:       end
476:       return nil
477:     end

[Source]

     # File lib/Dnsruby/message.rb, line 479
479:     def rcode
480:       rcode = @header.get_header_rcode
481:       opt = get_opt
482:       if (opt != nil)
483:         rcode = rcode.code + (opt.xrcode.code << 4)
484:         rcode = RCode.new(rcode)
485:       end
486:       return rcode;
487:     end

Return the first rrset of the specified attributes in the message

[Source]

     # File lib/Dnsruby/message.rb, line 318
318:     def rrset(name, type, klass = Classes::IN)
319:       [@answer, @authority, @additional].each do |section|
320:         if ((rrset = section.rrset(name, type, klass)).length > 0)
321:           return rrset
322:         end
323:       end
324:       return RRSet.new
325:     end

Return the rrsets of the specified type in the message

[Source]

     # File lib/Dnsruby/message.rb, line 328
328:     def rrsets(type, klass=Classes::IN)
329:       rrsetss = []
330:       [@answer, @authority, @additional].each do |section|
331:         if ((rrsets = section.rrsets(type, klass)).length > 0)
332:           rrsets.each {|rrset|
333:             rrsetss.push(rrset)
334:           }
335:         end
336:       end
337:       return rrsetss
338:     end

Return a hash, with the section as key, and the RRSets in that section as the data : {section => section_rrs}

[Source]

     # File lib/Dnsruby/message.rb, line 342
342:     def section_rrsets(type = nil, include_opt = false)
343:       ret = {}
344:       ["answer", "authority", "additional"].each do |section|
345:         ret[section] = self.send(section).rrsets(type, include_opt)
346:       end
347:       return ret
348:     end

Sets the TSIG to sign this message with. Can either be a Dnsruby::RR::TSIG object, or it can be a (name, key) tuple, or it can be a hash which takes Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.)

[Source]

     # File lib/Dnsruby/message.rb, line 442
442:     def set_tsig(*args)
443:       if (args.length == 1)
444:         if (args[0].instance_of?RR::TSIG)
445:           @tsigkey = args[0]
446:         elsif (args[0].instance_of?Hash)
447:           @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY'}.merge(args[0]))
448:         else
449:           raise ArgumentError.new("Wrong type of argument to Dnsruby::Message#set_tsig - should be TSIG or Hash")
450:         end
451:       elsif (args.length == 2)
452:         @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY', :name=>args[0], :key=>args[1]})
453:       else
454:         raise ArgumentError.new("Wrong number of arguments to Dnsruby::Message#set_tsig")
455:       end
456:     end

Was this message signed by a TSIG?

[Source]

     # File lib/Dnsruby/message.rb, line 459
459:     def signed?
460:       return (@tsigstate == :Signed ||
461:           @tsigstate == :Verified ||
462:           @tsigstate == :Failed)
463:     end

[Source]

     # File lib/Dnsruby/message.rb, line 489
489:     def to_s
490:       retval = "";
491:       
492:       if (@answerfrom != nil && @answerfrom != "")
493:         retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n";
494:       end
495:       retval = retval + ";; Security Level : #{@security_level.string}\n"
496:       
497:       retval = retval + ";; HEADER SECTION\n"
498:       # OPT pseudosection? EDNS flags, udpsize
499:       opt = get_opt
500:       if (!opt)
501:         retval = retval + @header.to_s
502:       else
503:         retval = retval + @header.to_s_with_rcode(rcode())
504:       end
505:       retval = retval + "\n"
506:       
507:       if (opt)
508:         retval = retval + opt.to_s
509:         retval = retval + "\n"
510:       end
511:             
512:       section = (@header.opcode == OpCode.UPDATE) ? "ZONE" : "QUESTION";
513:       retval = retval +  ";; #{section} SECTION (#{@header.qdcount}  record#{@header.qdcount == 1 ? '' : 's'})\n";
514:       each_question { |qr|
515:         retval = retval + ";; #{qr.to_s}\n";
516:       }
517:       
518:       if (@answer.size > 0)
519:         retval = retval + "\n";
520:         section = (@header.opcode == OpCode.UPDATE) ? "PREREQUISITE" : "ANSWER";
521:         retval = retval + ";; #{section} SECTION (#{@header.ancount}  record#{@header.ancount == 1 ? '' : 's'})\n";
522:         each_answer { |rr|
523:           retval = retval + rr.to_s + "\n";
524:         }
525:       end
526:       
527:       if (@authority.size > 0)
528:         retval = retval + "\n";
529:         section = (@header.opcode == OpCode.UPDATE) ? "UPDATE" : "AUTHORITY";
530:         retval = retval + ";; #{section} SECTION (#{@header.nscount}  record#{@header.nscount == 1 ? '' : 's'})\n";
531:         each_authority { |rr|
532:           retval = retval + rr.to_s + "\n";
533:         }
534:       end
535:       
536:       if ((@additional.size > 0 && !opt) || (@additional.size > 1))
537:         retval = retval + "\n";
538:         retval = retval + ";; ADDITIONAL SECTION (#{@header.arcount}  record#{@header.arcount == 1 ? '' : 's'})\n";
539:         each_additional { |rr|
540:           if (rr.type != Types::OPT)
541:             retval = retval + rr.to_s+ "\n"
542:           end
543:         }
544:       end
545:       
546:       return retval;
547:     end

Returns the TSIG record from the ADDITIONAL section, if one is present.

[Source]

     # File lib/Dnsruby/message.rb, line 430
430:     def tsig
431:       if (@additional.last)
432:         if (@additional.last.rr_type == Types.TSIG)
433:           return @additional.last
434:         end
435:       end
436:       return nil
437:     end

If this message was signed by a TSIG, was the TSIG verified?

[Source]

     # File lib/Dnsruby/message.rb, line 466
466:     def verified?
467:       return (@tsigstate == :Verified)
468:     end

[Validate]