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