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/iax2/call.rb
Views: 11703
1
# -*- coding: binary -*-
2
module Rex
3
module Proto
4
module IAX2
5
class Call
6
7
attr_accessor :client
8
attr_accessor :oseq, :iseq
9
attr_accessor :scall, :dcall
10
attr_accessor :codec, :state
11
attr_accessor :ring_start, :ring_finish
12
attr_accessor :itime
13
attr_accessor :queue
14
attr_accessor :audio_hook
15
attr_accessor :audio_buff
16
attr_accessor :time_limit
17
attr_accessor :busy
18
19
attr_accessor :caller_name
20
attr_accessor :caller_number
21
attr_accessor :dtmf
22
23
24
def initialize(client, src_id)
25
self.client = client
26
self.scall = src_id
27
self.dcall = 0
28
self.iseq = 0
29
self.oseq = 0
30
self.state = nil
31
32
self.itime = ::Time.now
33
self.queue = ::Queue.new
34
35
self.audio_buff = []
36
37
self.busy = false
38
self.dtmf = ''
39
end
40
41
42
def dprint(msg)
43
self.client.dprint(msg)
44
end
45
46
def wait_for(*stypes)
47
begin
48
::Timeout.timeout( Constants::IAX_DEFAULT_TIMEOUT ) do
49
while (res = self.queue.pop )
50
if stypes.include?(res[1])
51
return res
52
end
53
end
54
end
55
rescue ::Timeout::Error
56
return nil
57
end
58
end
59
60
# Register with the IAX endpoint
61
def register
62
self.client.send_regreq(self)
63
res = wait_for( Constants::IAX_SUBTYPE_REGAUTH, Constants::IAX_SUBTYPE_REGREJ )
64
return if not res
65
66
if res[1] == Constants::IAX_SUBTYPE_REGREJ
67
reason = res[2][Constants::IAX_IE_REGREJ_CAUSE] || "Unknown Reason"
68
dprint("REGREJ: #{reason}")
69
# Acknowledge the REGREJ
70
self.client.send_ack(self)
71
return
72
end
73
74
chall = nil
75
76
# Look for IAX_AUTH_MD5 (2) as an available auth method
77
if res[2][14].unpack("n")[0] & 2 <= 0
78
dprint("REGAUTH: MD5 authentication is not enabled on the server")
79
return
80
end
81
82
if res[2][Constants::IAX_IE_CHALLENGE_DATA]
83
self.dcall = res[0][0]
84
chall = res[2][Constants::IAX_IE_CHALLENGE_DATA]
85
end
86
87
if chall.nil?
88
dprint("REGAUTH: No challenge data received")
89
return
90
end
91
92
self.client.send_regreq_chall_response(self, chall)
93
res = wait_for( Constants::IAX_SUBTYPE_REGACK, Constants::IAX_SUBTYPE_REGREJ )
94
return if not res
95
96
if res[1] == Constants::IAX_SUBTYPE_REGREJ
97
reason = res[2][Constants::IAX_IE_REGREJ_CAUSE] || "Unknown Reason"
98
dprint("REGREJ: #{reason}")
99
return
100
end
101
102
if res[2][Constants::IAX_IE_APPARENT_ADDR]
103
r_fam, r_port, r_addr = res[2][Constants::IAX_IE_APPARENT_ADDR].unpack('nnA4')
104
r_addr = r_addr.unpack("C*").map{|x| x.to_s }.join(".")
105
dprint("REGACK: Registered from address #{r_addr}:#{r_port}")
106
end
107
108
# Acknowledge the REGACK
109
self.client.send_ack(self)
110
111
self.state = :registered
112
113
true
114
end
115
116
def dial(number)
117
self.client.send_new(self, number)
118
res = wait_for(Constants::IAX_SUBTYPE_AUTHREQ, Constants::IAX_SUBTYPE_ACCEPT)
119
return if not res
120
121
# Handle authentication if its requested
122
if res[1] == Constants::IAX_SUBTYPE_AUTHREQ
123
chall = nil
124
125
# Look for IAX_AUTH_MD5 (2) as an available auth method
126
if res[2][14].unpack("n")[0] & 2 <= 0
127
dprint("REGAUTH: MD5 authentication is not enabled on the server")
128
return
129
end
130
131
if res[2][Constants::IAX_IE_CHALLENGE_DATA]
132
self.dcall = res[0][0]
133
chall = res[2][Constants::IAX_IE_CHALLENGE_DATA]
134
end
135
136
if chall.nil?
137
dprint("REGAUTH: No challenge data received")
138
return
139
end
140
141
self.client.send_authrep_chall_response(self, chall)
142
res = wait_for( Constants::IAX_SUBTYPE_ACCEPT)
143
return if not res
144
end
145
146
self.codec = res[2][Constants::IAX_IE_DESIRED_CODEC].unpack("N")[0]
147
self.state = :ringing
148
self.ring_start = ::Time.now.to_i
149
self.client.send_ack(self)
150
true
151
end
152
153
def hangup
154
self.client.send_hangup(self)
155
self.state = :hangup
156
true
157
end
158
159
def ring_time
160
(self.ring_finish || ::Time.now).to_i - self.ring_start.to_i
161
end
162
163
def timestamp
164
(( ::Time.now - self.itime) * 1000.0 ).to_i & 0xffffffff
165
end
166
167
def process_elements(data,off=0)
168
res = {}
169
while( off < data.length )
170
ie_type = data[off ,1].unpack("C")[0]
171
ie_len = data[off + 1,2].unpack("C")[0]
172
res[ie_type] = data[off + 2, ie_len]
173
off += ie_len + 2
174
end
175
res
176
end
177
178
# Handling incoming control packets
179
# TODO: Enforce sequence order to prevent duplicates from breaking our state
180
def handle_control(pkt)
181
src_call, dst_call, tstamp, out_seq, inp_seq, itype = pkt.unpack('nnNCCC')
182
183
# Scrub the high bits out of the call IDs
184
src_call ^= 0x8000 if (src_call & 0x8000 != 0)
185
dst_call ^= 0x8000 if (dst_call & 0x8000 != 0)
186
187
phdr = [ src_call, dst_call, tstamp, out_seq, inp_seq, itype ]
188
189
info = nil
190
stype = pkt[11,1].unpack("C")[0]
191
info = process_elements(pkt, 12) if [Constants::IAX_TYPE_IAX, Constants::IAX_TYPE_CONTROL].include?(itype)
192
193
if dst_call != self.scall
194
dprint("Incoming packet to inactive call: #{dst_call} vs #{self.scall}: #{phdr.inspect} #{stype.inspect} #{info.inspect}")
195
return
196
end
197
198
# Increment the received sequence number
199
self.iseq = (self.iseq + 1) & 0xff
200
201
if self.state == :hangup
202
dprint("Packet received after hangup, replying with invalid")
203
self.client.send_invalid(self)
204
return
205
end
206
207
# Technically these all require an ACK reply
208
# NEW, HANGUP, REJECT, ACCEPT, PONG, AUTHREP, REGREL, REGACK, REGREJ, TXREL
209
210
case itype
211
when Constants::IAX_TYPE_DTMF_BEGIN
212
self.dprint("DTMF BEG: #{pkt[11,1]}")
213
self.dtmf << pkt[11,1]
214
215
when Constants::IAX_TYPE_DTMF_END
216
self.dprint("DTMF END: #{pkt[11,1]}")
217
218
when Constants::IAX_TYPE_CONTROL
219
case stype
220
when Constants::IAX_CTRL_HANGUP
221
dprint("HANGUP")
222
self.client.send_ack(self)
223
self.state = :hangup
224
225
when Constants::IAX_CTRL_RINGING
226
dprint("RINGING")
227
self.client.send_ack(self)
228
229
when Constants::IAX_CTRL_BUSY
230
dprint("BUSY")
231
self.busy = true
232
self.state = :hangup
233
self.client.send_ack(self)
234
235
when Constants::IAX_CTRL_ANSWER
236
dprint("ANSWER")
237
if self.state == :ringing
238
self.state = :answered
239
self.ring_finish = ::Time.now.to_i
240
end
241
self.client.send_ack(self)
242
243
when Constants::IAX_CTRL_PROGRESS
244
dprint("PROGRESS")
245
246
when Constants::IAX_CTRL_PROCEED
247
dprint("PROCEED")
248
249
when 255
250
dprint("STOP SOUNDS")
251
end
252
# Acknowledge all control packets
253
# self.client.send_ack(self)
254
255
when Constants::IAX_TYPE_IAX
256
257
dprint( ["RECV", phdr, stype, info].inspect )
258
case stype
259
when Constants::IAX_SUBTYPE_HANGUP
260
self.state = :hangup
261
self.client.send_ack(self)
262
when Constants::IAX_SUBTYPE_LAGRQ
263
# Lagrps echo the timestamp
264
self.client.send_lagrp(self, tstamp)
265
when Constants::IAX_SUBTYPE_ACK
266
# Nothing to do here
267
when Constants::IAX_SUBTYPE_PING
268
# Pongs echo the timestamp
269
self.client.send_pong(self, tstamp)
270
when Constants::IAX_SUBTYPE_PONG
271
self.client.send_ack(self)
272
else
273
dprint( ["RECV-QUEUE", phdr, stype, info].inspect )
274
self.queue.push( [phdr, stype, info ] )
275
end
276
277
when Constants::IAX_TYPE_VOICE
278
v_codec = stype
279
if self.state == :answered
280
handle_audio(pkt)
281
end
282
self.client.send_ack(self)
283
284
when nil
285
dprint("Invalid control packet: #{pkt.unpack("H*")[0]}")
286
end
287
end
288
289
290
# Encoded audio from the client
291
def handle_audio(pkt)
292
# Ignore audio received before the call is answered (ring ring)
293
return if self.state != :answered
294
295
# Extract the data from the packet (full or mini)
296
data = audio_packet_data(pkt)
297
298
# Decode the data into linear PCM frames
299
buff = decode_audio_frame(data)
300
301
# Call the caller-provided hook if its exists
302
if self.audio_hook
303
self.audio_buff(buff)
304
# Otherwise append the frame to the buffer
305
else
306
self.audio_buff << buff
307
end
308
end
309
310
def each_audio_frame(&block)
311
self.audio_buff.each do |frame|
312
block.call(frame)
313
end
314
end
315
316
def decode_audio_frame(buff)
317
case self.codec
318
319
# Convert u-law into signed PCM
320
when Constants::IAX_CODEC_G711_MULAW
321
Rex::Proto::IAX2::Codecs::MuLaw.decode(buff)
322
323
# Convert a-law into signed PCM
324
when Constants::IAX_CODEC_G711_ALAW
325
Rex::Proto::IAX2::Codecs::ALaw.decode(buff)
326
327
# Linear little-endian signed PCM is our native format
328
when Constants::IAX_CODEC_LINEAR_PCM
329
buff
330
331
# Unsupported codec, return empty
332
else
333
dprint("UNKNOWN CODEC: #{self.codec.inspect}")
334
''
335
end
336
end
337
338
def audio_packet_data(pkt)
339
(pkt[0,1].unpack("C")[0] & 0x80 == 0) ? pkt[4,pkt.length-4] : pkt[12,pkt.length-12]
340
end
341
342
end
343
end
344
end
345
end
346
347