Class: Net::DNS::Packet

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

Overview

Name

Net::DNS::Packet - DNS packet object class

Synopsis

require 'net/dns/packet'

Description

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.

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

Error classes

Some error classes has been defined for the Net::DNS::Packet class, which are listed here to keep a light and browsable main documentation. We have:

  • PacketArgumentError: Generic argument error for class Net::DNS::Packet

  • PacketError: Generic Packet error

Copyright

Copyright © 2006 Marco Ceresa

All rights reserved. This program is free software; you may redistribute it and/or modify it under the same terms as Ruby itself.

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

Create a new instance of Net::DNS::Packet class. Arguments are the canonical name of the resourse, 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.new_from_data instead.



126
127
128
129
130
131
132
133
134
# File 'lib/net/dns/packet.rb', line 126

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.



111
112
113
# File 'lib/net/dns/packet.rb', line 111

def additional
  @additional
end

#answerObject

Returns the value of attribute answer.



111
112
113
# File 'lib/net/dns/packet.rb', line 111

def answer
  @answer
end

#answerfromObject (readonly)

Returns the value of attribute answerfrom.



112
113
114
# File 'lib/net/dns/packet.rb', line 112

def answerfrom
  @answerfrom
end

#answersizeObject (readonly)

Returns the value of attribute answersize.



112
113
114
# File 'lib/net/dns/packet.rb', line 112

def answersize
  @answersize
end

#authorityObject

Returns the value of attribute authority.



111
112
113
# File 'lib/net/dns/packet.rb', line 111

def authority
  @authority
end

#headerObject

Returns the value of attribute header.



111
112
113
# File 'lib/net/dns/packet.rb', line 111

def header
  @header
end

#questionObject

Returns the value of attribute question.



111
112
113
# File 'lib/net/dns/packet.rb', line 111

def question
  @question
end

Class Method Details

.parse(*args) ⇒ Object

Create a new instance of Net::DNS::Packet class from binary data, taken out by 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, expecially when using RAW sockets.



150
151
152
153
154
# File 'lib/net/dns/packet.rb', line 150

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

Instance Method Details

#dataObject

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


168
169
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
# File 'lib/net/dns/packet.rb', line 168

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"


205
206
207
208
209
210
211
212
213
214
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
# File 'lib/net/dns/packet.rb', line 205

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

Iterate for every address in the answer section of a Net::DNS::Packet object.

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

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



397
398
399
400
401
402
# File 'lib/net/dns/packet.rb', line 397

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

#each_cnameObject

Iterate for every canonical name in the answer section of a Net::DNS::Packet object.

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


439
440
441
442
443
444
# File 'lib/net/dns/packet.rb', line 439

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

#each_mxObject

Iterate for every exchange record in the answer section of a Net::DNS::Packet object.

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


425
426
427
428
429
430
# File 'lib/net/dns/packet.rb', line 425

def each_mx
  @answer.each do |elem|
    next unless elem.class == Net::DNS::RR::MX
    yield elem.preference,elem.exchange
  end
end

#each_nameserverObject

Iterate for every nameserver in the answer section of a Net::DNS::Packet object.

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


411
412
413
414
415
416
# File 'lib/net/dns/packet.rb', line 411

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

#each_ptrObject

Iterate for every pointer in the answer section of a Net::DNS::Packet object.

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


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

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

#inspectObject

Inspect method



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/net/dns/packet.rb', line 249

def inspect
  retval = ""
  if @answerfrom != "0.0.0.0:0" and @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.size == 0
    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.size == 0
    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.size == 0
    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

Chacks whether a query has returned a NXDOMAIN error, meaning the domain name queried doesn’t exists.

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


481
482
483
# File 'lib/net/dns/packet.rb', line 481

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

#query?Boolean

Checks if the packet is a QUERY packet

Returns:

  • (Boolean)


158
159
160
# File 'lib/net/dns/packet.rb', line 158

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


467
468
469
# File 'lib/net/dns/packet.rb', line 467

def size
  data.size
end

#truncated?Boolean

Wrapper to Header#truncated?

Returns:

  • (Boolean)


297
298
299
# File 'lib/net/dns/packet.rb', line 297

def truncated?
  @header.truncated?
end