Class | Dnsruby::Message |
In: |
lib/Dnsruby/message.rb
|
Parent: | Object |
RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 2845
Message objects have five sections:
msg.header=Header.new(...) header = msg.header
msg.add_question(Question.new(domain, type, klass)) msg.each_question do |question| .... end
msg.add_answer(RR.create({:name => "a2.example.com", :type => "A", :address => "10.0.0.2"})) msg.each_answer {|answer| ... }
msg.add_authority(rr) msg.each_authority {|rr| ... }
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.
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 |
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
in which only every 100th envelope must be signed
|
Decode the encoded message
# File lib/Dnsruby/message.rb, line 587 587: def Message.decode(m) 588: o = Message.new() 589: MessageDecoder.new(m) {|msg| 590: o.header = Header.new(msg) 591: o.header.qdcount.times { 592: question = msg.get_question 593: o.question << question 594: } 595: o.header.ancount.times { 596: rr = msg.get_rr 597: o.answer << rr 598: } 599: o.header.nscount.times { 600: rr = msg.get_rr 601: o.authority << rr 602: } 603: o.header.arcount.times { |count| 604: start = msg.index 605: rr = msg.get_rr 606: if (rr.type == Types::TSIG) 607: if (count!=o.header.arcount-1) 608: Dnsruby.log.Error("Incoming message has TSIG record before last record") 609: raise DecodeError.new("TSIG record present before last record") 610: end 611: o.tsigstart = start # needed for TSIG verification 612: end 613: o.additional << rr 614: } 615: } 616: return o 617: end
Create a new Message. Takes optional name, type and class
type defaults to A, and klass defaults to IN
# File lib/Dnsruby/message.rb, line 186 186: def initialize(*args) 187: @header = Header.new() 188: # @question = Section.new(self) 189: @question = [] 190: @answer = Section.new(self) 191: @authority = Section.new(self) 192: @additional = Section.new(self) 193: @tsigstate = :Unsigned 194: @signing = false 195: @tsigkey = nil 196: @answerfrom = nil 197: @answerip = nil 198: @send_raw = false 199: @do_validation = true 200: @do_caching = true 201: @security_level = SecurityLevel.UNCHECKED 202: @security_error = nil 203: @cached = false 204: type = Types::A 205: klass = Classes::IN 206: if (args.length > 0) 207: name = args[0] 208: if (args.length > 1) 209: type = Types.new(args[1]) 210: if (args.length > 2) 211: klass = Classes.new(args[2]) 212: end 213: end 214: add_question(name, type, klass) 215: end 216: end
# File lib/Dnsruby/message.rb, line 302 302: def ==(other) 303: ret = false 304: if (other.kind_of?Message) 305: ret = @header == other.header && 306: @question[0] == other.question[0] && 307: @answer == other.answer && 308: @authority == other.authority && 309: @additional == other.additional 310: end 311: return ret 312: end
Add a new Question to the Message. Takes either a Question, or a name, and an optional type and class.
# File lib/Dnsruby/message.rb, line 351 351: def add_question(question, type=Types.A, klass=Classes.IN) 352: if (!question.kind_of?Question) 353: question = Question.new(question, type, klass) 354: end 355: @question << question 356: update_counts 357: end
# File lib/Dnsruby/message.rb, line 406 406: def each_additional 407: @additional.each {|rec| 408: yield rec 409: } 410: end
# File lib/Dnsruby/message.rb, line 380 380: def each_answer 381: @answer.each {|rec| 382: yield rec 383: } 384: end
# File lib/Dnsruby/message.rb, line 393 393: def each_authority 394: @authority.each {|rec| 395: yield rec 396: } 397: end
# File lib/Dnsruby/message.rb, line 359 359: def each_question 360: @question.each {|rec| 361: yield rec 362: } 363: end
Calls each_answer, each_authority, each_additional
# File lib/Dnsruby/message.rb, line 418 418: def each_resource 419: each_answer {|rec| yield rec} 420: each_authority {|rec| yield rec} 421: each_additional {|rec| yield rec} 422: end
Yields each section (question, answer, authority, additional)
# File lib/Dnsruby/message.rb, line 413 413: def each_section 414: [@answer, @authority, @additional].each {|section| yield section} 415: 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 565 565: def encode 566: if ((@tsigkey) && @tsigstate == :Unsigned && !@signing) 567: @signing = true 568: sign! 569: @signing = false 570: end 571: return MessageEncoder.new {|msg| 572: header = @header 573: header.encode(msg) 574: @question.each {|q| 575: msg.put_name(q.qname) 576: msg.put_pack('nn', q.qtype.code, q.qclass.code) 577: } 578: [@answer, @authority, @additional].each {|rr| 579: rr.each { |r| 580: msg.put_rr(r) 581: } 582: } 583: }.to_s 584: end
# File lib/Dnsruby/message.rb, line 274 274: def get_exception 275: exception = nil 276: if (rcode==RCode.NXDOMAIN) 277: exception = NXDomain.new 278: elsif (rcode==RCode.SERVFAIL) 279: exception = ServFail.new 280: elsif (rcode==RCode.FORMERR) 281: exception = FormErr.new 282: elsif (rcode==RCode.NOTIMP) 283: exception = NotImp.new 284: elsif (rcode==RCode.REFUSED) 285: exception = Refused.new 286: elsif (rcode==RCode.NOTZONE) 287: exception = NotZone.new 288: elsif (rcode==RCode.NOTAUTH) 289: exception = NotAuth.new 290: elsif (rcode==RCode.NXRRSET) 291: exception = NXRRSet.new 292: elsif (rcode==RCode.YXRRSET) 293: exception = YXRRSet.new 294: elsif (rcode==RCode.YXDOMAIN) 295: exception = YXDomain.new 296: elsif (rcode >= RCode.BADSIG && rcode <= RCode.BADALG) 297: return VerifyError.new # @TODO@ 298: end 299: return exception 300: end
# File lib/Dnsruby/message.rb, line 465 465: def get_opt 466: each_additional do |r| 467: if (r.type == Types::OPT) 468: return r 469: end 470: end 471: return nil 472: end
# File lib/Dnsruby/message.rb, line 474 474: def rcode 475: rcode = @header.get_header_rcode 476: opt = get_opt 477: if (opt != nil) 478: rcode = rcode.code + (opt.xrcode.code << 4) 479: rcode = RCode.new(rcode) 480: end 481: return rcode; 482: end
Return the first rrset of the specified attributes in the message
# File lib/Dnsruby/message.rb, line 315 315: def rrset(name, type, klass = Classes::IN) 316: [@answer, @authority, @additional].each do |section| 317: if ((rrset = section.rrset(name, type, klass)).length > 0) 318: return rrset 319: end 320: end 321: return RRSet.new 322: end
Return the rrsets of the specified type in the message
# File lib/Dnsruby/message.rb, line 325 325: def rrsets(type, klass=Classes::IN) 326: rrsets = [] 327: [@answer, @authority, @additional].each do |section| 328: if ((rrset = section.rrsets(type, klass)).length > 0) 329: rrsets.push(rrset) 330: end 331: end 332: return rrsets 333: 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 337 337: def section_rrsets(type = nil, include_opt = false) 338: ret = {} 339: ["answer", "authority", "additional"].each do |section| 340: ret[section] = self.send(section).rrsets(type, include_opt) 341: end 342: return ret 343: 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 437 437: def set_tsig(*args) 438: if (args.length == 1) 439: if (args[0].instance_of?RR::TSIG) 440: @tsigkey = args[0] 441: elsif (args[0].instance_of?Hash) 442: @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY'}.merge(args[0])) 443: else 444: raise ArgumentError.new("Wrong type of argument to Dnsruby::Message#set_tsig - should be TSIG or Hash") 445: end 446: elsif (args.length == 2) 447: @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY', :name=>args[0], :key=>args[1]}) 448: else 449: raise ArgumentError.new("Wrong number of arguments to Dnsruby::Message#set_tsig") 450: end 451: end
Was this message signed by a TSIG?
# File lib/Dnsruby/message.rb, line 454 454: def signed? 455: return (@tsigstate == :Signed || 456: @tsigstate == :Verified || 457: @tsigstate == :Failed) 458: end
# File lib/Dnsruby/message.rb, line 484 484: def to_s 485: retval = ""; 486: 487: if (@answerfrom != nil && @answerfrom != "") 488: retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n"; 489: end 490: retval = retval + ";; Security Level : #{@security_level.string}\n" 491: 492: retval = retval + ";; HEADER SECTION\n" 493: # OPT pseudosection? EDNS flags, udpsize 494: opt = get_opt 495: if (!opt) 496: retval = retval + @header.to_s 497: else 498: retval = retval + @header.to_s_with_rcode(rcode()) 499: end 500: retval = retval + "\n" 501: 502: if (opt) 503: retval = retval + opt.to_s 504: retval = retval + "\n" 505: end 506: 507: section = (@header.opcode == OpCode.UPDATE) ? "ZONE" : "QUESTION"; 508: retval = retval + ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n"; 509: each_question { |qr| 510: retval = retval + ";; #{qr.to_s}\n"; 511: } 512: 513: if (@answer.size > 0) 514: retval = retval + "\n"; 515: section = (@header.opcode == OpCode.UPDATE) ? "PREREQUISITE" : "ANSWER"; 516: retval = retval + ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n"; 517: each_answer { |rr| 518: retval = retval + rr.to_s + "\n"; 519: } 520: end 521: 522: if (@authority.size > 0) 523: retval = retval + "\n"; 524: section = (@header.opcode == OpCode.UPDATE) ? "UPDATE" : "AUTHORITY"; 525: retval = retval + ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n"; 526: each_authority { |rr| 527: retval = retval + rr.to_s + "\n"; 528: } 529: end 530: 531: if ((@additional.size > 0 && !opt) || (@additional.size > 1)) 532: retval = retval + "\n"; 533: retval = retval + ";; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n"; 534: each_additional { |rr| 535: if (rr.type != Types::OPT) 536: retval = retval + rr.to_s+ "\n" 537: end 538: } 539: end 540: 541: return retval; 542: end
Returns the TSIG record from the ADDITIONAL section, if one is present.
# File lib/Dnsruby/message.rb, line 425 425: def tsig 426: if (@additional.last) 427: if (@additional.last.rr_type == Types.TSIG) 428: return @additional.last 429: end 430: end 431: return nil 432: end