| 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