integrated switch board routing singleton

git-svn-id: file:///home/svn/incoming/trunk@2917 4d416f70-5f16-0410-b530-b9f4589650da
unstable
Matt Miller 2005-09-30 05:43:06 +00:00
parent 6ec3700b16
commit 3320bc9f0d
5 changed files with 304 additions and 0 deletions

View File

@ -85,6 +85,32 @@ module Socket
return to_sockaddr(host, 0)[4,4]
end
def self.resolv_nbo_i(host)
return resolv_nbo(host).unpack('N')[0]
end
#
# Converts a netmask (255.255.255.240) into a bitmask (28). This is the
# lame kid way of doing it.
#
def self.net2bitmask(netmask)
raw = resolv_nbo(netmask).unpack('N')[0]
0.upto(31) { |bit|
p = 2 ** bit
return (32 - bit) if ((raw & p) == p)
}
0
end
#
# Converts a bitmask (28) into a netmask (255.255.255.240)
#
def self.bit2netmask(bitmask)
[ (~((2 ** (32 - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
end
##
#
# Class initialization

View File

@ -58,4 +58,19 @@ class Rex::Socket::UnitTest < Test::Unit::TestCase
assert_equal("\x04\x03\x02\x01", Rex::Socket.resolv_nbo("4.3.2.1"))
end
def test_net2bitmask
assert_equal(32, Rex::Socket.net2bitmask('255.255.255.255'))
assert_equal(28, Rex::Socket.net2bitmask('255.255.255.240'))
assert_equal(24, Rex::Socket.net2bitmask('255.255.255.0'))
assert_equal(16, Rex::Socket.net2bitmask('255.255.0.0'))
end
def test_bit2netmask
assert_equal("255.255.255.255", Rex::Socket.bit2netmask(32))
assert_equal("255.255.255.254", Rex::Socket.bit2netmask(31))
assert_equal("255.255.255.240", Rex::Socket.bit2netmask(28))
assert_equal("255.255.255.0", Rex::Socket.bit2netmask(24))
assert_equal("255.255.0.0", Rex::Socket.bit2netmask(16))
end
end

View File

@ -0,0 +1,219 @@
require 'singleton'
require 'thread'
require 'rex'
require 'rex/socket'
module Rex
module Socket
###
#
# SwitchBoard
# -----------
#
# This class provides a global routing table that associates subnets with Comm
# classes. Comm classes are used to instantiate objects that are tied to
# remote network entities. For example, the Local Comm class is used to
# building network connections directly from the local machine whereas, for
# instance, a Meterpreter Comm would build a local socket pair that is
# associated with a connection established by a remote entity. This can be
# seen as a uniform way of communicating with hosts through arbitrary
# channels.
#
###
class SwitchBoard
include Singleton
include Enumerable
###
#
# Route
# -----
#
# This class represents a logical switch board route.
#
###
class Route
def initialize(subnet, netmask, comm)
self.subnet = subnet
self.netmask = netmask
self.comm = comm
self.subnet_nbo = Socket.resolv_nbo_i(subnet)
self.netmask_nbo = Socket.resolv_nbo_i(netmask)
end
#
# Sort according to bitmask
#
def <=>(other)
self.bitmask <=> other.bitmask
end
#
# Convert the netmask to a bitmask and cache it.
#
def bitmask
@_bitmask = Socket.net2bitmask(self.netmask) if (@_bitmask == nil)
@_bitmask
end
attr_reader :subnet, :netmask, :comm
attr_reader :subnet_nbo, :netmask_nbo
protected
attr_writer :subnet, :netmask, :comm
attr_writer :subnet_nbo, :netmask_nbo
end
##
#
# Class method wrappers
#
##
def self.add_route(subnet, mask, comm)
self.instance.add_route(subnet, mask, comm)
end
def self.remove_route(subnet, mask, comm)
self.instance.remove_route(subnet, mask, comm)
end
def self.flush_routes
self.instance.flush_routes
end
def self.each(&block)
self.instance.each(&block)
end
def self.routes
self.instance.routes
end
def self.best_comm(addr)
self.instance.best_comm(addr)
end
##
#
# Instance methods
#
##
#
# Adds a route for a given subnet and netmask destined through a given comm
# instance.
#
def add_route(subnet, mask, comm)
# If a bitmask was supplied, convert it.
netmask = (mask.to_s =~ /^\d+$/) ? Rex::Socket.bit2netmask(mask.to_i) : mask
rv = true
_init
mutex.synchronize {
# If the route already exists, return false to the caller.
if (route_exists?(subnet, netmask) == false)
self.routes << Route.new(subnet, netmask, comm)
else
rv = false
end
}
rv
end
#
# Removes a route for a given subnet and netmask destined through a given
# comm instance.
#
def remove_route(subnet, mask, comm)
# If a bitmask was supplied, convert it.
netmask = (mask.to_s =~ /^\d+$/) ? Rex::Socket.bit2netmask(mask.to_i) : mask
rv = false
_init
mutex.synchronize {
self.routes.delete_if { |route|
if (route.subnet == subnet and route.netmask == netmask and route.comm == comm)
rv = true
else
false
end
}
}
rv
end
#
# Flushes all established routes.
#
def flush_routes
_init
self.routes = Array.new
end
#
# Checks to see if a route already exists for the supplied subnet and
# netmask.
#
def route_exists?(subnet, netmask)
each { |route|
return true if (route.subnet == subnet and route.netmask == netmask)
}
false
end
#
# Enumerates each entry in the routing table.
#
def each(&block)
_init
routes.each(&block)
end
#
# Finds the best possible comm for the supplied target address.
#
def best_comm(addr)
addr_nbo = Socket.resolv_nbo_i(addr)
comm = nil
msb = 0
each { |route|
if ((route.subnet_nbo & route.netmask_nbo) ==
(addr_nbo & route.netmask_nbo))
if (route.bitmask >= msb)
comm = route.comm
msb = route.bitmask
end
end
}
comm
end
attr_reader :routes, :mutex
protected
attr_writer :routes, :mutex
def _init
if (@_initialized != true)
@_initialized = true
self.routes = Array.new
self.mutex = Mutex.new
end
end
end
end
end

View File

@ -0,0 +1,41 @@
#!/usr/bin/ruby
$:.unshift(File.join(File.dirname(__FILE__), '..', '..'))
require 'test/unit'
require 'rex/socket/switch_board'
class Rex::Socket::SwitchBoard::UnitTest < Test::Unit::TestCase
Klass = Rex::Socket::SwitchBoard
def test_add
Klass.flush_routes
assert_equal(true, Klass.add_route('0.0.0.0', 0, 'foo'))
assert_equal(false, Klass.add_route('0.0.0.0', 0, 'foo'))
assert_equal(1, Klass.routes.length)
assert_equal('0.0.0.0', Klass.routes[0].subnet)
assert_equal('0.0.0.0', Klass.routes[0].netmask)
assert_equal(0, Klass.routes[0].bitmask)
assert_equal('foo', Klass.routes[0].comm)
end
def test_remove
Klass.flush_routes
assert_equal(true, Klass.add_route('0.0.0.0', 0, 'foo'))
assert_equal(true, Klass.remove_route('0.0.0.0', 0, 'foo'))
assert_equal(false, Klass.remove_route('0.0.0.0', 0, 'foo'))
assert_equal(0, Klass.routes.length)
end
def test_best_comm
Klass.flush_routes
Klass.add_route('0.0.0.0', 0, 'default')
Klass.add_route('1.2.3.0', 24, 'spec')
assert_equal('default', Klass.best_comm('4.5.6.7'))
assert_equal('spec', Klass.best_comm('1.2.3.7'))
end
end

View File

@ -62,6 +62,9 @@ module ThreadSafe
# Keep chugging until we run out of time, if time was supplied.
end while ((left == nil) or (left > 0))
# Nothin.
nil
end
#