[tor-commits] [stem/master] Add abstract BaseRelayCell class
atagar at torproject.org
atagar at torproject.org
Sun Aug 26 20:49:21 UTC 2018
commit 240b125b01bc7ab2e517852d2e7f5947a799d345
Author: Dave Rolek <dmr-x at riseup.net>
Date: Fri Aug 3 14:56:31 2018 +0000
Add abstract BaseRelayCell class
It's intended to defined common Relay cell functionality, for encrypted
and decrypted cells.
By unpacking content no further than the payload, this abstraction
should allow a lot of flexibility in handling Relay cells prior to
decryption.
Note that unpacking is not yet possible - subclasses must be defined
with a VALUE. (Namely: RELAY and RELAY_EARLY)
---
stem/client/cell.py | 36 ++++++++++++++++++++++++++++++++++++
test/unit/client/cell.py | 31 +++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+)
diff --git a/stem/client/cell.py b/stem/client/cell.py
index 12fa994c..a4fb1a67 100644
--- a/stem/client/cell.py
+++ b/stem/client/cell.py
@@ -14,6 +14,7 @@ Messages communicated over a Tor relay's ORPort.
|- CircuitCell - Circuit management.
| |- CreateCell - Create a circuit. (section 5.1)
| |- CreatedCell - Acknowledge create. (section 5.1)
+ | |- BaseRelayCell - End-to-end data; abstract. (section 6.1)
| |- RelayCell - End-to-end data. (section 6.1)
| |- DestroyCell - Stop using a circuit. (section 5.4)
| |- CreateFastCell - Create a circuit, no PK. (section 5.1)
@@ -83,6 +84,7 @@ class Cell(object):
The following cell types explicitly don't have *unused* content:
* PaddingCell (we consider all content part of payload)
* VersionsCell (all content is unpacked and treated as a version specification)
+ * BaseRelayCell (we don't parse cell beyond header/body)
* VPaddingCell (we consider all content part of payload)
:var bytes unused: unused filler that padded the cell to the expected size
@@ -320,6 +322,40 @@ class CreatedCell(CircuitCell):
super(CreatedCell, self).__init__() # TODO: implement
+class BaseRelayCell(CircuitCell):
+ """
+ Cell whose subclasses are relayed over circuits.
+
+ :var bytes payload: raw payload, quite possibly encrypted
+ """
+
+ NAME = 'INTERNAL_BASE_RELAY' # defined for error/other strings
+ IS_FIXED_SIZE = True # all relay cells are fixed-size
+
+ # other attributes are deferred to subclasses, since this class cannot be directly unpacked
+
+ def __init__(self, circ_id, payload):
+ if not payload:
+ raise ValueError('Relay cells require a payload')
+ if len(payload) != FIXED_PAYLOAD_LEN:
+ raise ValueError('Payload should be %i bytes, but was %i' % (FIXED_PAYLOAD_LEN, len(payload)))
+
+ super(BaseRelayCell, self).__init__(circ_id, unused = b'')
+ self.payload = payload
+
+ def pack(self, link_protocol):
+ # unlike everywhere else, we actually want to use the subclass type, NOT *this* class
+ return type(self)._pack(link_protocol, self.payload, circ_id = self.circ_id)
+
+ @classmethod
+ def _unpack(cls, content, circ_id, link_protocol):
+ # unlike everywhere else, we actually want to use the subclass type, NOT *this* class
+ return cls(circ_id, content)
+
+ def __hash__(self):
+ return stem.util._hash_attr(self, 'circ_id', 'payload', cache = True)
+
+
class RelayCell(CircuitCell):
"""
Command concerning a relay circuit.
diff --git a/test/unit/client/cell.py b/test/unit/client/cell.py
index ce492638..278e0b4c 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 datetime
import hashlib
import os
+import struct
import unittest
from stem.client.datatype import ZERO, CertType, CloseReason, Address, Certificate
@@ -14,6 +15,7 @@ from stem.client.cell import (
FIXED_PAYLOAD_LEN,
Cell,
PaddingCell,
+ BaseRelayCell,
RelayCell,
DestroyCell,
CreateFastCell,
@@ -188,6 +190,35 @@ class TestCell(unittest.TestCase):
self.assertEqual(b'', cell.unused) # always empty
self.assertEqual(cell_bytes, cell.pack(link_protocol))
+ def test_base_relay_cell(self):
+ arbitrary_circ_id = 123
+ even_more_arbitrary_link_protocol = 1234
+
+ cell = BaseRelayCell(arbitrary_circ_id, RANDOM_PAYLOAD)
+ self.assertEqual(RANDOM_PAYLOAD, cell.payload)
+ self.assertEqual(arbitrary_circ_id, cell.circ_id)
+ self.assertEqual(True, cell.IS_FIXED_SIZE)
+
+ # Cell.unpack not reachable - won't be tested
+ # but we can at least directly test _unpack, although it's a pretty simple method
+ cell_2 = BaseRelayCell._unpack(RANDOM_PAYLOAD, arbitrary_circ_id, even_more_arbitrary_link_protocol)
+ self.assertEqual(cell, cell_2)
+
+ # pack not possible, but easily callable
+ self.assertRaises(struct.error, cell.pack, even_more_arbitrary_link_protocol)
+
+ # check other values and inequality
+ for (circ_id, payload) in ((arbitrary_circ_id, ZERO * FIXED_PAYLOAD_LEN), (arbitrary_circ_id + 1, RANDOM_PAYLOAD)):
+ unequal_cell = BaseRelayCell(circ_id, payload)
+ self.assertEqual(payload, unequal_cell.payload)
+ self.assertNotEqual(cell, unequal_cell)
+
+ # invalid constructions
+ self.assertRaisesWith(ValueError, 'Relay cells require a payload', BaseRelayCell, arbitrary_circ_id, None)
+ expected_message_format = 'Payload should be %i bytes, but was ' % FIXED_PAYLOAD_LEN + '%i'
+ for payload_len in (FIXED_PAYLOAD_LEN - 1, FIXED_PAYLOAD_LEN + 1):
+ self.assertRaisesWith(ValueError, expected_message_format % payload_len, BaseRelayCell, arbitrary_circ_id, ZERO * payload_len)
+
def test_relay_cell(self):
for cell_bytes, (command, command_int, circ_id, stream_id, data, digest, unused, link_protocol) in RELAY_CELLS.items():
if not unused.strip(ZERO):
More information about the tor-commits
mailing list