commit 8a00dcb0cd1bf00e6fc5ea315de555882a175683 Author: Damian Johnson atagar@torproject.org Date: Fri Sep 11 15:56:37 2020 -0700
Trailing whitespace broke 'ss' connection resolution
Some platforms append trailing whitespace to the output of the 'ss' command. Caught thanks to toralf...
https://github.com/torproject/stem/issues/46
Determined the problem by running the following against toralf's output...
from stem.util.connection import Resolver, get_connections from unittest.mock import patch
with open('ss-nptu.log') as ss_file: ss_output = ss_file.read()
with patch('stem.util.system.call') as call_mock: call_mock.return_value = ss_output.split('\n')
print('parsed %i connections' % len(get_connections(Resolver.SS, process_pid = 2238, process_name = 'tor')))
Before:
% python3.7 demo.py Traceback (most recent call last): File "demo.py", line 12, in <module> print('parsed %i connections' % len(get_connections(Resolver.SS, process_pid = 2238, process_name = 'tor'))) File "/home/atagar/Desktop/stem/stem/util/connection.py", line 333, in get_connections raise OSError('No results found using: %s' % resolver_command) OSError: No results found using: ss -nptu
After:
% python3.7 demo.py parsed 8114 connections --- docs/change_log.rst | 4 ++++ stem/util/connection.py | 2 +- test/settings.cfg | 1 + test/unit/util/connection.py | 19 +++++++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/docs/change_log.rst b/docs/change_log.rst index 5a9a8928..cc830d6c 100644 --- a/docs/change_log.rst +++ b/docs/change_log.rst @@ -67,6 +67,10 @@ The following are only available within Stem's `git repository
* *transport* lines within extrainfo descriptors failed to validate
+ * **Utilities** + + * *ss* connection resolver failed on platforms that append whitespace (:ticket:`46`) + * **Installation**
* Migrated from distutil to setuptools diff --git a/stem/util/connection.py b/stem/util/connection.py index a83922f7..2198d526 100644 --- a/stem/util/connection.py +++ b/stem/util/connection.py @@ -126,7 +126,7 @@ RESOLVER_FILTER = { Resolver.NETSTAT_WINDOWS: '^\s*{protocol}\s+{local}\s+{remote}\s+ESTABLISHED\s+{pid}\s*$',
# tcp ESTAB 0 0 192.168.0.20:44415 38.229.79.2:443 users:(("tor",15843,9)) - Resolver.SS: '^{protocol}\s+ESTAB\s+.*\s+{local}\s+{remote}\s+users:\(\("{name}",(?:pid=)?{pid},(?:fd=)?[0-9]+\)\)$', + Resolver.SS: '^{protocol}\s+ESTAB\s+.*\s+{local}\s+{remote}\s+users:\(\("{name}",(?:pid=)?{pid},(?:fd=)?[0-9]+\)\)\s*$',
# tor 3873 atagar 45u IPv4 40994 0t0 TCP 10.243.55.20:45724->194.154.227.109:9001 (ESTABLISHED) Resolver.LSOF: '^{name}\s+{pid}\s+.*\s+{protocol}\s+{local}->{remote} \(ESTABLISHED\)$', diff --git a/test/settings.cfg b/test/settings.cfg index e49835ea..b1d26d92 100644 --- a/test/settings.cfg +++ b/test/settings.cfg @@ -188,6 +188,7 @@ pycodestyle.ignore stem/descriptor/__init__.py => E402: import stem.descriptor.s pycodestyle.ignore stem/descriptor/__init__.py => E402: import stem.descriptor.tordnsel pycodestyle.ignore test/unit/util/connection.py => W291: _tor tor 15843 10 pipe 0x0 state: pycodestyle.ignore test/unit/util/connection.py => W291: _tor tor 15843 11 pipe 0x0 state: +pycodestyle.ignore test/unit/util/connection.py => W291: tcp ESTAB
# False positives from pyflakes. These are mappings between the path and the # issue. diff --git a/test/unit/util/connection.py b/test/unit/util/connection.py index 575667b4..24020c55 100644 --- a/test/unit/util/connection.py +++ b/test/unit/util/connection.py @@ -64,6 +64,12 @@ tcp ESTAB 0 0 127.0.0.1:22 127.0.0.1:56673 tcp ESTAB 0 0 192.168.0.1:44415 38.229.79.2:443 users:(("tor",15843,9)) """
+SS_WHITESPACE_OUTPUT = """\ +Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port +tcp ESTAB 0 0 192.168.0.1:44092 23.112.135.72:443 users:(("tor",15843,10)) +tcp ESTAB 0 0 192.168.0.1:44415 38.229.79.2:443 users:(("tor",15843,9)) +""" + SS_IPV6_OUTPUT = """\ Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp ESTAB 0 0 5.9.158.75:443 107.170.93.13:56159 users:(("tor",pid=25056,fd=997)) @@ -315,6 +321,19 @@ class TestConnection(unittest.TestCase): call_mock.side_effect = OSError('Unable to call ss') self.assertRaises(OSError, stem.util.connection.get_connections, Resolver.SS, process_pid = 1111)
+ @patch('stem.util.system.call', Mock(return_value = SS_WHITESPACE_OUTPUT.split('\n'))) + def test_get_connections_by_ss_with_whitespace(self): + """ + On some platforms the 'ss' command pads its lines with trailing whitespace. + """ + + expected = [ + Connection('192.168.0.1', 44092, '23.112.135.72', 443, 'tcp', False), + Connection('192.168.0.1', 44415, '38.229.79.2', 443, 'tcp', False), + ] + + self.assertEqual(expected, stem.util.connection.get_connections(Resolver.SS, process_pid = 15843, process_name = 'tor')) + @patch('stem.util.system.call', Mock(return_value = SS_IPV6_OUTPUT.split('\n'))) def test_get_connections_by_ss_ipv6(self): """
tor-commits@lists.torproject.org