RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 2845
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| ... }
Dnsruby::Message#encode Dnsruby::Message::decode(data)
#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.
The additional section, an array of Dnsruby::RR objects.
The answer section, an array of Dnsruby::RR objects.
If this Message is a response from a server, then answerfrom contains the address of the server
If this Message is a response from a server, then answerfrom contains the IP address of the server
If this Message is a response from a server, then answersize contains the size of the response
If the Message was returned from the cache, the cached flag will be set true. It will be false otherwise.
#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 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
The header section, a Dnsruby::Header object.
The answer section, an array of Dnsruby::RR objects.
The answer section, an array of Dnsruby::RR objects.
The question section, an array of Dnsruby::Question objects.
If there was a problem verifying this message with DNSSEC, then securiy_error will hold a description of the problem. It defaults to ""
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 - the resultant exception will define the error.
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
The authority section, an array of Dnsruby::RR objects.
The question section, an array of Dnsruby::Question objects.
Decode the encoded message
# File lib/Dnsruby/message.rb, line 592 def Message.decode(m) o = Message.new() begin MessageDecoder.new(m) {|msg| o.header = Header.new(msg) o.header.qdcount.times { question = msg.get_question o.question << question } o.header.ancount.times { rr = msg.get_rr o.answer << rr } o.header.nscount.times { rr = msg.get_rr o.authority << rr } o.header.arcount.times { |count| start = msg.index rr = msg.get_rr if (rr.type == Types::TSIG) if (count!=o.header.arcount-1) Dnsruby.log.Error("Incoming message has TSIG record before last record") raise DecodeError.new("TSIG record present before last record") end o.tsigstart = start # needed for TSIG verification end o.additional << rr } } rescue DecodeError => e # So we got a decode error # However, we might have been able to fill in many parts of the message # So let's raise the DecodeError, but add the partially completed message e.partial_message = o raise e end return o end
Create a new Message. Takes optional name, type and class
type defaults to A, and klass defaults to IN
::new("example.com") # defaults to A, IN
::new("example.com", 'AAAA')
::new("example.com", Dnsruby::Types.PTR, "HS")
# File lib/Dnsruby/message.rb, line 189 def initialize(*args) @header = Header.new() # @question = Section.new(self) @question = [] @answer = Section.new(self) @authority = Section.new(self) @additional = Section.new(self) @tsigstate = :Unsigned @signing = false @tsigkey = nil @answerfrom = nil @answerip = nil @send_raw = false @do_validation = true @do_caching = true @security_level = SecurityLevel.UNCHECKED @security_error = nil @cached = false type = Types::A klass = Classes::IN if (args.length > 0) name = args[0] if (args.length > 1) type = Types.new(args[1]) if (args.length > 2) klass = Classes.new(args[2]) end end add_question(name, type, klass) end end
# File lib/Dnsruby/message.rb, line 305 def ==(other) ret = false if (other.kind_of?Message) ret = @header == other.header && @question[0] == other.question[0] && @answer == other.answer && @authority == other.authority && @additional == other.additional end return ret end
Add a new Question to the Message. Takes either a Question, or a name, and an optional type and class.
msg.add_question(Dnsruby::Question.new("example.com", 'MX'))
msg.add_question("example.com") # defaults to Types.A, Classes.IN
msg.add_question("example.com", Types.LOC)
# File lib/Dnsruby/message.rb, line 356 def add_question(question, type=Types.A, klass=Classes.IN) if (!question.kind_of?Question) question = Question.new(question, type, klass) end @question << question update_counts end
# File lib/Dnsruby/message.rb, line 411 def each_additional @additional.each {|rec| yield rec } end
# File lib/Dnsruby/message.rb, line 385 def each_answer @answer.each {|rec| yield rec } end
# File lib/Dnsruby/message.rb, line 364 def each_question @question.each {|rec| yield rec } end
Calls #each_answer, #each_authority, #each_additional
# File lib/Dnsruby/message.rb, line 423 def each_resource each_answer {|rec| yield rec} each_authority {|rec| yield rec} each_additional {|rec| yield rec} end
Yields each section (question, answer, authority, additional)
# File lib/Dnsruby/message.rb, line 418 def each_section [@answer, @authority, @additional].each {|section| yield section} end
Return the encoded form of the message
If there is a TSIG record present and the record has not been signed then sign it
# File lib/Dnsruby/message.rb, line 570 def encode if ((@tsigkey) && @tsigstate == :Unsigned && !@signing) @signing = true sign! @signing = false end return MessageEncoder.new {|msg| header = @header header.encode(msg) @question.each {|q| msg.put_name(q.qname) msg.put_pack('nn', q.qtype.code, q.qclass.code) } [@answer, @authority, @additional].each {|rr| rr.each { |r| msg.put_rr(r) } } }.to_s end
# File lib/Dnsruby/message.rb, line 277 def get_exception exception = nil if (rcode==RCode.NXDOMAIN) exception = NXDomain.new elsif (rcode==RCode.SERVFAIL) exception = ServFail.new elsif (rcode==RCode.FORMERR) exception = FormErr.new elsif (rcode==RCode.NOTIMP) exception = NotImp.new elsif (rcode==RCode.REFUSED) exception = Refused.new elsif (rcode==RCode.NOTZONE) exception = NotZone.new elsif (rcode==RCode.NOTAUTH) exception = NotAuth.new elsif (rcode==RCode.NXRRSET) exception = NXRRSet.new elsif (rcode==RCode.YXRRSET) exception = YXRRSet.new elsif (rcode==RCode.YXDOMAIN) exception = YXDomain.new elsif (rcode >= RCode.BADSIG && rcode <= RCode.BADALG) return VerifyError.new # @TODO@ end return exception end
# File lib/Dnsruby/message.rb, line 470 def get_opt each_additional do |r| if (r.type == Types::OPT) return r end end return nil end
# File lib/Dnsruby/message.rb, line 479 def rcode rcode = @header.get_header_rcode opt = get_opt if (opt != nil) rcode = rcode.code + (opt.xrcode.code << 4) rcode = RCode.new(rcode) end return rcode; end
Return the first rrset of the specified attributes in the message
# File lib/Dnsruby/message.rb, line 318 def rrset(name, type, klass = Classes::IN) [@answer, @authority, @additional].each do |section| if ((rrset = section.rrset(name, type, klass)).length > 0) return rrset end end return RRSet.new end
Return the rrsets of the specified type in the message
# File lib/Dnsruby/message.rb, line 328 def rrsets(type, klass=Classes::IN) rrsetss = [] [@answer, @authority, @additional].each do |section| if ((rrsets = section.rrsets(type, klass)).length > 0) rrsets.each {|rrset| rrsetss.push(rrset) } end end return rrsetss end
Return a hash, with the section as key, and the RRSets in that section as the data : {section => section_rrs}
# File lib/Dnsruby/message.rb, line 342 def section_rrsets(type = nil, include_opt = false) ret = {} ["answer", "authority", "additional"].each do |section| ret[section] = self.send(section).rrsets(type, include_opt) end return ret 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.)
# File lib/Dnsruby/message.rb, line 442 def set_tsig(*args) if (args.length == 1) if (args[0].instance_of?RR::TSIG) @tsigkey = args[0] elsif (args[0].instance_of?Hash) @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY'}.merge(args[0])) else raise ArgumentError.new("Wrong type of argument to Dnsruby::Message#set_tsig - should be TSIG or Hash") end elsif (args.length == 2) @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY', :name=>args[0], :key=>args[1]}) else raise ArgumentError.new("Wrong number of arguments to Dnsruby::Message#set_tsig") end end
Was this message signed by a TSIG?
# File lib/Dnsruby/message.rb, line 459 def signed? return (@tsigstate == :Signed || @tsigstate == :Verified || @tsigstate == :Failed) end
# File lib/Dnsruby/message.rb, line 489 def to_s retval = ""; if (@answerfrom != nil && @answerfrom != "") retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n"; end retval = retval + ";; Security Level : #{@security_level.string}\n" retval = retval + ";; HEADER SECTION\n" # OPT pseudosection? EDNS flags, udpsize opt = get_opt if (!opt) retval = retval + @header.to_s else retval = retval + @header.to_s_with_rcode(rcode()) end retval = retval + "\n" if (opt) retval = retval + opt.to_s retval = retval + "\n" end section = (@header.opcode == OpCode.UPDATE) ? "ZONE" : "QUESTION"; retval = retval + ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n"; each_question { |qr| retval = retval + ";; #{qr.to_s}\n"; } if (@answer.size > 0) retval = retval + "\n"; section = (@header.opcode == OpCode.UPDATE) ? "PREREQUISITE" : "ANSWER"; retval = retval + ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n"; each_answer { |rr| retval = retval + rr.to_s + "\n"; } end if (@authority.size > 0) retval = retval + "\n"; section = (@header.opcode == OpCode.UPDATE) ? "UPDATE" : "AUTHORITY"; retval = retval + ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n"; each_authority { |rr| retval = retval + rr.to_s + "\n"; } end if ((@additional.size > 0 && !opt) || (@additional.size > 1)) retval = retval + "\n"; retval = retval + ";; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n"; each_additional { |rr| if (rr.type != Types::OPT) retval = retval + rr.to_s+ "\n" end } end return retval; end
Returns the TSIG record from the ADDITIONAL section, if one is present.
# File lib/Dnsruby/message.rb, line 430 def tsig if (@additional.last) if (@additional.last.rr_type == Types.TSIG) return @additional.last end end return nil end
If this message was signed by a TSIG, was the TSIG verified?
# File lib/Dnsruby/message.rb, line 466 def verified? return (@tsigstate == :Verified) end