[tor-commits] [stem/master] Have Cell.unpack() provide a list of cells

atagar at torproject.org atagar at torproject.org
Sun Jan 21 02:04:04 UTC 2018


commit 3db7fab9c3ea84a68c154a0a33829bbcf5da8cdf
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Jan 13 12:07:18 2018 -0800

    Have Cell.unpack() provide a list of cells
    
    On reflection ORPort socket responses contain a series of cells, not
    just one.   Changing our unpack function to parse a series. We'll need
    CERTS, AUTH_CHALLENGE, and NETINFO cell support before we can open links.
---
 stem/client/cell.py                  |  33 ++++++++++++++++++++++-----------
 test/unit/client/__init__.py         |  17 +++++++++++++++++
 test/unit/client/cell.py             |  11 ++++++++---
 test/unit/client/data/new_link_cells | Bin 0 -> 2043 bytes
 4 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/stem/client/cell.py b/stem/client/cell.py
index e29e404f..4d4667d0 100644
--- a/stem/client/cell.py
+++ b/stem/client/cell.py
@@ -42,7 +42,7 @@ import sys
 from stem import UNDEFINED
 from stem.client import ZERO, Size
 
-MAX_FIXED_PAYLOAD_LEN = 509
+FIXED_PAYLOAD_LEN = 509
 
 
 class Cell(object):
@@ -96,28 +96,39 @@ class Cell(object):
   @staticmethod
   def unpack(content, link_version):
     """
-    Unpacks encoded bytes into a Cell subclass.
+    Unpacks encoded bytes into a series of cells.
 
     :param bytes content: payload to decode
     :param int link_version: link protocol version
 
-    :returns: :class:`~stem.client.cell.Cell` subclass
+    :returns: **list** of :class:`~stem.client.cell.Cell` subclasses
 
     :raises:
       * ValueError if content is malformed
       * NotImplementedError if unable to unpack this cell type
     """
 
-    circ_id, content = Size.LONG.pop(content) if link_version > 3 else Size.SHORT.pop(content)
-    command, content = Size.CHAR.pop(content)
-    cls = Cell.by_value(command)
+    cells = []
 
-    if cls.IS_FIXED_SIZE:
-      payload_len = MAX_FIXED_PAYLOAD_LEN
-    else:
-      payload_len, content = Size.SHORT.pop(content)
+    while content:
+      circ_id, content = Size.SHORT.pop(content) if link_version < 4 else Size.LONG.pop(content)
+      command, content = Size.CHAR.pop(content)
+      cls = Cell.by_value(command)
+
+      if cls.IS_FIXED_SIZE:
+        payload_len = FIXED_PAYLOAD_LEN
+      else:
+        payload_len, content = Size.SHORT.pop(content)
+
+      if len(content) < payload_len:
+        raise ValueError('%s cell should have a payload of %i bytes, but only had %i' % (cls.NAME, payload_len, len(content)))
+
+      payload = content[:payload_len].rstrip(ZERO)  # strip padding
+      content = content[payload_len:]
+
+      cells.append(cls._unpack(payload, link_version, circ_id))
 
-    return cls._unpack(content, link_version, circ_id)
+    return cells
 
   @classmethod
   def _pack(cls, link_version, payload, circ_id = 0):
diff --git a/test/unit/client/__init__.py b/test/unit/client/__init__.py
index e35416a8..f5b82e7e 100644
--- a/test/unit/client/__init__.py
+++ b/test/unit/client/__init__.py
@@ -5,3 +5,20 @@ Unit tests for stem.client.* contents.
 __all__ = [
   'cell',
 ]
+
+import os
+
+TEST_DATA = os.path.join(os.path.dirname(__file__), 'data')
+
+
+def test_data(filename):
+  """
+  Provides test data in the given file.
+
+  :param str filename: test data to provide
+
+  :returns: **bytes** with the data
+  """
+
+  with open(os.path.join(TEST_DATA, filename), 'rb') as data_file:
+    return data_file.read()
diff --git a/test/unit/client/cell.py b/test/unit/client/cell.py
index 9502975e..3a2a55f2 100644
--- a/test/unit/client/cell.py
+++ b/test/unit/client/cell.py
@@ -5,6 +5,7 @@ Unit tests for the stem.client.cell.
 import unittest
 
 from stem.client.cell import Cell, VersionsCell
+from test.unit.client import test_data
 
 
 class TestCell(unittest.TestCase):
@@ -31,12 +32,16 @@ class TestCell(unittest.TestCase):
   def test_unpack_not_implemented(self):
     self.assertRaisesRegexp(NotImplementedError, 'Unpacking not yet implemented for AUTHORIZE cells', Cell.unpack, '\x00\x00\x84\x00\x06\x00\x01\x00\x02\x00\x03', 2)
 
+  def test_unpack_for_new_link(self):
+    # TODO: we need to support more cell types before we can test this
+    self.assertRaisesRegexp(NotImplementedError, 'Unpacking not yet implemented for CERTS cells', Cell.unpack, test_data('new_link_cells'), 2)
+
   def test_versions_pack(self):
     self.assertEqual('\x00\x00\x07\x00\x00', VersionsCell.pack([]))
     self.assertEqual('\x00\x00\x07\x00\x02\x00\x01', VersionsCell.pack([1]))
     self.assertEqual('\x00\x00\x07\x00\x06\x00\x01\x00\x02\x00\x03', VersionsCell.pack([1, 2, 3]))
 
   def test_versions_unpack(self):
-    self.assertEqual([], Cell.unpack('\x00\x00\x07\x00\x00', 2).versions)
-    self.assertEqual([1], Cell.unpack('\x00\x00\x07\x00\x02\x00\x01', 2).versions)
-    self.assertEqual([1, 2, 3], Cell.unpack('\x00\x00\x07\x00\x06\x00\x01\x00\x02\x00\x03', 2).versions)
+    self.assertEqual([], Cell.unpack('\x00\x00\x07\x00\x00', 2)[0].versions)
+    self.assertEqual([1], Cell.unpack('\x00\x00\x07\x00\x02\x00\x01', 2)[0].versions)
+    self.assertEqual([1, 2, 3], Cell.unpack('\x00\x00\x07\x00\x06\x00\x01\x00\x02\x00\x03', 2)[0].versions)
diff --git a/test/unit/client/data/new_link_cells b/test/unit/client/data/new_link_cells
new file mode 100644
index 00000000..38e68613
Binary files /dev/null and b/test/unit/client/data/new_link_cells differ





More information about the tor-commits mailing list