Parent

Included Modules

Class/Module Index [+]

Quicksearch

Rex::Services::LocalRelay

This service acts as a local TCP relay whereby clients can connect to a local listener that forwards to an arbitrary remote endpoint. Interaction with the remote endpoint socket requires that it implement the Rex::IO::Stream interface.

Attributes

relay_mutex[RW]
relay_thread[RW]
relays[RW]
rfds[RW]

Public Class Methods

hardcore_alias(*args) click to toggle source

Returns the hardcore alias for the local relay service.

# File lib/rex/services/local_relay.rb, line 128
def self.hardcore_alias(*args)
        "__#{args}"
end
new() click to toggle source

Initializes the local tcp relay monitor.

# File lib/rex/services/local_relay.rb, line 112
def initialize
        self.relays       = Hash.new
        self.rfds         = Array.new
        self.relay_thread = nil
        self.relay_mutex  = Mutex.new
end

Public Instance Methods

alias() click to toggle source

Returns the alias for this service.

# File lib/rex/services/local_relay.rb, line 135
def alias
        super || "Local Relay"
end
each_tcp_relay(&block) click to toggle source

Enumerate each TCP relay

# File lib/rex/services/local_relay.rb, line 250
def each_tcp_relay(&block)
        self.relays.each_pair { |name, relay|
                next if (relay.opts['__RelayType'] != 'tcp')

                yield(
                        relay.opts['LocalHost'] || '0.0.0.0',
                        relay.opts['LocalPort'],
                        relay.opts['PeerHost'],
                        relay.opts['PeerPort'],
                        relay.opts)
        }
end
start() click to toggle source

Starts the thread that monitors the local relays.

# File lib/rex/services/local_relay.rb, line 142
def start
        if (!self.relay_thread)
                self.relay_thread = Rex::ThreadFactory.spawn("LocalRelay", false) {
                        begin
                                monitor_relays
                        rescue ::Exception
                                elog("Error in #{self} monitor_relays: #{$!}", 'rex')
                        end
                }
        end
end
start_relay(stream_server, name, opts = {}) click to toggle source

Starts a local relay on the supplied local port. This listener will call the supplied callback procedures when various events occur.

# File lib/rex/services/local_relay.rb, line 206
def start_relay(stream_server, name, opts = {})
        # Create a Relay instance with the local stream and remote stream
        relay = Relay.new(name, stream_server, opts)

        # Extend the stream_server so that we can associate it with this relay
        stream_server.extend(StreamServer)
        stream_server.relay = relay

        # Add the stream associations the appropriate lists and hashes
        self.relay_mutex.synchronize {
                self.relays[name] = relay

                self.rfds << stream_server
        }
end
start_tcp_relay(lport, opts = {}) click to toggle source

Starts a local TCP relay.

# File lib/rex/services/local_relay.rb, line 186
def start_tcp_relay(lport, opts = {})
        # Make sure our options are valid
        if ((opts['PeerHost'] == nil or opts['PeerPort'] == nil) and (opts['Stream'] != true))
                raise ArgumentError, "Missing peer host or peer port.", caller
        end

        listener = Rex::Socket.create_tcp_server(
                'LocalHost' => opts['LocalHost'],
                'LocalPort' => lport)

        opts['LocalPort']   = lport
        opts['__RelayType'] = 'tcp'

        start_relay(listener, lport.to_s + (opts['LocalHost'] || '0.0.0.0'), opts)
end
stop() click to toggle source

Stops the thread that monitors the local relays and destroys all local listeners.

# File lib/rex/services/local_relay.rb, line 158
def stop
        if (self.relay_thread)
                self.relay_thread.kill
                self.relay_thread = nil
        end

        self.relay_mutex.synchronize {
                self.relays.delete_if { |k, v|
                        v.shutdown
                        v.close
                        true
                }
        }

        # Flush the relay list and read fd list
        self.relays.clear
        self.rfds.clear
end
stop_relay(name) click to toggle source

Stops a relay with a given name.

# File lib/rex/services/local_relay.rb, line 232
def stop_relay(name)
        rv = false

        self.relay_mutex.synchronize {
                relay = self.relays[name]

                if (relay)
                        close_relay(relay)
                        rv = true
                end
        }

        rv
end
stop_tcp_relay(lport, lhost = nil) click to toggle source

Stops relaying on a given local port.

# File lib/rex/services/local_relay.rb, line 225
def stop_tcp_relay(lport, lhost = nil)
        stop_relay(lport.to_s + (lhost || '0.0.0.0'))
end

Protected Instance Methods

accept_relay_conn(srvfd) click to toggle source

Accepts a client connection on a local relay.

# File lib/rex/services/local_relay.rb, line 320
def accept_relay_conn(srvfd)
        relay = srvfd.relay

        begin
                dlog("Accepting relay client connection...", 'rex', LEV_3)

                # Accept the child connection
                lfd = srvfd.accept
                dlog("Got left side of relay: #{lfd}", 'rex', LEV_3)

                # Call the relay's on_local_connection method which should return a
                # remote connection on success
                rfd = srvfd.on_local_connection(relay, lfd)

                dlog("Got right side of relay: #{rfd}", 'rex', LEV_3)
        rescue
                wlog("Failed to get remote half of local connection on relay #{relay.name}: #{$!}", 'rex')
                lfd.close
                return
        end

        # If we have both sides, then we rock.  Extend the instances, associate
        # them with the relay, associate them with each other, and add them to
        # the list of polling file descriptors
        if (lfd and rfd)
                lfd.extend(Stream)
                rfd.extend(Stream)

                lfd.relay = relay
                rfd.relay = relay

                lfd.other_stream = rfd
                rfd.other_stream = lfd

                self.rfds << lfd
                self.rfds << rfd

        # Otherwise, we don't have both sides, we'll close them.
        else
                close_relay_conn(lfd)
        end
end
close_relay(relay) click to toggle source

Closes an cleans up a specific relay

# File lib/rex/services/local_relay.rb, line 271
def close_relay(relay)
        self.rfds.delete(relay.listener)
        self.relays.delete(relay.name)

        begin
                relay.shutdown
                relay.close
        rescue IOError
        end
end
close_relay_conn(fd) click to toggle source

Closes a specific relay connection without tearing down the actual relay itself.

# File lib/rex/services/local_relay.rb, line 286
def close_relay_conn(fd)
        relay = fd.relay
        ofd   = fd.other_stream

        self.rfds.delete(fd)

        begin
                if (relay.on_conn_close_proc)
                        relay.on_conn_close_proc.call(fd)
                end

                fd.shutdown
                fd.close
        rescue IOError
        end

        if (ofd)
                self.rfds.delete(ofd)

                begin
                        if (relay.on_conn_close_proc)
                                relay.on_conn_close_proc.call(ofd)
                        end

                        ofd.shutdown
                        ofd.close
                rescue IOError
                end
        end
end
monitor_relays() click to toggle source

Monitors the relays for data and passes it in both directions.

# File lib/rex/services/local_relay.rb, line 366
def monitor_relays
        begin
                # Helps with latency
                Thread.current.priority = 2

                # Poll all the streams...
                begin
                        socks = Rex::ThreadSafe.select(rfds, nil, nil, 0.25)
                rescue StreamClosedError => e
                        dlog("monitor_relays: closing stream #{e.stream}", 'rex', LEV_3)

                        # Close the relay connection that is associated with the stream
                        # closed error
                        if (e.stream.kind_of?(Stream))
                                close_relay_conn(e.stream)
                        end

                        dlog("monitor_relays: closed stream #{e.stream}", 'rex', LEV_3)

                        next
                rescue
                        elog("Error in #{self} monitor_relays select: #{$!.class} #{$!}", 'rex')
                        return
                end

                # If socks is nil, go again.
                next unless socks

                # Process read-ready file descriptors, if any.
                socks[0].each { |rfd|

                        # If this file descriptor is a server, accept the connection
                        if (rfd.kind_of?(StreamServer))
                                accept_relay_conn(rfd)
                        # Otherwise, it's a relay connection, read data from one side
                        # and write it to the other
                        else
                                begin
                                        # Pass the data onto the other fd, most likely writing it.
                                        data = rfd.sysread(65536)
                                        rfd.other_stream.on_other_data(data)
                                # If we catch an error, close the connection
                                rescue ::Exception
                                        elog("Error in #{self} monitor_relays read: #{$!}", 'rex')
                                        close_relay_conn(rfd)
                                end
                        end

                } if (socks[0])

        end while true
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.