class PacketGen::Header::DNS

DNS: Domain Name Service

A DNS packet consists of a header:

                                1  1  1  1  1  1
  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      ID                       |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    QDCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ANCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    NSCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ARCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

A DNS packet also contains up to 4 sections:

Create a DNS header

# standalone
dns = PacketGen::Header::DNS.new
# in a IP packet
pkt = PacketGen.gen('IP').add('DNS')
# access to DNS header
pkt.dns   # => PacketGen::Header::DNS

DNS attributes

dns.id = 0x1234
dns.qr = false
# opcode may be set as an Integer (all values are possible)
# or as a String (only keys from PacketGen::Header::DNS::OPCODES)
dns.opcode = 0xe       # set as integer, value not defined in standard
dns.opcode = 'query'   # set as string
dns.aa = dns.tc = dns.rd = dns.ra = false
# rcode may be set as an Integer (all values are possible)
# or as a String (only keys from PacketGen::Header::DNS::RCODES)
dns.rcode = 11
dns.rcode = 'refused'
dns.qdcount = 123
dns.ancount = 0x1234
dns.nscount = 1
dns.arcount = 0

One can also access to DNS sections:

dns.qd   # => PacketGen::Header::DNS::QDSection
dns.an   # => PacketGen::Header::DNS::RRSection
dns.ns   # => PacketGen::Header::DNS::RRSection
dns.ar   # => PacketGen::Header::DNS::RRSection

Add a question to DNS question section

Adding a {Question} with {QDSection#<<} automagically increments {#qdcount}. To not modify qdcount, use {QDSection#push}.

# add a question about example.net IP address. Increment qdcount
dns.qd << PacketGen::Header::DNS::Question.new(dns, name: 'example.net')
# or
dns.qd << { rtype: 'Question', name: 'example.net' }
# add a question about example.net IPv6 address. Dot not modify qdcount
dns.qd.push PacketGen::Header::DNS::Question.new(dns, name: 'example.net', type: 'AAAA')
# or
dns.qd.push({ rtype: 'Question', name: 'example.net', type: 'AAAA' })

Add a ressource record to a DNS section

Adding a {RR} with {RRSection#<< RRSection#<<} automagically increments section counter. To not modify it, use {RRSection#push RRSection#push}

# add a RR to answer section. Increment ancount
dns.an << PacketGen::Header::DNS::RR.new(dns, name: 'example.net', rdata: IPAddr.new('1.2.3.4').hton)
# or
dns.an << { rtype: 'RR', name: 'example.net', rdata: IPAddr.new('1.2.3.4').hton }
# add a RR to NS section. Dot not modify nscount
rdata = PacketGen::Header::DNS::Name.new(dns).parse('dns.net')
dns.ns.push PacketGen::Header::DNS::RR.new(dns, name: 'example.net', type: 'NS', rdata: rdata)
# or
dns.ns.push(rtype: 'RR', name: 'example.net', type: 'NS', rdata: rdata)

Extended DNS EDNS(0)

# Add an OPT to ar section
dns.ar << PacketGen::Header::DNS::OPT.new(dns, udp_size: 4096, ext_rcode: 43)
# or
dns.ar << { rtype: 'OPT', udp_size: 4096, ext_rcode: 43 }
# add an option to OPT record
dns.ar.last.options << PacketGen::Header::DNS::Option.new(code: 48, data: '12')
# or
dns.ar.last.options << { code: 48, data: '12' }

@author Sylvain Daubert @since 1.3.0

Constants

OPCODES

DNS opcodes

Option

@!parse

# DNS option is a TLV object:
# * {#code} is a {Types::Int16},
# * {#length #length} is a {Types::Int16},
# * {#data} is a {Types::String}.
#
# @since 1.3.0
# @since 3.1.0 defined with {Types::AbstractTLV}
# @!parse class Option < Types::AbstractTLV; end
# @!attribute code
#   Alias for {#type}
#   @return [Integer]
# @!attribute data
#   Alias for {#value}
#   @return [Types::String]
class Option < Types::AbstractTLV; end

@private

RCODES

DNS Response codes

TCP_PORT

Port number for DNS over TCP

UDP_PORT

Port number for DNS over UDP

Public Instance Methods

inspect() click to toggle source

@return [String]

Calls superclass method
# File lib/packetgen/header/dns.rb, line 240
def inspect
  super do |attr|
    next unless attr == :u16

    flags = %i[qr aa tc rd ra].select! { |flag| send "#{flag}?" }
                              .map(&:to_s).join(',')
    str = Inspect.shift_level
    str << Inspect::FMT_ATTR % ['Flags', 'flags', flags]
    opcode = '%-16s (%u)' % [OPCODES.key(self.opcode), self.opcode]
    str << Inspect.shift_level
    str << Inspect::FMT_ATTR % ['Integer', 'opcode', opcode]
    rcode = '%-16s (%u)' % [RCODES.key(self.rcode), self.rcode]
    str << Inspect.shift_level
    str << Inspect::FMT_ATTR % ['Integer', 'rcode', rcode]
  end
end
opcode=(value) click to toggle source

Set opcode @param [Integer,String] value @return [Integer]

# File lib/packetgen/header/dns.rb, line 198
def opcode=(value)
  intg = case value
         when Integer
           value
         else
           OPCODES[value.to_s]
         end
  raise ArgumentError, "unknown opcode #{value.inspect}" unless intg

  self.u16 &= 0x87ff
  self.u16 |= (intg & 0xf) << 11
end
query?() click to toggle source

Is message a query @return [Boolean]

# File lib/packetgen/header/dns.rb, line 235
def query?
  !qr?
end
rcode=(value) click to toggle source

Set rcode @param [Integer,String] value @return [Integer]

# File lib/packetgen/header/dns.rb, line 214
def rcode=(value)
  intg = case value
         when Integer
           value
         else
           RCODES[value]
         end
  raise ArgumentError, "unknown rcode #{value.inspect}" unless intg

  self.u16 &= 0xfff0
  self.u16 |= intg & 0xf
end
response?() click to toggle source

Is message a response @return [Boolean]

# File lib/packetgen/header/dns.rb, line 229
def response?
  qr?
end