net-dns2 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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