[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Support IPv4-mapped IPv6 addresses
commit 195f36b382fd9825269d057fde775dcce25d482b
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sat Jan 23 14:25:14 2016 -0800
Support IPv4-mapped IPv6 addresses
Ohhh IPv6, how many quirks can you manage to cram into one spec? I know, I
know, it's the new hotness but why couldn't they keep the @*!& addresses simple
as its predecessor? </grumble>
Thanks to cypherpunks for pointing this out!
https://trac.torproject.org/projects/tor/ticket/18079#comment:6
---
docs/change_log.rst | 1 +
stem/util/connection.py | 38 ++++++++++++++++++++++++++++++++++++++
test/unit/util/connection.py | 31 ++++++++++++++++++-------------
3 files changed, 57 insertions(+), 13 deletions(-)
diff --git a/docs/change_log.rst b/docs/change_log.rst
index 1420ddf..9c6b88b 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -74,6 +74,7 @@ The following are only available within Stem's `git repository
* Added :func:`~stem.util.__init__.datetime_to_unix`
* Basic IPv6 support in :func:`~stem.util.connection.get_connections`
* The 'ss' connection resolver didn't work on Gentoo (:trac:`18079`)
+ * Recognize IPv4-mapped IPv6 addresses in our utils (:trac:`18079`)
* Allow :func:`stem.util.conf.Config.set` to remove values when provided with a **None** value
* **Interpreter**
diff --git a/stem/util/connection.py b/stem/util/connection.py
index 0490f9c..37839cb 100644
--- a/stem/util/connection.py
+++ b/stem/util/connection.py
@@ -393,6 +393,22 @@ def is_valid_ipv6_address(address, allow_brackets = False):
if address.startswith('[') and address.endswith(']'):
address = address[1:-1]
+ if address.count('.') == 3:
+ # Likely an ipv4-mapped portion. Check that its vaild, then replace with a
+ # filler.
+
+ ipv4_start = address.rfind(':', 0, address.find('.')) + 1
+ ipv4_end = address.find(':', ipv4_start + 1)
+
+ if ipv4_end == -1:
+ ipv4_end = None # don't crop the last character
+
+ if not is_valid_ipv4_address(address[ipv4_start:ipv4_end]):
+ return False
+
+ addr_comp = [address[:ipv4_start - 1] if ipv4_start != 0 else None, 'ff:ff', address[ipv4_end + 1:] if ipv4_end else None]
+ address = ':'.join(filter(None, addr_comp))
+
# addresses are made up of eight colon separated groups of four hex digits
# with leading zeros being optional
# https://en.wikipedia.org/wiki/IPv6#Address_format
@@ -494,6 +510,9 @@ def expand_ipv6_address(address):
>>> expand_ipv6_address('::')
'0000:0000:0000:0000:0000:0000:0000:0000'
+ >>> expand_ipv6_address('::ffff:5.9.158.75')
+ '0000:0000:0000:0000:0000:ffff:0509:9e4b'
+
:param str address: IPv6 address to be expanded
:raises: **ValueError** if the address can't be expanded due to being malformed
@@ -502,6 +521,25 @@ def expand_ipv6_address(address):
if not is_valid_ipv6_address(address):
raise ValueError("'%s' isn't a valid IPv6 address" % address)
+ # expand ipv4-mapped portions of addresses
+ if address.count('.') == 3:
+ ipv4_start = address.rfind(':', 0, address.find('.')) + 1
+ ipv4_end = address.find(':', ipv4_start + 1)
+
+ if ipv4_end == -1:
+ ipv4_end = None # don't crop the last character
+
+ # Converts ipv4 address to its hex ipv6 representation. For instance...
+ #
+ # '5.9.158.75' => '0509:9e4b'
+
+ ipv4_bin = _get_address_binary(address[ipv4_start:ipv4_end])
+ groupings = [ipv4_bin[16 * i:16 * (i + 1)] for i in range(2)]
+ ipv6_snippet = ':'.join(['%04x' % int(group, 2) for group in groupings])
+
+ addr_comp = [address[:ipv4_start - 1] if ipv4_start != 0 else None, ipv6_snippet, address[ipv4_end + 1:] if ipv4_end else None]
+ address = ':'.join(filter(None, addr_comp))
+
# expands collapsed groupings, there can only be a single '::' in a valid
# address
if '::' in address:
diff --git a/test/unit/util/connection.py b/test/unit/util/connection.py
index 776182d..793bcc6 100644
--- a/test/unit/util/connection.py
+++ b/test/unit/util/connection.py
@@ -53,7 +53,7 @@ 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_GENTOO_OUTPUT = """\
+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))
tcp ESTAB 0 0 5.9.158.75:443 159.203.97.91:37802 users:(("tor",pid=25056,fd=77))
@@ -241,15 +241,16 @@ class TestConnection(unittest.TestCase):
self.assertRaises(IOError, stem.util.connection.get_connections, Resolver.SS, process_pid = 1111)
@patch('stem.util.system.call')
- def test_get_connections_by_ss_on_gentoo(self, call_mock):
+ def test_get_connections_by_ss_ipv6(self, call_mock):
"""
- Checks the get_connections function with the ss resolver results on a
- hardened Gentoo system...
+ Checks the get_connections function with the ss resolver results on IPv6
+ conections. This also checks with the output from a hardened Gentoo system
+ which has subtle differences...
https://trac.torproject.org/projects/tor/ticket/18079
"""
- call_mock.return_value = SS_GENTOO_OUTPUT.split('\n')
+ call_mock.return_value = SS_IPV6_OUTPUT.split('\n')
expected = [
Connection('5.9.158.75', 443, '107.170.93.13', 56159, 'tcp'),
Connection('5.9.158.75', 443, '159.203.97.91', 37802, 'tcp'),
@@ -257,12 +258,7 @@ class TestConnection(unittest.TestCase):
Connection('2a01:4f8:190:514a::2', 443, '2001:858:2:2:aabb:0:563b:1526', 51428, 'tcp'),
]
self.assertEqual(expected, stem.util.connection.get_connections(Resolver.SS, process_pid = 25056, process_name = 'tor'))
-
- # TODO: This example ss output has entries like "::ffff:5.9.158.75:5222".
- # What is this? Looks like a IPv6 *and* IPv4 address mashed together. Our
- # resolver understandably thinks this is invalid right now.
-
- self.assertRaises(IOError, stem.util.connection.get_connections, Resolver.SS, process_name = 'beam')
+ self.assertEqual(2, len(stem.util.connection.get_connections(Resolver.SS, process_name = 'beam')))
@patch('stem.util.system.call')
def test_get_connections_by_lsof(self, call_mock):
@@ -399,6 +395,9 @@ class TestConnection(unittest.TestCase):
'fe80:0000:0000:0000:0202:b3ff:fe1e:8329',
'fe80:0:0:0:202:b3ff:fe1e:8329',
'fe80::202:b3ff:fe1e:8329',
+ '::ffff:5.9.158.75',
+ '5.9.158.75::ffff',
+ '::5.9.158.75:ffff',
'::',
)
@@ -406,16 +405,19 @@ class TestConnection(unittest.TestCase):
'fe80:0000:0000:0000:0202:b3ff:fe1e:829g',
'fe80:0000:0000:0000:0202:b3ff:fe1e: 8329',
'2001:db8::aaaa::1',
+ '::ffff:5.9.158.75.12',
+ '::ffff:5.9.158',
+ '::ffff:5.9',
':::',
':',
'',
)
for address in valid_addresses:
- self.assertTrue(stem.util.connection.is_valid_ipv6_address(address))
+ self.assertTrue(stem.util.connection.is_valid_ipv6_address(address), "%s isn't a valid IPv6 address" % address)
for address in invalid_addresses:
- self.assertFalse(stem.util.connection.is_valid_ipv6_address(address))
+ self.assertFalse(stem.util.connection.is_valid_ipv6_address(address), '%s should be an invalid IPv6 address' % address)
def test_is_valid_port(self):
"""
@@ -464,6 +466,9 @@ class TestConnection(unittest.TestCase):
'::': '0000:0000:0000:0000:0000:0000:0000:0000',
'::1': '0000:0000:0000:0000:0000:0000:0000:0001',
'1::1': '0001:0000:0000:0000:0000:0000:0000:0001',
+ '::ffff:5.9.158.75': '0000:0000:0000:0000:0000:ffff:0509:9e4b',
+ '5.9.158.75::ffff': '0509:9e4b:0000:0000:0000:0000:0000:ffff',
+ '::5.9.158.75:ffff': '0000:0000:0000:0000:0000:0509:9e4b:ffff',
}
for test_arg, expected in test_values.items():
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits