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

Logger can be set by using logger= to set the logger to any object that implements the necessary functions. If no logger is set then no logging is performed.

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

Defined Under Namespace

Classes: Error, PacketError

Constant Summary collapse

@@logger =
nil

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 = nil, 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. If the arguments are omitted, no question is added to the new packet; type and class default to A and IN if a name is given.

packet = Net::DNS::Packet.new
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.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/net/dns/packet.rb', line 100

def initialize(name = nil, type = Net::DNS::A, cls = Net::DNS::IN)
  default_qdcount = 0
  @question = []

  if not name.nil?
    default_qdcount = 1
    @question = [Net::DNS::Question.new(name, type, cls)]
  end

  @header = Net::DNS::Header.new(:qdCount => default_qdcount)
  @answer = []
  @authority = []
  @additional = []
end

Instance Attribute Details

#additionalObject

Returns the value of attribute additional.



84
85
86
# File 'lib/net/dns/packet.rb', line 84

def additional
  @additional
end

#answerObject

Returns the value of attribute answer.



84
85
86
# File 'lib/net/dns/packet.rb', line 84

def answer
  @answer
end

#answerfromObject (readonly)

Returns the value of attribute answerfrom.



85
86
87
# File 'lib/net/dns/packet.rb', line 85

def answerfrom
  @answerfrom
end

#answersizeObject (readonly)

Returns the value of attribute answersize.



85
86
87
# File 'lib/net/dns/packet.rb', line 85

def answersize
  @answersize
end

#authorityObject

Returns the value of attribute authority.



84
85
86
# File 'lib/net/dns/packet.rb', line 84

def authority
  @authority
end

#headerObject

Returns the value of attribute header.



84
85
86
# File 'lib/net/dns/packet.rb', line 84

def header
  @header
end

#questionObject

Returns the value of attribute question.



84
85
86
# File 'lib/net/dns/packet.rb', line 84

def question
  @question
end

Class Method Details

.logger=(logger) ⇒ Object



120
121
122
123
124
125
126
# File 'lib/net/dns/packet.rb', line 120

def self.logger= logger
  if logger.respond_to?(:warn) && logger.respond_to?(:debug) && logger.respond_to?(:info)
    @@logger = logger
  else
    raise ArgumentError, "Invalid logger provided to #{self.class}"
  end
end

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



469
470
471
472
473
# File 'lib/net/dns/packet.rb', line 469

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"


152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/net/dns/packet.rb', line 152

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"


189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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
# File 'lib/net/dns/packet.rb', line 189

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

#debug(*args) ⇒ Object



134
135
136
137
138
# File 'lib/net/dns/packet.rb', line 134

def debug *args
  if @@logger
    @@logger.debug *args
  end
end

#each_address(&block) ⇒ Object

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.



381
382
383
# File 'lib/net/dns/packet.rb', line 381

def each_address(&block)
  elements(Net::DNS::RR::A).map(&:address).each(&block)
end

#each_cname(&block) ⇒ Object

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

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


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

def each_cname(&block)
  elements(Net::DNS::RR::CNAME).map(&:cname).each(&block)
end

#each_mx(&block) ⇒ Object

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


403
404
405
# File 'lib/net/dns/packet.rb', line 403

def each_mx(&block)
  elements(Net::DNS::RR::MX).map{|elem| [elem.preference, elem.exchange]}.each(&block)
end

#each_nameserver(&block) ⇒ Object

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

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


392
393
394
# File 'lib/net/dns/packet.rb', line 392

def each_nameserver(&block)
  elements(Net::DNS::RR::NS).map(&:nsdname).each(&block)
end

#each_ptr(&block) ⇒ Object

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

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


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

def each_ptr(&block)
  elements(Net::DNS::RR::PTR).map(&:ptrdname).each(&block)
end

#elements(type = nil) ⇒ Object

Filters the elements in the answer section based on the class given



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

def elements(type = nil)
  if type
    @answer.select {|elem| elem.kind_of? type}
  else
    @answer
  end
end

#info(*args) ⇒ Object



140
141
142
143
144
# File 'lib/net/dns/packet.rb', line 140

def info *args
  if @@logger
    @@logger.info *args
  end
end

#inspectObject Also known as: to_s

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



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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/net/dns/packet.rb', line 234

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

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)


450
451
452
# File 'lib/net/dns/packet.rb', line 450

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

#query?Boolean

Checks if the packet is a QUERY packet

Returns:

  • (Boolean)


116
117
118
# File 'lib/net/dns/packet.rb', line 116

def query?
  @header.query?
end

#sizeObject

Returns the packet size in bytes.

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


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

def size
  data.size
end

#truncated?Boolean

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

Returns:

  • (Boolean)


281
282
283
# File 'lib/net/dns/packet.rb', line 281

def truncated?
  @header.truncated?
end

#warn(*args) ⇒ Object



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

def warn *args
  if @@logger
    @@logger.warn *args
  end
end