commit b3aa2fb6fbf2159f56b1bab64fa2d9c1ba238947 Author: icodemachine gauthamnekk@gmail.com Date: Mon Feb 16 22:17:40 2015 +0530
Added Windows Resolver support for get_connections as well as pid_by_name --- stem/util/connection.py | 11 ++++++++--- stem/util/system.py | 13 ++++++++++--- test/integ/util/system.py | 23 +++++++++++++++++++---- test/unit/util/connection.py | 36 +++++++++++++++++++++++++++++++----- test/unit/util/system.py | 26 +++++++++++++++++++++++++- 5 files changed, 93 insertions(+), 16 deletions(-)
diff --git a/stem/util/connection.py b/stem/util/connection.py index 70ba609..93e137a 100644 --- a/stem/util/connection.py +++ b/stem/util/connection.py @@ -64,7 +64,7 @@ LOG_CONNECTION_RESOLUTION = False Resolver = enum.Enum( ('PROC', 'proc'), ('NETSTAT', 'netstat'), - ('NETSTAT_WINDOWS', 'netstat'), + ('NETSTAT_WINDOWS', 'netstat (windows)'), ('SS', 'ss'), ('LSOF', 'lsof'), ('SOCKSTAT', 'sockstat'), @@ -187,12 +187,17 @@ def get_connections(resolver, process_pid = None, process_name = None): return [Connection(*conn) for conn in stem.util.proc.connections(process_pid)]
resolver_command = RESOLVER_COMMAND[resolver].format(pid = process_pid) - + + #In case, process_name is only specified + if resolver == Resolver.NETSTAT_WINDOWS: + if not process_pid and process_name: + process_pid = stem.util.system.pid_by_name(process_name)[0] + try: results = stem.util.system.call(resolver_command) except OSError as exc: raise IOError("Unable to query '%s': %s" % (resolver_command, exc)) - + resolver_regex_str = RESOLVER_FILTER[resolver].format( protocol = '(?P<protocol>\S+)', local_address = '(?P<local_address>[0-9.]+)', diff --git a/stem/util/system.py b/stem/util/system.py index ea8d8a1..98b64f7 100644 --- a/stem/util/system.py +++ b/stem/util/system.py @@ -454,6 +454,9 @@ def pid_by_name(process_name, multiple = False): tasklist_regex_str = '^\s*' + process_name + '\s+(?P<pid>[0-9]*)' tasklist_regex = re.compile(tasklist_regex_str) + if not results: + raise IOError("No results found for tasklist") + for line in results: match = tasklist_regex.search(line) if match: @@ -461,13 +464,17 @@ def pid_by_name(process_name, multiple = False): id = int(attr['pid']) process_ids.append(id) - return process_ids + if process_ids == []: + raise IOError("Process Name not Found : %s" % process_name) + + if multiple: + return process_ids + elif len(process_ids) > 0: + return process_ids[0] except OSError as exc: log.debug("failed to query '%s': %s" % (command, exc)) raise IOError("Unable to query '%s': %s" % (command, exc)) - -
log.debug("failed to resolve a pid for '%s'" % process_name) diff --git a/test/integ/util/system.py b/test/integ/util/system.py index 1ffe7cd..3971e61 100644 --- a/test/integ/util/system.py +++ b/test/integ/util/system.py @@ -79,10 +79,7 @@ class TestSystem(unittest.TestCase): will fail if there's other tor instances running. """
- if stem.util.system.is_windows(): - test.runner.skip(self, '(unavailable on windows)') - return - elif self._is_extra_tor_running(): + if self._is_extra_tor_running(): test.runner.skip(self, '(multiple tor instances)') return
@@ -218,6 +215,24 @@ class TestSystem(unittest.TestCase):
if len(all_tor_pids) == 1: self.assertEqual(our_tor_pid, all_tor_pids[0]) + + def test_pid_by_name_tasklist(self): + """ + Tests the pid_by_name function with a tasklist response. + """ + + runner = test.runner.get_runner() + if self._is_extra_tor_running(): + test.runner.skip(self, '(multiple tor instances)') + return + elif not stem.util.system.is_available('tasklist'): + test.runner.skip(self, '(tasklist unavailable)') + return + + tor_pid = test.runner.get_runner().get_pid() + tor_cmd = test.runner.get_runner().get_tor_command(True) + self.assertEqual(tor_pid, stem.util.system.pid_by_name(tor_cmd)) +
def test_pid_by_port(self): """ diff --git a/test/unit/util/connection.py b/test/unit/util/connection.py index 731fa5b..233cb29 100644 --- a/test/unit/util/connection.py +++ b/test/unit/util/connection.py @@ -32,6 +32,18 @@ unix 3 [ ] STREAM CONNECTED 34164276 15843/tor unix 3 [ ] STREAM CONNECTED 7951 - """
+NETSTAT_WINDOWS_OUTPUT = """\ +Active Connections + + Proto Local Address Foreign Address State PID + TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 852 + TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4 + TCP 0.0.0.0:902 0.0.0.0:0 LISTENING 2076 + TCP 0.0.0.0:912 0.0.0.0:0 LISTENING 2076 + TCP 192.168.0.1:44284 38.229.79.2:443 ESTABLISHED 15843 + TCP 0.0.0.0:37782 0.0.0.0:0 LISTENING 4128 +""" + SS_OUTPUT = """\ Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp CLOSE-WAIT 1 0 192.168.0.1:43780 53.203.145.45:443 users:(("firefox",20586,118)) @@ -105,19 +117,17 @@ BSD_PROCSTAT_OUTPUT = """\
class TestConnection(unittest.TestCase): - @patch('os.access') @patch('stem.util.system.is_available') @patch('stem.util.proc.is_available') - def test_system_resolvers(self, proc_mock, is_available_mock, os_mock): + def test_system_resolvers(self, proc_mock, is_available_mock): """ Checks the system_resolvers function. """
is_available_mock.return_value = True proc_mock.return_value = False - os_mock.return_value = True
- self.assertEqual([], stem.util.connection.system_resolvers('Windows')) + self.assertEqual([Resolver.NETSTAT_WINDOWS], stem.util.connection.system_resolvers('Windows')) self.assertEqual([Resolver.LSOF], stem.util.connection.system_resolvers('Darwin')) self.assertEqual([Resolver.LSOF], stem.util.connection.system_resolvers('OpenBSD')) self.assertEqual([Resolver.BSD_SOCKSTAT, Resolver.BSD_PROCSTAT, Resolver.LSOF], stem.util.connection.system_resolvers('FreeBSD')) @@ -126,7 +136,7 @@ class TestConnection(unittest.TestCase): proc_mock.return_value = True self.assertEqual([Resolver.PROC, Resolver.NETSTAT, Resolver.SOCKSTAT, Resolver.LSOF, Resolver.SS], stem.util.connection.system_resolvers('Linux'))
- # check that calling without an argument is equivalent to calling for this + # check that calling without an argument is equivilant to calling for this # platform
self.assertEqual(stem.util.connection.system_resolvers(platform.system()), stem.util.connection.system_resolvers()) @@ -185,6 +195,22 @@ class TestConnection(unittest.TestCase): self.assertRaises(IOError, stem.util.connection.get_connections, Resolver.NETSTAT, process_pid = 1111)
@patch('stem.util.system.call') + def test_get_connections_by_windows_netstat(self, call_mock): + """ + Checks the get_connections function with the Windows netstat resolver. + """ + + call_mock.return_value = NETSTAT_WINDOWS_OUTPUT.split('\n') + expected = [Connection('192.168.0.1', 44284, '38.229.79.2', 443, 'tcp')] + self.assertEqual(expected, stem.util.connection.get_connections(Resolver.NETSTAT_WINDOWS, process_pid = 15843, process_name = 'tor')) + + #self.assertRaises(IOError, stem.util.connection.get_connections, Resolver.NETSTAT, process_pid = 15843, process_name = 'stuff') + self.assertRaises(IOError, stem.util.connection.get_connections, Resolver.NETSTAT_WINDOWS, process_pid = 1111, process_name = 'tor') + + call_mock.side_effect = OSError('Unable to call netstat') + self.assertRaises(IOError, stem.util.connection.get_connections, Resolver.NETSTAT_WINDOWS, process_pid = 1111) + + @patch('stem.util.system.call') def test_get_connections_by_ss(self, call_mock): """ Checks the get_connections function with the ss resolver. diff --git a/test/unit/util/system.py b/test/unit/util/system.py index 3b4f035..94bb7fa 100644 --- a/test/unit/util/system.py +++ b/test/unit/util/system.py @@ -45,6 +45,16 @@ GET_PID_BY_NAME_PS_BSD_MULTIPLE = [ ' 10 ?? Ss 0:09.97 kextd', ' 41 ?? Ss 9:00.22 launchd']
+ +GET_PID_BY_NAME_TASKLIST_RESULTS = [ + 'Image Name PID Session Name Session# Mem Usage', + 'System Idle Process 0 Services 0 20 K', + 'svchost.exe 872 Services 0 8,744 K', + 'hpservice.exe 1112 Services 0 3,828 K', + 'tor.exe 3712 Console 1 29,976 K', + 'tor.exe 3713 Console 1 21,976 K', + 'conhost.exe 3012 Console 1 4,652 K'] + GET_PID_BY_PORT_NETSTAT_RESULTS = [ 'Active Internet connections (only servers)', 'Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name', @@ -53,7 +63,7 @@ GET_PID_BY_PORT_NETSTAT_RESULTS = [ 'tcp6 0 0 ::1:631 :::* LISTEN - ', 'udp 0 0 0.0.0.0:5353 0.0.0.0:* - ', 'udp6 0 0 fe80::7ae4:ff:fe2f::123 :::* - '] - + GET_PID_BY_PORT_SOCKSTAT_RESULTS = [ '_tor tor 4397 7 tcp4 51.64.7.84:9051 *:*', '_tor tor 4397 12 tcp4 51.64.7.84:54011 80.3.121.7:9051', @@ -252,6 +262,20 @@ class TestSystem(unittest.TestCase):
@patch('stem.util.system.call') @patch('stem.util.system.is_available', Mock(return_value = True)) + def test_pid_by_name_tasklist(self, call_mock): + """ + Tests the pid_by_name function with tasklist responses. + """ + + call_mock.return_value = GET_PID_BY_NAME_TASKLIST_RESULTS + self.assertEqual(3712, system.pid_by_name('tor')) + self.assertEqual(None, system.pid_by_name('DirectoryService')) + self.assertEqual(None, system.pid_by_name('blarg')) + + self.assertEqual([3712, 3713], system.pid_by_name('tor', multiple = True)) + + @patch('stem.util.system.call') + @patch('stem.util.system.is_available', Mock(return_value = True)) def test_pid_by_port_netstat(self, call_mock): """ Tests the pid_by_port function with a netstat response.
tor-commits@lists.torproject.org