From 3320bc9f0dd2497f17b468aa624e6814b4ee4935 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Fri, 30 Sep 2005 05:43:06 +0000 Subject: [PATCH] integrated switch board routing singleton git-svn-id: file:///home/svn/incoming/trunk@2917 4d416f70-5f16-0410-b530-b9f4589650da --- lib/rex/socket.rb | 26 ++++ lib/rex/socket.rb.ut.rb | 15 ++ lib/rex/socket/switch_board.rb | 219 +++++++++++++++++++++++++++ lib/rex/socket/switch_board.rb.ut.rb | 41 +++++ lib/rex/sync/thread_safe.rb | 3 + 5 files changed, 304 insertions(+) create mode 100644 lib/rex/socket/switch_board.rb create mode 100644 lib/rex/socket/switch_board.rb.ut.rb diff --git a/lib/rex/socket.rb b/lib/rex/socket.rb index f2e781f826..7e60a0f98f 100644 --- a/lib/rex/socket.rb +++ b/lib/rex/socket.rb @@ -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 diff --git a/lib/rex/socket.rb.ut.rb b/lib/rex/socket.rb.ut.rb index faaefc0cde..e2bb01aaff 100644 --- a/lib/rex/socket.rb.ut.rb +++ b/lib/rex/socket.rb.ut.rb @@ -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 diff --git a/lib/rex/socket/switch_board.rb b/lib/rex/socket/switch_board.rb new file mode 100644 index 0000000000..addeb2c380 --- /dev/null +++ b/lib/rex/socket/switch_board.rb @@ -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 diff --git a/lib/rex/socket/switch_board.rb.ut.rb b/lib/rex/socket/switch_board.rb.ut.rb new file mode 100644 index 0000000000..dc38d5c27b --- /dev/null +++ b/lib/rex/socket/switch_board.rb.ut.rb @@ -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 diff --git a/lib/rex/sync/thread_safe.rb b/lib/rex/sync/thread_safe.rb index 3ca4d7c45e..1a1f750a83 100644 --- a/lib/rex/sync/thread_safe.rb +++ b/lib/rex/sync/thread_safe.rb @@ -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 #