CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/proto/dns/packet.rb
Views: 11704
1
# -*- coding: binary -*-
2
3
require 'net/dns'
4
require 'resolv'
5
require 'dnsruby'
6
7
module Rex
8
module Proto
9
module DNS
10
11
module Packet
12
13
#
14
# Checks string to ensure it can be used as a valid hostname
15
#
16
# @param subject [String] Subject name to check
17
#
18
# @return [TrueClass,FalseClass] Disposition on name match
19
def self.valid_hostname?(subject = '')
20
!subject.match(Rex::Proto::DNS::Constants::MATCH_HOSTNAME).nil?
21
end
22
23
#
24
# Reconstructs a packet with both standard DNS libraries
25
# Ensures that headers match the payload
26
#
27
# @param packet [String, Net::DNS::Packet, Dnsruby::Message] Data to be validated
28
#
29
# @return [Dnsruby::Message]
30
def self.validate(packet)
31
self.encode_drb(self.encode_net(self.encode_res(packet)))
32
end
33
34
#
35
# Sets header values to match packet content
36
#
37
# @param packet [String] Net::DNS::Packet, Resolv::DNS::Message, Dnsruby::Message]
38
#
39
# @return [Dnsruby::Message]
40
def self.recalc_headers(packet)
41
packet = self.encode_drb(packet)
42
{
43
:qdcount= => :question,
44
:ancount= => :answer,
45
:nscount= => :authority,
46
:arcount= => :additional
47
}.each do |header,body|
48
packet.header.send(header,packet.send(body).count)
49
end
50
51
return packet
52
end
53
54
#
55
# Reads a packet into the Net::DNS::Packet format
56
#
57
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message, Dnsruby::Message] Input data
58
#
59
# @return [Net::DNS::Packet]
60
def self.encode_net(packet)
61
return packet if packet.is_a?(Net::DNS::Packet)
62
Net::DNS::Packet.parse(
63
self.encode_raw(packet)
64
)
65
end
66
67
# Reads a packet into the Resolv::DNS::Message format
68
#
69
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message, Dnsruby::Message] Input data
70
#
71
# @return [Resolv::DNS::Message]
72
def self.encode_res(packet)
73
return packet if packet.is_a?(Resolv::DNS::Message)
74
Resolv::DNS::Message.decode(
75
self.encode_raw(packet)
76
)
77
end
78
79
# Reads a packet into the Dnsruby::Message format
80
#
81
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message, Dnsruby::Message] Input data
82
#
83
# @return [Dnsruby::Message]
84
def self.encode_drb(packet)
85
return packet if packet.is_a?(Dnsruby::Message)
86
Dnsruby::Message.decode(
87
self.encode_raw(packet)
88
)
89
end
90
91
# Reads a packet into the raw String format
92
#
93
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message, Dnsruby::Message] Input data
94
#
95
# @return [String]
96
def self.encode_raw(packet)
97
return packet unless packet.respond_to?(:encode) or packet.respond_to?(:data)
98
(packet.respond_to?(:data) ? packet.data : packet.encode).force_encoding('binary')
99
end
100
101
#
102
# Generates a request packet, taken from Net::DNS::Resolver
103
#
104
# @param subject [String] Subject name of question section
105
# @param type [Fixnum] Type of DNS record to query
106
# @param cls [Fixnum] Class of dns record to query
107
# @param recurse [Fixnum] Recursive query or not
108
#
109
# @return [Dnsruby::Message] request packet
110
def self.generate_request(subject, type = Dnsruby::Types::A, cls = Dnsruby::Classes::IN, recurse = 1)
111
case subject
112
when IPAddr
113
name = subject.reverse
114
type = Dnsruby::Types::PTR
115
when /\d/ # Contains a number, try to see if it's an IP or IPv6 address
116
begin
117
name = IPAddr.new(subject).reverse
118
type = Dnsruby::Types::PTR
119
rescue ArgumentError
120
name = subject if self.valid_hostname?(subject)
121
end
122
else
123
name = subject if self.valid_hostname?(subject)
124
end
125
126
# Create the packet
127
packet = Dnsruby::Message.new(name, type, cls)
128
129
if packet.header.opcode == Dnsruby::OpCode::Query
130
packet.header.recursive = recurse
131
end
132
133
# DNSSEC and TSIG stuff to be inserted here
134
135
return packet
136
end
137
138
#
139
# Generates a response packet for an existing request
140
#
141
# @param request [String] Net::DNS::Packet, Resolv::DNS::Message] Original request
142
# @param answer [Array] Set of answers to provide in the response
143
# @param authority [Array] Set of authority records to provide in the response
144
# @param additional [Array] Set of additional records to provide in the response
145
#
146
# @return [Dnsruby::Message] Response packet
147
def self.generate_response(request, answer = nil, authority = nil, additional = nil)
148
packet = self.encode_drb(request)
149
packet.answer = answer if answer
150
packet.authority = authority if authority
151
packet.additional = additional if additional
152
packet = self.recalc_headers(packet)
153
154
# Set error code for NXDomain or unset it if reprocessing a response
155
if packet.header.ancount < 1
156
packet.header.rcode = Dnsruby::RCode::NXDOMAIN
157
else
158
if packet.header.qr and packet.header.get_header_rcode.to_i == 3
159
packet.header.rcode = Dnsruby::RCode::NOERROR
160
end
161
end
162
# Set response bit last to allow reprocessing of responses
163
packet.header.qr = true
164
# Set recursion available bit if recursion desired
165
packet.header.ra = true if packet.header.rd
166
return packet
167
end
168
169
module Raw
170
171
#
172
# Convert data to little endian unsigned short
173
#
174
# @param data [Fixnum, Float, Array] Input for conversion
175
#
176
# @return [String] Raw output
177
def self.to_short_le(data)
178
[data].flatten.pack('S*')
179
end
180
181
#
182
# Convert data from little endian unsigned short
183
#
184
# @param data [String] Input for conversion
185
#
186
# @return [Array] Integer array output
187
def self.from_short_le(data)
188
data.unpack('S*')
189
end
190
191
#
192
# Convert data to little endian unsigned int
193
#
194
# @param data [Fixnum, Float, Array] Input for conversion
195
#
196
# @return [String] Raw output
197
def self.to_int_le(data)
198
[data].flatten.pack('I*')
199
end
200
201
#
202
# Convert data from little endian unsigned int
203
#
204
# @param data [String] Input for conversion
205
#
206
# @return [Array] Integer array output
207
def self.from_int_le(data)
208
data.unpack('I*')
209
end
210
211
#
212
# Convert data to little endian unsigned long
213
#
214
# @param data [Fixnum, Float, Array] Input for conversion
215
#
216
# @return [String] Raw output
217
def self.to_long_le(data)
218
[data].flatten.pack('L*')
219
end
220
221
#
222
# Convert data from little endian unsigned long
223
#
224
# @param data [String] Input for conversion
225
#
226
# @return [Array] Integer array output
227
def self.from_long_le(data)
228
data.unpack('L*')
229
end
230
231
#
232
# Convert data to big endian unsigned short
233
#
234
# @param data [Fixnum, Float, Array] Input for conversion
235
#
236
# @return [String] Raw output
237
def self.to_short_be(data)
238
[data].flatten.pack('S>*')
239
end
240
241
#
242
# Convert data from big endian unsigned short
243
#
244
# @param data [String] Input for conversion
245
#
246
# @return [Array] Integer array output
247
def self.from_short_be(data)
248
data.unpack('S>*')
249
end
250
251
#
252
# Convert data to big endian unsigned int
253
#
254
# @param data [Fixnum, Float, Array] Input for conversion
255
#
256
# @return [String] Raw output
257
def self.to_int_be(data)
258
[data].flatten.pack('I>*')
259
end
260
261
#
262
# Convert data from big endian unsigned int
263
#
264
# @param data [String] Input for conversion
265
#
266
# @return [Array] Integer array output
267
def self.from_int_be(data)
268
data.unpack('I>*')
269
end
270
271
#
272
# Convert data to big endian unsigned long
273
#
274
# @param data [Fixnum, Float, Array] Input for conversion
275
#
276
# @return [String] Raw output
277
def self.to_long_be(data)
278
[data].flatten.pack('L>*')
279
end
280
281
#
282
# Convert data from big endian unsigned long
283
#
284
# @param data [String] Input for conversion
285
#
286
# @return [Array] Integer array output
287
def self.from_long_be(data)
288
data.unpack('L>*')
289
end
290
291
#
292
# Returns request ID from raw packet skipping parsing
293
#
294
# @param data [String] Request data
295
#
296
# @return [Fixnum] Request ID
297
def self.request_id(data)
298
self.from_short_be(data[0..1])[0]
299
end
300
301
#
302
# Returns request length from raw packet skipping parsing
303
#
304
# @param data [String] Request data
305
#
306
# @return [Fixnum] Request Length
307
def self.request_length(data)
308
self.from_short_le(data[0..2])[0]
309
end
310
end
311
end
312
313
end
314
end
315
end
316
317