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