class Dnsruby::Message

Defines a DNS packet.

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

Sections

Message objects have five sections:

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.

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.

pre[R]

The answer section, an array of Dnsruby::RR objects.

prerequisite[R]

The answer section, an array of Dnsruby::RR objects.

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

update[R]

The authority section, an array of Dnsruby::RR objects.

zone[R]

The question section, an array of Dnsruby::Question objects.

Public Class Methods

decode(m) click to toggle source

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
new(*args) click to toggle source

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
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

Public Instance Methods

==(other) click to toggle source
# 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_question(question, type=Types.A, klass=Classes.IN) click to toggle source

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)

# 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
Also aliased as: add_zone
add_zone(question, type=Types.A, klass=Classes.IN)
Alias for: add_question
each_additional() { |rec| ... } click to toggle source
# File lib/Dnsruby/message.rb, line 411
def each_additional
  @additional.each {|rec|
    yield rec
  }
end
each_answer() { |rec| ... } click to toggle source
# File lib/Dnsruby/message.rb, line 385
def each_answer
  @answer.each {|rec|
    yield rec
  }
end
Also aliased as: each_pre
each_authority() { |rec| ... } click to toggle source
# File lib/Dnsruby/message.rb, line 398
def each_authority
  @authority.each {|rec|
    yield rec
  }
end
Also aliased as: each_update
each_pre()
Also aliased as: each_prerequisite
Alias for: each_answer
each_prerequisite()
Alias for: each_pre
each_question() { |rec| ... } click to toggle source
# File lib/Dnsruby/message.rb, line 364
def each_question
  @question.each {|rec|
    yield rec
  }
end
Also aliased as: each_zone
each_resource() { |rec| ... } click to toggle source

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
each_section() { |section| ... } click to toggle source

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
each_update()
Alias for: each_authority
each_zone()
Alias for: each_question
encode() click to toggle source

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
get_exception() click to toggle source
# 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
get_opt() click to toggle source
# 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
rcode() click to toggle source
# 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
rrset(name, type, klass = Classes::IN) click to toggle source

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
rrsets(type, klass=Classes::IN) click to toggle source

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
section_rrsets(type = nil, include_opt = false) click to toggle source

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
set_tsig(*args) click to toggle source

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
signed?() click to toggle source

Was this message signed by a TSIG?

# File lib/Dnsruby/message.rb, line 459
def signed?
  return (@tsigstate == :Signed ||
      @tsigstate == :Verified ||
      @tsigstate == :Failed)
end
to_s() click to toggle source
# 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
tsig() click to toggle source

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
verified?() click to toggle source

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