lib/rex/proto/iax2/call.rb in librex-0.0.68 vs lib/rex/proto/iax2/call.rb in librex-0.0.70

- old
+ new

@@ -2,319 +2,319 @@ module Rex module Proto module IAX2 class Call - attr_accessor :client - attr_accessor :oseq, :iseq - attr_accessor :scall, :dcall - attr_accessor :codec, :state - attr_accessor :ring_start, :ring_finish - attr_accessor :itime - attr_accessor :queue - attr_accessor :audio_hook - attr_accessor :audio_buff - attr_accessor :time_limit - attr_accessor :busy + attr_accessor :client + attr_accessor :oseq, :iseq + attr_accessor :scall, :dcall + attr_accessor :codec, :state + attr_accessor :ring_start, :ring_finish + attr_accessor :itime + attr_accessor :queue + attr_accessor :audio_hook + attr_accessor :audio_buff + attr_accessor :time_limit + attr_accessor :busy - attr_accessor :caller_name - attr_accessor :caller_number - attr_accessor :dtmf + attr_accessor :caller_name + attr_accessor :caller_number + attr_accessor :dtmf - def initialize(client, src_id) - self.client = client - self.scall = src_id - self.dcall = 0 - self.iseq = 0 - self.oseq = 0 - self.state = nil + def initialize(client, src_id) + self.client = client + self.scall = src_id + self.dcall = 0 + self.iseq = 0 + self.oseq = 0 + self.state = nil - self.itime = ::Time.now - self.queue = ::Queue.new + self.itime = ::Time.now + self.queue = ::Queue.new - self.audio_buff = [] + self.audio_buff = [] - self.busy = false - self.dtmf = '' - end + self.busy = false + self.dtmf = '' + end - def dprint(msg) - self.client.dprint(msg) - end + def dprint(msg) + self.client.dprint(msg) + end - def wait_for(*stypes) - begin - ::Timeout.timeout( IAX_DEFAULT_TIMEOUT ) do - while (res = self.queue.pop ) - if stypes.include?(res[1]) - return res - end - end - end - rescue ::Timeout::Error - return nil - end - end + def wait_for(*stypes) + begin + ::Timeout.timeout( IAX_DEFAULT_TIMEOUT ) do + while (res = self.queue.pop ) + if stypes.include?(res[1]) + return res + end + end + end + rescue ::Timeout::Error + return nil + end + end - # Register with the IAX endpoint - def register - self.client.send_regreq(self) - res = wait_for( IAX_SUBTYPE_REGAUTH, IAX_SUBTYPE_REGREJ ) - return if not res + # Register with the IAX endpoint + def register + self.client.send_regreq(self) + res = wait_for( IAX_SUBTYPE_REGAUTH, IAX_SUBTYPE_REGREJ ) + return if not res - if res[1] == IAX_SUBTYPE_REGREJ - reason = res[2][IAX_IE_REGREJ_CAUSE] || "Unknown Reason" - dprint("REGREJ: #{reason}") - # Acknowledge the REGREJ - self.client.send_ack(self) - return - end + if res[1] == IAX_SUBTYPE_REGREJ + reason = res[2][IAX_IE_REGREJ_CAUSE] || "Unknown Reason" + dprint("REGREJ: #{reason}") + # Acknowledge the REGREJ + self.client.send_ack(self) + return + end - chall = nil - if res[2][14] == "\x00\x03" and res[2][IAX_IE_CHALLENGE_DATA] - self.dcall = res[0][0] - chall = res[2][IAX_IE_CHALLENGE_DATA] - end + chall = nil + if res[2][14] == "\x00\x03" and res[2][IAX_IE_CHALLENGE_DATA] + self.dcall = res[0][0] + chall = res[2][IAX_IE_CHALLENGE_DATA] + end - self.client.send_regreq_chall_response(self, chall) - res = wait_for( IAX_SUBTYPE_REGACK, IAX_SUBTYPE_REGREJ ) - return if not res + self.client.send_regreq_chall_response(self, chall) + res = wait_for( IAX_SUBTYPE_REGACK, IAX_SUBTYPE_REGREJ ) + return if not res - if res[1] == IAX_SUBTYPE_REGREJ - reason = res[2][IAX_IE_REGREJ_CAUSE] || "Unknown Reason" - dprint("REGREJ: #{reason}") - return - end + if res[1] == IAX_SUBTYPE_REGREJ + reason = res[2][IAX_IE_REGREJ_CAUSE] || "Unknown Reason" + dprint("REGREJ: #{reason}") + return + end - if res[2][IAX_IE_APPARENT_ADDR] - r_fam, r_port, r_addr = res[2][IAX_IE_APPARENT_ADDR].unpack('nnA4') - r_addr = r_addr.unpack("C*").map{|x| x.to_s }.join(".") - dprint("REGACK: Registered from address #{r_addr}:#{r_port}") - end + if res[2][IAX_IE_APPARENT_ADDR] + r_fam, r_port, r_addr = res[2][IAX_IE_APPARENT_ADDR].unpack('nnA4') + r_addr = r_addr.unpack("C*").map{|x| x.to_s }.join(".") + dprint("REGACK: Registered from address #{r_addr}:#{r_port}") + end - # Acknowledge the REGACK - self.client.send_ack(self) + # Acknowledge the REGACK + self.client.send_ack(self) - self.state = :registered + self.state = :registered - true - end + true + end - def dial(number) - self.client.send_new(self, number) - res = wait_for(IAX_SUBTYPE_AUTHREQ, IAX_SUBTYPE_ACCEPT) - return if not res + def dial(number) + self.client.send_new(self, number) + res = wait_for(IAX_SUBTYPE_AUTHREQ, IAX_SUBTYPE_ACCEPT) + return if not res - # Handle authentication if its requested - if res[1] == IAX_SUBTYPE_AUTHREQ - chall = nil - if res[2][14] == "\x00\x03" and res[1][15] - self.dcall = res[0][0] - chall = res[2][15] - end + # Handle authentication if its requested + if res[1] == IAX_SUBTYPE_AUTHREQ + chall = nil + if res[2][14] == "\x00\x03" and res[1][15] + self.dcall = res[0][0] + chall = res[2][15] + end - self.client.send_authrep_chall_response(self, chall) - res = wait_for( IAX_SUBTYPE_ACCEPT) - return if not res - end + self.client.send_authrep_chall_response(self, chall) + res = wait_for( IAX_SUBTYPE_ACCEPT) + return if not res + end - self.codec = res[2][IAX_IE_DESIRED_CODEC].unpack("N")[0] - self.state = :ringing - self.ring_start = ::Time.now.to_i - self.client.send_ack(self) - true - end + self.codec = res[2][IAX_IE_DESIRED_CODEC].unpack("N")[0] + self.state = :ringing + self.ring_start = ::Time.now.to_i + self.client.send_ack(self) + true + end - def hangup - self.client.send_hangup(self) - self.state = :hangup - true - end + def hangup + self.client.send_hangup(self) + self.state = :hangup + true + end - def ring_time - (self.ring_finish || Time.now).to_i - self.ring_start.to_i - end + def ring_time + (self.ring_finish || Time.now).to_i - self.ring_start.to_i + end - def timestamp - (( ::Time.now - self.itime) * 1000.0 ).to_i & 0xffffffff - end + def timestamp + (( ::Time.now - self.itime) * 1000.0 ).to_i & 0xffffffff + end - def process_elements(data,off=0) - res = {} - while( off < data.length ) - ie_type = data[off ,1].unpack("C")[0] - ie_len = data[off + 1,2].unpack("C")[0] - res[ie_type] = data[off + 2, ie_len] - off += ie_len + 2 - end - res - end + def process_elements(data,off=0) + res = {} + while( off < data.length ) + ie_type = data[off ,1].unpack("C")[0] + ie_len = data[off + 1,2].unpack("C")[0] + res[ie_type] = data[off + 2, ie_len] + off += ie_len + 2 + end + res + end - # Handling incoming control packets - # TODO: Enforce sequence order to prevent duplicates from breaking our state - def handle_control(pkt) - src_call, dst_call, tstamp, out_seq, inp_seq, itype = pkt.unpack('nnNCCC') + # Handling incoming control packets + # TODO: Enforce sequence order to prevent duplicates from breaking our state + def handle_control(pkt) + src_call, dst_call, tstamp, out_seq, inp_seq, itype = pkt.unpack('nnNCCC') - # Scrub the high bits out of the call IDs - src_call ^= 0x8000 if (src_call & 0x8000 != 0) - dst_call ^= 0x8000 if (dst_call & 0x8000 != 0) + # Scrub the high bits out of the call IDs + src_call ^= 0x8000 if (src_call & 0x8000 != 0) + dst_call ^= 0x8000 if (dst_call & 0x8000 != 0) - phdr = [ src_call, dst_call, tstamp, out_seq, inp_seq, itype ] + phdr = [ src_call, dst_call, tstamp, out_seq, inp_seq, itype ] - info = nil - stype = pkt[11,1].unpack("C")[0] - info = process_elements(pkt, 12) if [IAX_TYPE_IAX, IAX_TYPE_CONTROL].include?(itype) + info = nil + stype = pkt[11,1].unpack("C")[0] + info = process_elements(pkt, 12) if [IAX_TYPE_IAX, IAX_TYPE_CONTROL].include?(itype) - if dst_call != self.scall - dprint("Incoming packet to inactive call: #{dst_call} vs #{self.scall}: #{phdr.inspect} #{stype.inspect} #{info.inspect}") - return - end + if dst_call != self.scall + dprint("Incoming packet to inactive call: #{dst_call} vs #{self.scall}: #{phdr.inspect} #{stype.inspect} #{info.inspect}") + return + end - # Increment the received sequence number - self.iseq = (self.iseq + 1) & 0xff + # Increment the received sequence number + self.iseq = (self.iseq + 1) & 0xff - if self.state == :hangup - dprint("Packet received after hangup, replying with invalid") - self.client.send_invalid(self) - return - end + if self.state == :hangup + dprint("Packet received after hangup, replying with invalid") + self.client.send_invalid(self) + return + end - # Technically these all require an ACK reply - # NEW, HANGUP, REJECT, ACCEPT, PONG, AUTHREP, REGREL, REGACK, REGREJ, TXREL + # Technically these all require an ACK reply + # NEW, HANGUP, REJECT, ACCEPT, PONG, AUTHREP, REGREL, REGACK, REGREJ, TXREL - case itype - when IAX_TYPE_DTMF_BEGIN - self.dprint("DTMF BEG: #{pkt[11,1]}") - self.dtmf << pkt[11,1] + case itype + when IAX_TYPE_DTMF_BEGIN + self.dprint("DTMF BEG: #{pkt[11,1]}") + self.dtmf << pkt[11,1] - when IAX_TYPE_DTMF_END - self.dprint("DTMF END: #{pkt[11,1]}") + when IAX_TYPE_DTMF_END + self.dprint("DTMF END: #{pkt[11,1]}") - when IAX_TYPE_CONTROL - case stype - when IAX_CTRL_HANGUP - dprint("HANGUP") - self.client.send_ack(self) - self.state = :hangup + when IAX_TYPE_CONTROL + case stype + when IAX_CTRL_HANGUP + dprint("HANGUP") + self.client.send_ack(self) + self.state = :hangup - when IAX_CTRL_RINGING - dprint("RINGING") - self.client.send_ack(self) + when IAX_CTRL_RINGING + dprint("RINGING") + self.client.send_ack(self) - when IAX_CTRL_BUSY - dprint("BUSY") - self.busy = true - self.state = :hangup - self.client.send_ack(self) + when IAX_CTRL_BUSY + dprint("BUSY") + self.busy = true + self.state = :hangup + self.client.send_ack(self) - when IAX_CTRL_ANSWER - dprint("ANSWER") - if self.state == :ringing - self.state = :answered - self.ring_finish = ::Time.now.to_i - end - self.client.send_ack(self) + when IAX_CTRL_ANSWER + dprint("ANSWER") + if self.state == :ringing + self.state = :answered + self.ring_finish = ::Time.now.to_i + end + self.client.send_ack(self) - when IAX_CTRL_PROGRESS - dprint("PROGRESS") + when IAX_CTRL_PROGRESS + dprint("PROGRESS") - when IAX_CTRL_PROCEED - dprint("PROCEED") + when IAX_CTRL_PROCEED + dprint("PROCEED") - when 255 - dprint("STOP SOUNDS") - end - # Acknowledge all control packets - # self.client.send_ack(self) + when 255 + dprint("STOP SOUNDS") + end + # Acknowledge all control packets + # self.client.send_ack(self) - when IAX_TYPE_IAX + when IAX_TYPE_IAX - dprint( ["RECV", phdr, stype, info].inspect ) - case stype - when IAX_SUBTYPE_HANGUP - self.state = :hangup - self.client.send_ack(self) - when IAX_SUBTYPE_LAGRQ - # Lagrps echo the timestamp - self.client.send_lagrp(self, tstamp) - when IAX_SUBTYPE_ACK - # Nothing to do here - when IAX_SUBTYPE_PING - # Pongs echo the timestamp - self.client.send_pong(self, tstamp) - when IAX_SUBTYPE_PONG - self.client.send_ack(self) - else - dprint( ["RECV-QUEUE", phdr, stype, info].inspect ) - self.queue.push( [phdr, stype, info ] ) - end + dprint( ["RECV", phdr, stype, info].inspect ) + case stype + when IAX_SUBTYPE_HANGUP + self.state = :hangup + self.client.send_ack(self) + when IAX_SUBTYPE_LAGRQ + # Lagrps echo the timestamp + self.client.send_lagrp(self, tstamp) + when IAX_SUBTYPE_ACK + # Nothing to do here + when IAX_SUBTYPE_PING + # Pongs echo the timestamp + self.client.send_pong(self, tstamp) + when IAX_SUBTYPE_PONG + self.client.send_ack(self) + else + dprint( ["RECV-QUEUE", phdr, stype, info].inspect ) + self.queue.push( [phdr, stype, info ] ) + end - when IAX_TYPE_VOICE - v_codec = stype - if self.state == :answered - handle_audio(pkt) - end - self.client.send_ack(self) + when IAX_TYPE_VOICE + v_codec = stype + if self.state == :answered + handle_audio(pkt) + end + self.client.send_ack(self) - when nil - dprint("Invalid control packet: #{pkt.unpack("H*")[0]}") - end - end + when nil + dprint("Invalid control packet: #{pkt.unpack("H*")[0]}") + end + end - # Encoded audio from the client - def handle_audio(pkt) - # Ignore audio received before the call is answered (ring ring) - return if self.state != :answered + # Encoded audio from the client + def handle_audio(pkt) + # Ignore audio received before the call is answered (ring ring) + return if self.state != :answered - # Extract the data from the packet (full or mini) - data = audio_packet_data(pkt) + # Extract the data from the packet (full or mini) + data = audio_packet_data(pkt) - # Decode the data into linear PCM frames - buff = decode_audio_frame(data) + # Decode the data into linear PCM frames + buff = decode_audio_frame(data) - # Call the caller-provided hook if its exists - if self.audio_hook - self.audio_buff(buff) - # Otherwise append the frame to the buffer - else - self.audio_buff << buff - end - end + # Call the caller-provided hook if its exists + if self.audio_hook + self.audio_buff(buff) + # Otherwise append the frame to the buffer + else + self.audio_buff << buff + end + end - def each_audio_frame(&block) - self.audio_buff.each do |frame| - block.call(frame) - end - end + def each_audio_frame(&block) + self.audio_buff.each do |frame| + block.call(frame) + end + end - def decode_audio_frame(buff) - case self.codec + def decode_audio_frame(buff) + case self.codec - # Convert u-law into signed PCM - when IAX_CODEC_G711_MULAW - Rex::Proto::IAX2::Codecs::MuLaw.decode(buff) + # Convert u-law into signed PCM + when IAX_CODEC_G711_MULAW + Rex::Proto::IAX2::Codecs::MuLaw.decode(buff) - # Convert a-law into signed PCM - when IAX_CODEC_G711_ALAW - Rex::Proto::IAX2::Codecs::ALaw.decode(buff) + # Convert a-law into signed PCM + when IAX_CODEC_G711_ALAW + Rex::Proto::IAX2::Codecs::ALaw.decode(buff) - # Linear little-endian signed PCM is our native format - when IAX_CODEC_LINEAR_PCM - buff + # Linear little-endian signed PCM is our native format + when IAX_CODEC_LINEAR_PCM + buff - # Unsupported codec, return empty - else - dprint("UNKNOWN CODEC: #{self.codec.inspect}") - '' - end - end + # Unsupported codec, return empty + else + dprint("UNKNOWN CODEC: #{self.codec.inspect}") + '' + end + end - def audio_packet_data(pkt) - (pkt[0,1].unpack("C")[0] & 0x80 == 0) ? pkt[4,pkt.length-4] : pkt[12,pkt.length-12] - end + def audio_packet_data(pkt) + (pkt[0,1].unpack("C")[0] & 0x80 == 0) ? pkt[4,pkt.length-4] : pkt[12,pkt.length-12] + end end end end end