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 592 592: def Message.decode(m) 593: o = Message.new() 594: MessageDecoder.new(m) {|msg| 595: o.header = Header.new(msg) 596: o.header.qdcount.times { 597: question = msg.get_question 598: o.question << question 599: } 600: o.header.ancount.times { 601: rr = msg.get_rr 602: o.answer << rr 603: } 604: o.header.nscount.times { 605: rr = msg.get_rr 606: o.authority << rr 607: } 608: o.header.arcount.times { |count| 609: start = msg.index 610: rr = msg.get_rr 611: if (rr.type == Types::TSIG) 612: if (count!=o.header.arcount-1) 613: Dnsruby.log.Error("Incoming message has TSIG record before last record") 614: raise DecodeError.new("TSIG record present before last record") 615: end 616: o.tsigstart = start # needed for TSIG verification 617: end 618: o.additional << rr 619: } 620: } 621: return o 622: 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 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
# 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.
# 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
# File lib/Dnsruby/message.rb, line 411 411: def each_additional 412: @additional.each {|rec| 413: yield rec 414: } 415: end
# File lib/Dnsruby/message.rb, line 385 385: def each_answer 386: @answer.each {|rec| 387: yield rec 388: } 389: end
# File lib/Dnsruby/message.rb, line 398 398: def each_authority 399: @authority.each {|rec| 400: yield rec 401: } 402: end
# 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
# 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)
# File lib/Dnsruby/message.rb, line 418 418: def each_section 419: [@answer, @authority, @additional].each {|section| yield section} 420: 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 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
# 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
# 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
# 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
# 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
# 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}
# 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.)
# 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?
# File lib/Dnsruby/message.rb, line 459 459: def signed? 460: return (@tsigstate == :Signed || 461: @tsigstate == :Verified || 462: @tsigstate == :Failed) 463: end
# 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.
# 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