metasploit-framework/lib/telephony/modem.rb

182 lines
4.0 KiB
Ruby

module Telephony
class Modem
attr_accessor :serialport, :sp, :sock
attr_accessor :baud, :data_bits, :parity, :stop_bits
attr_accessor :flowcontrol
attr_accessor :display
attr_reader :commandstate
NONE = SerialPort::NONE
HARD = SerialPort::HARD
SOFT = SerialPort::SOFT
SPACE = SerialPort::SPACE
MARK = SerialPort::MARK
EVEN = SerialPort::EVEN
ODD = SerialPort::ODD
def initialize(serialport = '/dev/ttyS0')
@serialport = serialport || '/dev/ttyS0'
@sp = nil
@baud = 2400
@data_bits = 8
@parity = NONE
@stop_bits = 1
@flowcontrol = NONE
@commandstate = true
@display = true
# Connect to and init modem
begin
#print("Opening Serial Port #{@serialport} (#{@baud} #{@data_bits}#{@parity}#{@stop_bits})\r\n")
@sp = SerialPort.create(serialport)
@sp.modem_params = {'baud' => @baud, 'data_bits' => @data_bits, 'parity' => @parity, 'stop_bits' => @stop_bits}
@sp.read_timeout = -1
@sp.rts = 1
@sp.dtr = 1 if sp.respond_to?:dtr= # unsupported in windows ):
@sock = @sp
@sock.extend(Rex::IO::Stream)
@sock.extend(Rex::IO::StreamAbstraction::Ext)
rescue ::Interrupt
raise $!
rescue ::Exception => e
print("Error opening serial port #{@serialport} : #{e.class} #{e} #{e.backtrace}\r\n")
return true
end
end
# This provides pass-through method support for the SerialPort object
def method_missing(meth, *args); self.sp.send(meth, *args); end
def put_command(command, timeout)
switchback = false
if ! @commandstate
commandstate
switchback = true
end
begin
self.flush # TODO: This doesn't work in exploits for some reason but it does in aux modules
@sp.puts command + "\r\n"
echo = get_response(timeout) # read back the echoed command (not really a response)
rescue ::Interrupt
raise $!
rescue ::Exception => e
print("Error sending command to modem: #{e.class} #{e} #{e.backtrace}\r\n")
return
end
result = get_response(timeout)
datastate if switchback == true
return result
end
def get_response(timeout)
time = Time.now
@sp.read_timeout = -1
result = ''
while Time.now <= time + timeout
# get a char from the modem
begin
c = @sp.getc
if c
c = c.chr
result += c
if c == "\n"
result = result.chomp
break if result.length > 0
end
end
rescue ::Interrupt
raise $!
rescue ::Exception => e
print("Error reading from modem: #{e.class} #{e} #{e.backtrace}\r\n")
return
end
end
if result.length > 0
print "[m] #{result}\r\n" if @display
else
result = 'TIMEOUT'
end
return result
end
def commandstate
if ! @commandstate
@sp.break 10 # tenths of a second
@sp.puts '+++'
@sp.break 10 # tenths of a second
result = get_response(3)
if result != 'OK'
print( "Error switching to command state: FAILED\r\n" )
return false
else
@commandstate = true
end
end
return true
end
def datastate
if @commandstate
result = put_command('ATO0', 3)
if result =~ /CONNECT/i
@commandstate = false
else
print( "Error switching to data state: FAILED\r\n" )
return false
end
end
return true
end
def hangup
flush
if @commandstate == true
#print( "Hanging up... (commandstate ATH0)\r\n" )
result = put_command('ATH0', 3)
return true if result == 'OK' or result == 'NO CARRIER'
else
if @sp.respond_to?:dtr= # unsupported in windows ):
#print( "Hanging up... (DTR = 0)\r\n" )
@sp.dtr = 0
sleep 0.75
@sp.dtr = 1
result = get_response(3)
@commandstate = true if result == 'NO CARRIER'
return true
else
#print( "Hanging up... (datastate ATH0)\r\n" )
commandstate
result = put_command('ATH0', 3)
return true if result == 'OK'
end
end
return false
end
# flush any stale data in the read buffer
def flush
@sp.read_timeout = -1
while @sp.getc; end
end
# TODO: confert all calls to Modem.params to Modem.modem_params and remove this def
def params=(params)
@sp.modem_params = params
end
end
end