[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [stem/master] Parse address payloads



commit 954269505c8d0656b69849de8121195c42aeee5b
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date:   Mon Jan 15 09:29:45 2018 -0800

    Parse address payloads
    
    Add support for addresses in Netinfo cells. Presently this only decodes IPv4
    addresses since that's what I have an example for.
---
 stem/client/__init__.py      | 74 ++++++++++++++++++++++++++++++++++++++++++++
 test/settings.cfg            |  1 +
 test/unit/client/__init__.py |  1 +
 test/unit/client/types.py    | 19 ++++++++++++
 4 files changed, 95 insertions(+)

diff --git a/stem/client/__init__.py b/stem/client/__init__.py
index 6909c1a7..931dbab1 100644
--- a/stem/client/__init__.py
+++ b/stem/client/__init__.py
@@ -15,17 +15,51 @@ a wrapper for :class:`~stem.socket.RelaySocket`, much the same way as
     |- pack - encodes content
     |- unpack - decodes content
     +- pop - decodes content with remainder
+
+.. data:: AddrType (enum)
+
+  Form an address takes.
+
+  ===================== ===========
+  AddressType           Description
+  ===================== ===========
+  **HOSTNAME**          relay hostname
+  **IPv4**              IPv4 address
+  **IPv6**              IPv6 address
+  **ERROR_TRANSIENT**   temporarily error retrieving address
+  **ERROR_PERMANENT**   permanent error retrieving address
+  **UNKNOWN**           unrecognized address type
+  ===================== ===========
 """
 
 import collections
 import struct
 
+import stem.util.enum
+
 ZERO = '\x00'
 
 __all__ = [
   'cell',
 ]
 
+AddrType = stem.util.enum.UppercaseEnum(
+  'HOSTNAME',
+  'IPv4',
+  'IPv6',
+  'ERROR_TRANSIENT',
+  'ERROR_PERMANENT',
+  'UNKNOWN',
+)
+
+ADDR_INT = {
+  0: AddrType.HOSTNAME,
+  4: AddrType.IPv4,
+  6: AddrType.IPv6,
+  16: AddrType.ERROR_TRANSIENT,
+  17: AddrType.ERROR_PERMANENT,
+}
+
 
 class Certificate(collections.namedtuple('Certificate', ['type', 'value'])):
   """
@@ -45,6 +79,46 @@ class Certificate(collections.namedtuple('Certificate', ['type', 'value'])):
   """
 
 
+class Address(collections.namedtuple('Address', ['type', 'type_int', 'value', 'value_bin', 'ttl'])):
+  """
+  Relay address.
+
+  :var stem.client.AddrType type: address type
+  :var int type_int: integer value of the address type
+  :var unicode value: address value
+  :var bytes value_bin: encoded address value
+  :var int ttl: seconds the record can be validly cached for
+  """
+
+  @staticmethod
+  def pop(content):
+    if not content:
+      raise ValueError('Payload empty where an address was expected')
+    elif len(content) < 2:
+      raise ValueError('Insuffient data for address headers')
+
+    addr_type_int, content = Size.CHAR.pop(content)
+    addr_type = ADDR_INT.get(addr_type_int, AddrType.UNKNOWN)
+    addr_length, content = Size.CHAR.pop(content)
+
+    if len(content) < addr_length:
+      raise ValueError('Address specified a payload of %i bytes, but only had %i' % (addr_length, len(content)))
+    elif len(content) < addr_length + 4:
+      raise ValueError('Address missing a TTL at its end')
+
+    address_bin, content = content[:addr_length], content[addr_length:]
+    ttl, content = Size.LONG.pop(content)
+
+    # TODO: add support for other address types
+
+    address = None
+
+    if addr_type == AddrType.IPv4 and len(address_bin) == 4:
+      address = '.'.join([str(Size.CHAR.unpack(address_bin[i])) for i in range(4)])
+
+    return Address(addr_type, addr_type_int, address, address_bin, ttl), content
+
+
 class Size(object):
   """
   Unsigned `struct.pack format
diff --git a/test/settings.cfg b/test/settings.cfg
index b4b125a8..d4e1a6c3 100644
--- a/test/settings.cfg
+++ b/test/settings.cfg
@@ -229,6 +229,7 @@ test.unit_tests
 |test.unit.response.authchallenge.TestAuthChallengeResponse
 |test.unit.response.protocolinfo.TestProtocolInfoResponse
 |test.unit.response.mapaddress.TestMapAddressResponse
+|test.unit.client.types.TestClientTypes
 |test.unit.client.cell.TestCell
 |test.unit.connection.authentication.TestAuthenticate
 |test.unit.connection.connect.TestConnect
diff --git a/test/unit/client/__init__.py b/test/unit/client/__init__.py
index f5b82e7e..fdc7a0c6 100644
--- a/test/unit/client/__init__.py
+++ b/test/unit/client/__init__.py
@@ -4,6 +4,7 @@ Unit tests for stem.client.* contents.
 
 __all__ = [
   'cell',
+  'types',
 ]
 
 import os
diff --git a/test/unit/client/types.py b/test/unit/client/types.py
new file mode 100644
index 00000000..fdf4e085
--- /dev/null
+++ b/test/unit/client/types.py
@@ -0,0 +1,19 @@
+"""
+Unit tests for the types in stem.client.
+"""
+
+import unittest
+
+from stem.client import Address
+
+
+class TestClientTypes(unittest.TestCase):
+  def test_address_ipv4(self):
+    addr, content = Address.pop('\x04\x04\x7f\x00\x00\x01\x01\x04\x04aq\x0f\x02\x00\x00\x00\x00')
+    self.assertEqual('q\x0f\x02\x00\x00\x00\x00', content)
+
+    self.assertEqual('IPv4', addr.type)
+    self.assertEqual(4, addr.type_int)
+    self.assertEqual('127.0.0.1', addr.value)
+    self.assertEqual('\x7f\x00\x00\x01', addr.value_bin)
+    self.assertEqual(17040481, addr.ttl)



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits