net-dns2 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +20 -0
  4. data/CHANGELOG.md +105 -0
  5. data/Gemfile +4 -0
  6. data/README.md +155 -0
  7. data/Rakefile +94 -0
  8. data/THANKS.rdoc +24 -0
  9. data/demo/check_soa.rb +115 -0
  10. data/demo/threads.rb +22 -0
  11. data/lib/net/dns.rb +112 -0
  12. data/lib/net/dns/core_ext.rb +52 -0
  13. data/lib/net/dns/header.rb +713 -0
  14. data/lib/net/dns/names.rb +118 -0
  15. data/lib/net/dns/packet.rb +563 -0
  16. data/lib/net/dns/question.rb +188 -0
  17. data/lib/net/dns/resolver.rb +1313 -0
  18. data/lib/net/dns/resolver/socks.rb +154 -0
  19. data/lib/net/dns/resolver/timeouts.rb +75 -0
  20. data/lib/net/dns/rr.rb +366 -0
  21. data/lib/net/dns/rr/a.rb +124 -0
  22. data/lib/net/dns/rr/aaaa.rb +103 -0
  23. data/lib/net/dns/rr/classes.rb +133 -0
  24. data/lib/net/dns/rr/cname.rb +82 -0
  25. data/lib/net/dns/rr/hinfo.rb +108 -0
  26. data/lib/net/dns/rr/mr.rb +79 -0
  27. data/lib/net/dns/rr/mx.rb +92 -0
  28. data/lib/net/dns/rr/ns.rb +78 -0
  29. data/lib/net/dns/rr/null.rb +53 -0
  30. data/lib/net/dns/rr/ptr.rb +83 -0
  31. data/lib/net/dns/rr/soa.rb +78 -0
  32. data/lib/net/dns/rr/srv.rb +45 -0
  33. data/lib/net/dns/rr/txt.rb +61 -0
  34. data/lib/net/dns/rr/types.rb +193 -0
  35. data/lib/net/dns/version.rb +16 -0
  36. data/net-dns.gemspec +37 -0
  37. data/test/header_test.rb +167 -0
  38. data/test/names_test.rb +21 -0
  39. data/test/packet_test.rb +49 -0
  40. data/test/question_test.rb +83 -0
  41. data/test/resolver_test.rb +117 -0
  42. data/test/rr/a_test.rb +113 -0
  43. data/test/rr/aaaa_test.rb +109 -0
  44. data/test/rr/classes_test.rb +85 -0
  45. data/test/rr/cname_test.rb +97 -0
  46. data/test/rr/hinfo_test.rb +117 -0
  47. data/test/rr/mr_test.rb +105 -0
  48. data/test/rr/mx_test.rb +112 -0
  49. data/test/rr/ns_test.rb +86 -0
  50. data/test/rr/types_test.rb +69 -0
  51. data/test/rr_test.rb +131 -0
  52. data/test/test_helper.rb +4 -0
  53. metadata +158 -0
@@ -0,0 +1,118 @@
1
+ module Net # :nodoc:
2
+ module DNS
3
+
4
+ module Names
5
+
6
+ # Base error class.
7
+ class Error < StandardError
8
+ end
9
+
10
+ # Generic Names Error.
11
+ class ExpandError < Error
12
+ end
13
+
14
+
15
+ INT16SZ = 2
16
+
17
+ # Expand a compressed name in a DNS Packet object. Please
18
+ # see RFC1025 for an explanation of how the compression
19
+ # in DNS packets works, how may it be useful and how should
20
+ # be handled.
21
+ #
22
+ # This method accept two parameters: a raw packet data and an
23
+ # offset, which indicates the point in the packet in which the
24
+ # parsing has arrived.
25
+ #
26
+ def dn_expand(packet,offset)
27
+ name = ""
28
+ packetlen = packet.size
29
+ while true
30
+ raise ExpandError, "Offset is greater than packet lenght!" if packetlen < (offset+1)
31
+ len = packet.unpack("@#{offset} C")[0]
32
+
33
+ if len == 0
34
+ offset += 1
35
+ break
36
+ elsif (len & 0xC0) == 0xC0
37
+ raise ExpandError, "Packet ended before offset expand" if packetlen < (offset+INT16SZ)
38
+ ptr = packet.unpack("@#{offset} n")[0]
39
+ ptr &= 0x3FFF
40
+ name2 = dn_expand(packet,ptr)[0]
41
+ raise ExpandError, "Packet is malformed!" if name2 == nil
42
+ name += name2
43
+ offset += INT16SZ
44
+ break
45
+ else
46
+ offset += 1
47
+ raise ExpandError, "No expansion found" if packetlen < (offset+len)
48
+ elem = packet[offset..offset+len-1]
49
+ name += "#{elem}."
50
+ offset += len
51
+ end
52
+ end
53
+ [name, offset] # name.chomp(".") if trailing dot has to be omitted
54
+ end
55
+
56
+ def pack_name(name)
57
+ if name.size > 255
58
+ raise ArgumentError, "Name may not exceed 255 chars"
59
+ end
60
+ arr = name.split(".")
61
+ str = ""
62
+ arr.each do |elem|
63
+ if elem.size > 63
64
+ raise ArgumentError, "Label may not exceed 63 chars"
65
+ end
66
+ str += [elem.size,elem].pack("Ca*")
67
+ end
68
+ str += [0].pack("C")
69
+ str
70
+ end
71
+
72
+ def names_array(name)
73
+ arr = name.split(".")
74
+ ar = []
75
+ string = ""
76
+ arr.size.times do |i|
77
+ x = i+1
78
+ elem = arr[-x]
79
+ len = elem.size
80
+ string = ((string.reverse)+([len,elem].pack("Ca*")).reverse).reverse
81
+ ar.unshift(string)
82
+ end
83
+ return ar
84
+ end
85
+
86
+ def dn_comp(name,offset,compnames)
87
+ names = {}
88
+ ptr = 0
89
+ str = ""
90
+ arr = names_array(name)
91
+ arr.each do |entry|
92
+ if compnames.has_key?(entry)
93
+ ptr = 0xC000 | compnames[entry]
94
+ str += [ptr].pack("n")
95
+ offset += INT16SZ
96
+ break
97
+ else
98
+ len = entry.unpack("C")[0]
99
+ elem = entry[1..len]
100
+ str += [len,elem].pack("Ca*")
101
+ names.update({"#{entry}" => offset})
102
+ offset += len
103
+ end
104
+ end
105
+ return str,offset,names
106
+ end
107
+
108
+ def valid?(name)
109
+ if name =~ /^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+((a[cdefgilmnoqrstuwxz]|aero|arpa)|(b[abdefghijmnorstvwyz]|biz)|(c[acdfghiklmnorsuvxyz]|cat|com|coop)|d[ejkmoz]|(e[ceghrstu]|edu)|f[ijkmor]|(g[abdefghilmnpqrstuwy]|gov)|h[kmnrtu]|(i[delmnoqrst]|info|int)|(j[emop]|jobs)|k[eghimnprwyz]|l[abcikrstuvy]|(m[acdghklmnopqrstuvwxyz]|mil|mobi|museum)|(n[acefgilopruz]|name|net)|(om|org)|(p[aefghklmnrstwy]|pro)|qa|r[eouw]|s[abcdeghijklmnortvyz]|(t[cdfghjklmnoprtvwz]|travel)|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw])$/i
110
+ return name
111
+ else
112
+ raise ArgumentError, "Invalid FQDN: #{name}"
113
+ end
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,563 @@
1
+ require 'logger'
2
+ require 'net/dns/names'
3
+ require 'net/dns/header'
4
+ require 'net/dns/question'
5
+ require 'net/dns/rr'
6
+
7
+ module Net
8
+ module DNS
9
+
10
+ #
11
+ # = Net::DNS::Packet
12
+ #
13
+ # The Net::DNS::Packet class represents an entire DNS packet,
14
+ # divided in his main section:
15
+ #
16
+ # * Header (instance of Net::DNS::Header)
17
+ # * Question (array of Net::DNS::Question objects)
18
+ # * Answer, Authority, Additional (each formed by an array of Net::DNS::RR
19
+ # objects)
20
+ #
21
+ # You can use this class whenever you need to create a DNS packet, whether
22
+ # in an user application, in a resolver instance (have a look, for instance,
23
+ # at the <tt>Net::DNS::Resolver#send</tt> method) or for a nameserver.
24
+ #
25
+ # For example:
26
+ #
27
+ # # Create a packet
28
+ # packet = Net::DNS::Packet.new("www.example.com")
29
+ # mx = Net::DNS::Packet.new("example.com", Net::DNS::MX)
30
+ #
31
+ # # Getting packet binary data, suitable for network transmission
32
+ # data = packet.data
33
+ #
34
+ # A packet object can be created from binary data too, like an
35
+ # answer packet just received from a network stream:
36
+ #
37
+ # packet = Net::DNS::Packet::parse(data)
38
+ #
39
+ # Each part of a packet can be gotten by the right accessors:
40
+ #
41
+ # header = packet.header # Instance of Net::DNS::Header class
42
+ # question = packet.question # Instance of Net::DNS::Question class
43
+ #
44
+ # # Iterate over additional RRs
45
+ # packet.additional.each do |rr|
46
+ # puts "Got an #{rr.type} record"
47
+ # end
48
+ #
49
+ # Some iterators have been written to easy the access of those RRs,
50
+ # which are often the most important. So instead of doing:
51
+ #
52
+ # packet.answer.each do |rr|
53
+ # if rr.type == Net::DNS::RR::Types::A
54
+ # # do something with +rr.address+
55
+ # end
56
+ # end
57
+ #
58
+ # we can do:
59
+ #
60
+ # packet.each_address do |ip|
61
+ # # do something with +ip+
62
+ # end
63
+ #
64
+ # Be sure you don't miss all the iterators in the class documentation.
65
+ #
66
+ # == Logging facility
67
+ #
68
+ # As Net::DNS::Resolver class, Net::DNS::Packet class has its own logging
69
+ # facility too. It work in the same way the other one do, so you can
70
+ # maybe want to override it or change the file descriptor.
71
+ #
72
+ # packet = Net::DNS::Packet.new("www.example.com")
73
+ # packet.logger = $stderr
74
+ #
75
+ # # or even
76
+ # packet.logger = Logger.new("/tmp/packet.log")
77
+ #
78
+ # If the <tt>Net::DNS::Packet</tt> class is directly instantiated by the <tt>Net::DNS::Resolver</tt>
79
+ # class, like the great majority of the time, it will use the same logger facility.
80
+ #
81
+ # Logger level will be set to <tt>Logger::Debug</tt> if <tt>$DEBUG</tt> variable is set.
82
+ #
83
+ class Packet
84
+ include Names
85
+
86
+ # Base error class.
87
+ class Error < StandardError
88
+ end
89
+
90
+ # Generic Packet Error.
91
+ class PacketError < Error
92
+ end
93
+
94
+
95
+ attr_reader :header, :question, :answer, :authority, :additional
96
+ attr_reader :answerfrom, :answersize
97
+
98
+ # Creates a new instance of <tt>Net::DNS::Packet</tt> class. Arguments are the
99
+ # canonical name of the resource, an optional type field and an optional
100
+ # class field. The record type and class can be omitted; they default
101
+ # to +A+ and +IN+.
102
+ #
103
+ # packet = Net::DNS::Packet.new("www.example.com")
104
+ # packet = Net::DNS::Packet.new("example.com", Net::DNS::MX)
105
+ # packet = Net::DNS::Packet.new("example.com", Net::DNS::TXT, Net::DNS::CH)
106
+ #
107
+ # This class no longer instantiate object from binary data coming from
108
+ # network streams. Please use <tt>Net::DNS::Packet.parse</tt> instead.
109
+ def initialize(name, type = Net::DNS::A, cls = Net::DNS::IN)
110
+ @header = Net::DNS::Header.new(:qdCount => 1)
111
+ @question = [Net::DNS::Question.new(name, type, cls)]
112
+ @answer = []
113
+ @authority = []
114
+ @additional = []
115
+ @logger = Logger.new $stdout
116
+ @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
117
+ end
118
+
119
+
120
+ # Checks if the packet is a QUERY packet
121
+ def query?
122
+ @header.opCode == Net::DNS::Header::QUERY
123
+ end
124
+
125
+ # Returns the packet object in binary data, suitable
126
+ # for sending across a network stream.
127
+ #
128
+ # packet_data = packet.data
129
+ # puts "Packet is #{packet_data.size} bytes long"
130
+ #
131
+ def data
132
+ qdcount=ancount=nscount=arcount=0
133
+ data = @header.data
134
+ headerlength = data.length
135
+
136
+ @question.each do |question|
137
+ data += question.data
138
+ qdcount += 1
139
+ end
140
+ @answer.each do |rr|
141
+ data += rr.data#(data.length)
142
+ ancount += 1
143
+ end
144
+ @authority.each do |rr|
145
+ data += rr.data#(data.length)
146
+ nscount += 1
147
+ end
148
+ @additional.each do |rr|
149
+ data += rr.data#(data.length)
150
+ arcount += 1
151
+ end
152
+
153
+ @header.qdCount = qdcount
154
+ @header.anCount = ancount
155
+ @header.nsCount = nscount
156
+ @header.arCount = arcount
157
+
158
+ @header.data + data[Net::DNS::HFIXEDSZ..data.size]
159
+ end
160
+
161
+ # Same as <tt>Net::DNS::Packet#data</tt>, but implements name compression
162
+ # (see RFC1025) for a considerable save of bytes.
163
+ #
164
+ # packet = Net::DNS::Packet.new("www.example.com")
165
+ # puts "Size normal is #{packet.data.size} bytes"
166
+ # puts "Size compressed is #{packet.data_comp.size} bytes"
167
+ #
168
+ def data_comp
169
+ offset = 0
170
+ compnames = {}
171
+ qdcount=ancount=nscount=arcount=0
172
+ data = @header.data
173
+ headerlength = data.length
174
+
175
+ @question.each do |question|
176
+ str,offset,names = question.data
177
+ data += str
178
+ compnames.update(names)
179
+ qdcount += 1
180
+ end
181
+
182
+ @answer.each do |rr|
183
+ str,offset,names = rr.data(offset,compnames)
184
+ data += str
185
+ compnames.update(names)
186
+ ancount += 1
187
+ end
188
+
189
+ @authority.each do |rr|
190
+ str,offset,names = rr.data(offset,compnames)
191
+ data += str
192
+ compnames.update(names)
193
+ nscount += 1
194
+ end
195
+
196
+ @additional.each do |rr|
197
+ str,offset,names = rr.data(offset,compnames)
198
+ data += str
199
+ compnames.update(names)
200
+ arcount += 1
201
+ end
202
+
203
+ @header.qdCount = qdcount
204
+ @header.anCount = ancount
205
+ @header.nsCount = nscount
206
+ @header.arCount = arcount
207
+
208
+ @header.data + data[Net::DNS::HFIXEDSZ..data.size]
209
+ end
210
+
211
+ # Returns a string containing a human-readable representation
212
+ # of this <tt>Net::DNS::Packet</tt> instance.
213
+ def inspect
214
+ retval = ""
215
+ if @answerfrom != "0.0.0.0:0" and @answerfrom
216
+ retval += ";; Answer received from #@answerfrom (#{@answersize} bytes)\n;;\n"
217
+ end
218
+
219
+ retval += ";; HEADER SECTION\n"
220
+ retval += @header.inspect
221
+
222
+ retval += "\n"
223
+ section = (@header.opCode == "UPDATE") ? "ZONE" : "QUESTION"
224
+ retval += ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '' : 's'}):\n"
225
+ @question.each do |qr|
226
+ retval += ";; " + qr.inspect + "\n"
227
+ end
228
+
229
+ unless @answer.size == 0
230
+ retval += "\n"
231
+ section = (@header.opCode == "UPDATE") ? "PREREQUISITE" : "ANSWER"
232
+ retval += ";; #{section} SECTION (#{@header.anCount} record#{@header.anCount == 1 ? '' : 's'}):\n"
233
+ @answer.each do |rr|
234
+ retval += rr.inspect + "\n"
235
+ end
236
+ end
237
+
238
+ unless @authority.size == 0
239
+ retval += "\n"
240
+ section = (@header.opCode == "UPDATE") ? "UPDATE" : "AUTHORITY"
241
+ retval += ";; #{section} SECTION (#{@header.nsCount} record#{@header.nsCount == 1 ? '' : 's'}):\n"
242
+ @authority.each do |rr|
243
+ retval += rr.inspect + "\n"
244
+ end
245
+ end
246
+
247
+ unless @additional.size == 0
248
+ retval += "\n"
249
+ retval += ";; ADDITIONAL SECTION (#{@header.arCount} record#{@header.arCount == 1 ? '' : 's'}):\n"
250
+ @additional.each do |rr|
251
+ retval += rr.inspect + "\n"
252
+ end
253
+ end
254
+
255
+ retval
256
+ end
257
+ alias_method :to_s, :inspect
258
+
259
+ # Delegates to <tt>Net::DNS::Header#truncated?</tt>.
260
+ def truncated?
261
+ @header.truncated?
262
+ end
263
+
264
+ # Assigns a <tt>Net::DNS::Header</tt> <tt>object</tt>
265
+ # to this <tt>Net::DNS::Packet</tt> instance.
266
+ def header=(object)
267
+ if object.kind_of? Net::DNS::Header
268
+ @header = object
269
+ else
270
+ raise ArgumentError, "Argument must be a Net::DNS::Header object"
271
+ end
272
+ end
273
+
274
+ # Assigns a <tt>Net::DNS::Question</tt> <tt>object</tt>
275
+ # to this <tt>Net::DNS::Packet</tt> instance.
276
+ def question=(object)
277
+ case object
278
+ when Array
279
+ if object.all? {|x| x.kind_of? Net::DNS::Question}
280
+ @question = object
281
+ else
282
+ raise ArgumentError, "Some of the elements is not an Net::DNS::Question object"
283
+ end
284
+ when Net::DNS::Question
285
+ @question = [object]
286
+ else
287
+ raise ArgumentError, "Invalid argument, not a Question object nor an array of objects"
288
+ end
289
+ end
290
+
291
+ # Assigns one or an array of <tt>Net::DNS::RR</tt> <tt>object</tt>s
292
+ # to the answer section of this <tt>Net::DNS::Packet</tt> instance.
293
+ def answer=(object)
294
+ case object
295
+ when Array
296
+ if object.all? {|x| x.kind_of? Net::DNS::RR}
297
+ @answer = object
298
+ else
299
+ raise ArgumentError, "Some of the elements is not an Net::DNS::RR object"
300
+ end
301
+ when Net::DNS::RR
302
+ @answer = [object]
303
+ else
304
+ raise ArgumentError, "Invalid argument, not a RR object nor an array of objects"
305
+ end
306
+ end
307
+
308
+ # Assigns one or an array of <tt>Net::DNS::RR</tt> <tt>object</tt>s
309
+ # to the additional section of this <tt>Net::DNS::Packet</tt> instance.
310
+ def additional=(object)
311
+ case object
312
+ when Array
313
+ if object.all? {|x| x.kind_of? Net::DNS::RR}
314
+ @additional = object
315
+ else
316
+ raise ArgumentError, "Some of the elements is not an Net::DNS::RR object"
317
+ end
318
+ when Net::DNS::RR
319
+ @additional = [object]
320
+ else
321
+ raise ArgumentError, "Invalid argument, not a RR object nor an array of objects"
322
+ end
323
+ end
324
+
325
+ # Assigns one or an array of <tt>Net::DNS::RR</tt> <tt>object</tt>s
326
+ # to the authority section of this <tt>Net::DNS::Packet</tt> instance.
327
+ def authority=(object)
328
+ case object
329
+ when Array
330
+ if object.all? {|x| x.kind_of? Net::DNS::RR}
331
+ @authority = object
332
+ else
333
+ raise ArgumentError, "Some of the elements is not an Net::DNS::RR object"
334
+ end
335
+ when Net::DNS::RR
336
+ @authority = [object]
337
+ else
338
+ raise ArgumentError, "Invalid argument, not a RR object nor an array of objects"
339
+ end
340
+ end
341
+
342
+ # Iterates every address in the +answer+ section
343
+ # of this <tt>Net::DNS::Packet</tt> instance.
344
+ #
345
+ # packet.each_address do |ip|
346
+ # ping ip.to_s
347
+ # end
348
+ #
349
+ # As you can see in the documentation for the <tt>Net::DNS::RR::A</tt> class,
350
+ # the address returned is an instance of <tt>IPAddr</tt> class.
351
+ def each_address(&block)
352
+ @answer.each do |elem|
353
+ next unless elem.class == Net::DNS::RR::A
354
+ yield elem.address
355
+ end
356
+ end
357
+
358
+ # Iterates every nameserver in the +answer+ section
359
+ # of this <tt>Net::DNS::Packet</tt> instance.
360
+ #
361
+ # packet.each_nameserver do |ns|
362
+ # puts "Nameserver found: #{ns}"
363
+ # end
364
+ #
365
+ def each_nameserver(&block)
366
+ @answer.each do |elem|
367
+ next unless elem.class == Net::DNS::RR::NS
368
+ yield elem.nsdname
369
+ end
370
+ end
371
+
372
+ # Iterates every exchange record in the +answer+ section
373
+ # of this <tt>Net::DNS::Packet</tt> instance.
374
+ #
375
+ # packet.each_mx do |pref,name|
376
+ # puts "Mail exchange #{name} has preference #{pref}"
377
+ # end
378
+ #
379
+ def each_mx(&block)
380
+ @answer.each do |elem|
381
+ next unless elem.class == Net::DNS::RR::MX
382
+ yield elem.preference, elem.exchange
383
+ end
384
+ end
385
+
386
+ # Iterates every canonical name in the +answer+ section
387
+ # of this <tt>Net::DNS::Packet</tt> instance.
388
+ #
389
+ # packet.each_cname do |cname|
390
+ # puts "Canonical name: #{cname}"
391
+ # end
392
+ #
393
+ def each_cname(&block)
394
+ @answer.each do |elem|
395
+ next unless elem.class == Net::DNS::RR::CNAME
396
+ yield elem.cname
397
+ end
398
+ end
399
+
400
+ # Iterates every pointer in the +answer+ section
401
+ # of this <tt>Net::DNS::Packet</tt> instance.
402
+ #
403
+ # packet.each_ptr do |ptr|
404
+ # puts "Pointer for resource: #{ptr}"
405
+ # end
406
+ #
407
+ def each_ptr(&block)
408
+ @answer.each do |elem|
409
+ next unless elem.class == Net::DNS::RR::PTR
410
+ yield elem.ptrdname
411
+ end
412
+ end
413
+
414
+ # Returns the packet size in bytes.
415
+ #
416
+ # Resolver("www.google.com") do |packet|
417
+ # puts packet.size + " bytes"}
418
+ # end
419
+ # # => 484 bytes
420
+ #
421
+ def size
422
+ data.size
423
+ end
424
+
425
+ # Checks whether the query returned a NXDOMAIN error,
426
+ # meaning the queried domain name doesn't exist.
427
+ #
428
+ # %w[a.com google.com ibm.com d.com].each do |domain|
429
+ # response = Net::DNS::Resolver.new.send(domain)
430
+ # puts "#{domain} doesn't exist" if response.nxdomain?
431
+ # end
432
+ # # => a.com doesn't exist
433
+ # # => d.com doesn't exist
434
+ #
435
+ def nxdomain?
436
+ header.rCode.code == Net::DNS::Header::RCode::NAME
437
+ end
438
+
439
+
440
+ # Creates a new instance of <tt>Net::DNS::Packet</tt> class from binary data,
441
+ # taken out from a network stream. For example:
442
+ #
443
+ # # udp_socket is an UDPSocket waiting for a response
444
+ # ans = udp_socket.recvfrom(1500)
445
+ # packet = Net::DNS::Packet::parse(ans)
446
+ #
447
+ # An optional +from+ argument can be used to specify the information
448
+ # of the sender. If data is passed as is from a Socket#recvfrom call,
449
+ # the method will accept it.
450
+ #
451
+ # Be sure that your network data is clean from any UDP/TCP header,
452
+ # especially when using RAW sockets.
453
+ #
454
+ def self.parse(*args)
455
+ o = allocate
456
+ o.send(:new_from_data, *args)
457
+ o
458
+ end
459
+
460
+ private
461
+
462
+ # New packet from binary data
463
+ def new_from_data(data, from = nil)
464
+ unless from
465
+ if data.kind_of? Array
466
+ data, from = data
467
+ else
468
+ from = [0, 0, "0.0.0.0", "unknown"]
469
+ end
470
+ end
471
+
472
+ @answerfrom = from[2] + ":" + from[1].to_s
473
+ @answersize = data.size
474
+ @logger = Logger.new $stdout
475
+ @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
476
+
477
+ #------------------------------------------------------------
478
+ # Header section
479
+ #------------------------------------------------------------
480
+ offset = Net::DNS::HFIXEDSZ
481
+ @header = Net::DNS::Header.parse(data[0..offset-1])
482
+
483
+ @logger.debug ";; HEADER SECTION"
484
+ @logger.debug @header.inspect
485
+
486
+ #------------------------------------------------------------
487
+ # Question section
488
+ #------------------------------------------------------------
489
+ section = @header.opCode == "UPDATE" ? "ZONE" : "QUESTION"
490
+ @logger.debug ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '': 's'})"
491
+
492
+ @question = []
493
+ @header.qdCount.times do
494
+ qobj,offset = parse_question(data,offset)
495
+ @question << qobj
496
+ @logger.debug ";; #{qobj.inspect}"
497
+ end
498
+
499
+ #------------------------------------------------------------
500
+ # Answer/prerequisite section
501
+ #------------------------------------------------------------
502
+ section = @header.opCode == "UPDATE" ? "PREREQUISITE" : "ANSWER"
503
+ @logger.debug ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '': 's'})"
504
+
505
+ @answer = []
506
+ @header.anCount.times do
507
+ begin
508
+ rrobj,offset = Net::DNS::RR.parse_packet(data,offset)
509
+ @answer << rrobj
510
+ @logger.debug rrobj.inspect
511
+ rescue NameError => e
512
+ warn "Net::DNS unsupported record type: #{e.message}"
513
+ end
514
+ end
515
+
516
+ #------------------------------------------------------------
517
+ # Authority/update section
518
+ #------------------------------------------------------------
519
+ section = @header.opCode == "UPDATE" ? "UPDATE" : "AUTHORITY"
520
+ @logger.debug ";; #{section} SECTION (#{@header.nsCount} record#{@header.nsCount == 1 ? '': 's'})"
521
+
522
+ @authority = []
523
+ @header.nsCount.times do
524
+ begin
525
+ rrobj,offset = Net::DNS::RR.parse_packet(data,offset)
526
+ @authority << rrobj
527
+ @logger.debug rrobj.inspect
528
+ rescue NameError => e
529
+ warn "Net::DNS unsupported record type: #{e.message}"
530
+ end
531
+ end
532
+
533
+ #------------------------------------------------------------
534
+ # Additional section
535
+ #------------------------------------------------------------
536
+ @logger.debug ";; ADDITIONAL SECTION (#{@header.arCount} record#{@header.arCount == 1 ? '': 's'})"
537
+
538
+ @additional = []
539
+ @header.arCount.times do
540
+ begin
541
+ rrobj,offset = Net::DNS::RR.parse_packet(data,offset)
542
+ @additional << rrobj
543
+ @logger.debug rrobj.inspect
544
+ rescue NameError => e
545
+ warn "Net::DNS supported record type: #{e.message}"
546
+ end
547
+ end
548
+
549
+ end
550
+
551
+
552
+ # Parse question section
553
+ def parse_question(data,offset)
554
+ size = (dn_expand(data, offset)[1] - offset) + (2 * Net::DNS::INT16SZ)
555
+ return [Net::DNS::Question.parse(data[offset, size]), offset + size]
556
+ rescue StandardError => e
557
+ raise PacketError, "Caught exception, maybe packet malformed => #{e.message}"
558
+ end
559
+
560
+ end
561
+
562
+ end
563
+ end