Class: Net::DNS::Packet

Inherits:
Object
  • Object
show all
Includes:
Names
Defined in:
lib/net/dns/packet.rb

Overview

Net::DNS::Packet

The Net::DNS::Packet class represents an entire DNS packet, divided in his main section:

  • Header (instance of Net::DNS::Header)

  • Question (array of Net::DNS::Question objects)

  • Answer, Authority, Additional (each formed by an array of Net::DNS::RR objects)

You can use this class whenever you need to create a DNS packet, whether in an user application, in a resolver instance (have a look, for instance, at the Net::DNS::Resolver#send method) or for a nameserver.

For example:

# Create a packet
packet = Net::DNS::Packet.new("www.example.com")
mx = Net::DNS::Packet.new("example.com", Net::DNS::MX)

# Getting packet binary data, suitable for network transmission
data = packet.data

A packet object can be created from binary data too, like an answer packet just received from a network stream:

packet = Net::DNS::Packet::parse(data)

Each part of a packet can be gotten by the right accessors:

header = packet.header     # Instance of Net::DNS::Header class
question = packet.question # Instance of Net::DNS::Question class

# Iterate over additional RRs
packet.additional.each do |rr|
  puts "Got an #{rr.type} record"
end

Some iterators have been written to easy the access of those RRs, which are often the most important. So instead of doing:

packet.answer.each do |rr|
  if rr.type == Net::DNS::RR::Types::A
    # do something with +rr.address+
  end
end

we can do:

packet.each_address do |ip|
  # do something with +ip+
end

Be sure you don’t miss all the iterators in the class documentation.

Logging facility

As Net::DNS::Resolver class, Net::DNS::Packet class has its own logging facility too. It work in the same way the other one do, so you can maybe want to override it or change the file descriptor.

packet = Net::DNS::Packet.new("www.example.com")
packet.logger = $stderr

# or even
packet.logger = Logger.new("/tmp/packet.log")

If the Net::DNS::Packet class is directly instantiated by the Net::DNS::Resolver class, like the great majority of the time, it will use the same logger facility.

Logger level will be set to Logger::Debug if $DEBUG variable is set.

Defined Under Namespace

Classes: Error, PacketError

Constant Summary

Constants included from Names

Names::INT16SZ

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Names

#dn_comp, #dn_expand, #names_array, #pack_name, #valid?

Constructor Details

#initialize(name, type = Net::DNS::A, cls = Net::DNS::IN) ⇒ Packet

Creates a new instance of Net::DNS::Packet class. Arguments are the canonical name of the resource, an optional type field and an optional class field. The record type and class can be omitted; they default to A and IN.

packet = Net::DNS::Packet.new("www.example.com")
packet = Net::DNS::Packet.new("example.com", Net::DNS::MX)
packet = Net::DNS::Packet.new("example.com", Net::DNS::TXT, Net::DNS::CH)

This class no longer instantiate object from binary data coming from network streams. Please use Net::DNS::Packet.parse instead.



112
113
114
115
116
117
118
119
120
# File 'lib/net/dns/packet.rb', line 112

def initialize(name, type = Net::DNS::A, cls = Net::DNS::IN)
  @header = Net::DNS::Header.new(qdCount: 1)
  @question = [Net::DNS::Question.new(name, type, cls)]
  @answer = []
  @authority = []
  @additional = []
  @logger = Logger.new $stdout
  @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
end

Instance Attribute Details

#additionalObject

Returns the value of attribute additional.



97
98
99
# File 'lib/net/dns/packet.rb', line 97

def additional
  @additional
end

#answerObject

Returns the value of attribute answer.



95
96
97
# File 'lib/net/dns/packet.rb', line 95

def answer
  @answer
end

#answerfromObject (readonly)

Returns the value of attribute answerfrom.



98
99
100
# File 'lib/net/dns/packet.rb', line 98

def answerfrom
  @answerfrom
end

#answersizeObject (readonly)

Returns the value of attribute answersize.



99
100
101
# File 'lib/net/dns/packet.rb', line 99

def answersize
  @answersize
end

#authorityObject

Returns the value of attribute authority.



96
97
98
# File 'lib/net/dns/packet.rb', line 96

def authority
  @authority
end

#headerObject

Returns the value of attribute header.



93
94
95
# File 'lib/net/dns/packet.rb', line 93

def header
  @header
end

#questionObject

Returns the value of attribute question.



94
95
96
# File 'lib/net/dns/packet.rb', line 94

def question
  @question
end

Class Method Details

.parse(*args) ⇒ Object

Creates a new instance of Net::DNS::Packet class from binary data, taken out from a network stream. For example:

# udp_socket is an UDPSocket waiting for a response
ans = udp_socket.recvfrom(1500)
packet = Net::DNS::Packet::parse(ans)

An optional from argument can be used to specify the information of the sender. If data is passed as is from a Socket#recvfrom call, the method will accept it.

Be sure that your network data is clean from any UDP/TCP header, especially when using RAW sockets.



454
455
456
457
458
# File 'lib/net/dns/packet.rb', line 454

def self.parse(*args)
  o = allocate
  o.send(:new_from_data, *args)
  o
end

Instance Method Details

#dataObject

Returns the packet object in binary data, suitable for sending across a network stream.

packet_data = packet.data
puts "Packet is #{packet_data.size} bytes long"


133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/net/dns/packet.rb', line 133

def data
  qdcount = ancount = nscount = arcount = 0
  data = @header.data
  headerlength = data.length

  @question.each do |question|
    data += question.data
    qdcount += 1
  end
  @answer.each do |rr|
    data += rr.data # (data.length)
    ancount += 1
  end
  @authority.each do |rr|
    data += rr.data # (data.length)
    nscount += 1
  end
  @additional.each do |rr|
    data += rr.data # (data.length)
    arcount += 1
  end

  @header.qdCount = qdcount
  @header.anCount = ancount
  @header.nsCount = nscount
  @header.arCount = arcount

  @header.data + data[Net::DNS::HFIXEDSZ..data.size]
end

#data_compObject

Same as Net::DNS::Packet#data, but implements name compression (see RFC1025) for a considerable save of bytes.

packet = Net::DNS::Packet.new("www.example.com")
puts "Size normal is #{packet.data.size} bytes"
puts "Size compressed is #{packet.data_comp.size} bytes"


170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/net/dns/packet.rb', line 170

def data_comp
  offset = 0
  compnames = {}
  qdcount = ancount = nscount = arcount = 0
  data = @header.data
  headerlength = data.length

  @question.each do |question|
    str, offset, names = question.data
    data += str
    compnames.update(names)
    qdcount += 1
  end

  @answer.each do |rr|
    str, offset, names = rr.data(offset, compnames)
    data += str
    compnames.update(names)
    ancount += 1
  end

  @authority.each do |rr|
    str, offset, names = rr.data(offset, compnames)
    data += str
    compnames.update(names)
    nscount += 1
  end

  @additional.each do |rr|
    str, offset, names = rr.data(offset, compnames)
    data += str
    compnames.update(names)
    arcount += 1
  end

  @header.qdCount = qdcount
  @header.anCount = ancount
  @header.nsCount = nscount
  @header.arCount = arcount

  @header.data + data[Net::DNS::HFIXEDSZ..data.size]
end

#each_addressObject

Iterates every address in the answer section of this Net::DNS::Packet instance.

packet.each_address do |ip|
  ping ip.to_s
end

As you can see in the documentation for the Net::DNS::RR::A class, the address returned is an instance of IPAddr class.



347
348
349
350
351
352
353
# File 'lib/net/dns/packet.rb', line 347

def each_address
  @answer.each do |elem|
    next unless elem.class == Net::DNS::RR::A

    yield elem.address
  end
end

#each_cnameObject

Iterates every canonical name in the answer section of this Net::DNS::Packet instance.

packet.each_cname do |cname|
  puts "Canonical name: #{cname}"
end


392
393
394
395
396
397
398
# File 'lib/net/dns/packet.rb', line 392

def each_cname
  @answer.each do |elem|
    next unless elem.class == Net::DNS::RR::CNAME

    yield elem.cname
  end
end

#each_mxObject

Iterates every exchange record in the answer section of this Net::DNS::Packet instance.

packet.each_mx do |pref,name|
  puts "Mail exchange #{name} has preference #{pref}"
end


377
378
379
380
381
382
383
# File 'lib/net/dns/packet.rb', line 377

def each_mx
  @answer.each do |elem|
    next unless elem.class == Net::DNS::RR::MX

    yield elem.preference, elem.exchange
  end
end

#each_nameserverObject

Iterates every nameserver in the answer section of this Net::DNS::Packet instance.

packet.each_nameserver do |ns|
  puts "Nameserver found: #{ns}"
end


362
363
364
365
366
367
368
# File 'lib/net/dns/packet.rb', line 362

def each_nameserver
  @answer.each do |elem|
    next unless elem.class == Net::DNS::RR::NS

    yield elem.nsdname
  end
end

#each_ptrObject

Iterates every pointer in the answer section of this Net::DNS::Packet instance.

packet.each_ptr do |ptr|
  puts "Pointer for resource: #{ptr}"
end


407
408
409
410
411
412
413
# File 'lib/net/dns/packet.rb', line 407

def each_ptr
  @answer.each do |elem|
    next unless elem.class == Net::DNS::RR::PTR

    yield elem.ptrdname
  end
end

#inspectObject Also known as: to_s

Returns a string containing a human-readable representation of this Net::DNS::Packet instance.



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/net/dns/packet.rb', line 215

def inspect
  retval = ""
  if (@answerfrom != "0.0.0.0:0") && @answerfrom
    retval += ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n"
  end

  retval += ";; HEADER SECTION\n"
  retval += @header.inspect

  retval += "\n"
  section = @header.opCode == "UPDATE" ? "ZONE" : "QUESTION"
  retval += ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '' : 's'}):\n"
  @question.each do |qr|
    retval += ";; " + qr.inspect + "\n"
  end

  unless @answer.empty?
    retval += "\n"
    section = @header.opCode == "UPDATE" ? "PREREQUISITE" : "ANSWER"
    retval += ";; #{section} SECTION (#{@header.anCount} record#{@header.anCount == 1 ? '' : 's'}):\n"
    @answer.each do |rr|
      retval += rr.inspect + "\n"
    end
  end

  unless @authority.empty?
    retval += "\n"
    section = @header.opCode == "UPDATE" ? "UPDATE" : "AUTHORITY"
    retval += ";; #{section} SECTION (#{@header.nsCount} record#{@header.nsCount == 1 ? '' : 's'}):\n"
    @authority.each do |rr|
      retval += rr.inspect + "\n"
    end
  end

  unless @additional.empty?
    retval += "\n"
    retval += ";; ADDITIONAL SECTION (#{@header.arCount} record#{@header.arCount == 1 ? '' : 's'}):\n"
    @additional.each do |rr|
      retval += rr.inspect + "\n"
    end
  end

  retval
end

#nxdomain?Boolean

Checks whether the query returned a NXDOMAIN error, meaning the queried domain name doesn’t exist.

%w[a.com google.com ibm.com d.com].each do |domain|
  response = Net::DNS::Resolver.new.send(domain)
  puts "#{domain} doesn't exist" if response.nxdomain?
end
# => a.com doesn't exist
# => d.com doesn't exist

Returns:

  • (Boolean)


436
437
438
# File 'lib/net/dns/packet.rb', line 436

def nxdomain?
  header.rCode.code == Net::DNS::Header::RCode::NAME
end

#query?Boolean

Checks if the packet is a QUERY packet

Returns:

  • (Boolean)


123
124
125
# File 'lib/net/dns/packet.rb', line 123

def query?
  @header.opCode == Net::DNS::Header::QUERY
end

#sizeObject

Returns the packet size in bytes.

Resolver("www.google.com") do |packet|
  puts packet.size + " bytes"}
end
# => 484 bytes


422
423
424
# File 'lib/net/dns/packet.rb', line 422

def size
  data.size
end

#truncated?Boolean

Delegates to Net::DNS::Header#truncated?.

Returns:

  • (Boolean)


262
263
264
# File 'lib/net/dns/packet.rb', line 262

def truncated?
  @header.truncated?
end