module Rex::IO::StreamServer

This mixin provides the framework and interface for implementing a streaming server that can listen for and accept stream client connections. Stream servers extend this class and are required to implement the following methods:

accept
fd

Attributes

client_waiter[RW]
on_client_close_proc[RW]

This callback procedure can be set and will be called when a client disconnects from the server.

on_client_connect_proc[RW]

This callback procedure can be set and will be called when new clients connect.

on_client_data_proc[RW]

This callback procedure can be set and will be called when clients have data to be processed.

Public Instance Methods

close_client(client) click to toggle source

This method closes a client connection and cleans up the resources associated with it.

# File lib/rex/io/stream_server.rb, line 92
def close_client(client)
  if (client)
    clients.delete(client)

    begin
      client.close
    rescue IOError
    end
  end
end
on_client_close(client) click to toggle source

This callback is notified when a client connection has closed.

# File lib/rex/io/stream_server.rb, line 54
def on_client_close(client)
  if (on_client_close_proc)
    on_client_close_proc.call(client)
  end
end
on_client_connect(client) click to toggle source

This callback is notified when a client connects.

# File lib/rex/io/stream_server.rb, line 35
def on_client_connect(client)
  if (on_client_connect_proc)
    on_client_connect_proc.call(client)
  end
end
on_client_data(client) click to toggle source

This callback is notified when a client connection has data that needs to be processed.

# File lib/rex/io/stream_server.rb, line 45
def on_client_data(client)
  if (on_client_data_proc)
    on_client_data_proc.call(client)
  end
end
start() click to toggle source

Start monitoring the listener socket for connections and keep track of all client connections.

# File lib/rex/io/stream_server.rb, line 64
def start
  self.clients = []
  self.client_waiter = ::Queue.new

  self.listener_thread = Rex::ThreadFactory.spawn("StreamServerListener", false) {
    monitor_listener
  }
  self.clients_thread = Rex::ThreadFactory.spawn("StreamServerClientMonitor", false) {
    monitor_clients
  }
end
stop() click to toggle source

Terminates the listener monitoring threads and closes all active clients.

# File lib/rex/io/stream_server.rb, line 79
def stop
  self.listener_thread.kill
  self.clients_thread.kill

  self.clients.each { |cli|
    close_client(cli)
  }
end
wait() click to toggle source

This method waits on the server listener thread

# File lib/rex/io/stream_server.rb, line 106
def wait
  self.listener_thread.join if self.listener_thread
end

Protected Instance Methods

monitor_clients() click to toggle source

This method monitors client connections for data and calls the on_client_data routine when new data arrives.

# File lib/rex/io/stream_server.rb, line 178
def monitor_clients
  begin

    # Wait for a notify if our client list is empty
    if (clients.length == 0)
      self.client_waiter.pop
      next
    end

    sd = Rex::ThreadSafe.select(clients, nil, nil, nil)

    sd[0].each { |cfd|
      begin
        on_client_data(cfd)
      rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
        on_client_close(cfd)
        close_client(cfd)
      rescue ::Interrupt
        raise $!
      rescue ::Exception
        close_client(cfd)
        elog("Error in stream server client monitor: #{$!}")
        rlog(ExceptionCallStack)

      end
    }

  rescue ::Rex::StreamClosedError => e
    # Remove the closed stream from the list
    clients.delete(e.stream)
  rescue ::Interrupt
    raise $!
  rescue ::Exception
    elog("Error in stream server client monitor: #{$!}")
    rlog(ExceptionCallStack)
  end while true
end
monitor_listener() click to toggle source

This method monitors the listener socket for new connections and calls the on_client_connect callback routine.

# File lib/rex/io/stream_server.rb, line 142
def monitor_listener

  while true
    begin
      cli = accept
      if not cli
        elog("The accept() returned nil in stream server listener monitor:  #{fd.inspect}")
        ::IO.select(nil, nil, nil, 0.10)
        next
      end

      # Append to the list of clients
      self.clients << cli

      # Initialize the connection processing
      on_client_connect(cli)

      # Notify the client monitor
      self.client_waiter.push(cli)

    # Skip exceptions caused by accept() [ SSL ]
    rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
    rescue ::Interrupt
      raise $!
    rescue ::Exception
      elog("Error in stream server server monitor: #{$!}")
      rlog(ExceptionCallStack)
      break
    end
  end
end