[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Support DESTROY cells
commit aeb17f9d150a7e3f404a37f57c29759325ab3221
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Mon Jan 22 12:10:21 2018 -0800
Support DESTROY cells
---
stem/client/__init__.py | 40 +++++++++++++++++++++++++
stem/client/cell.py | 76 ++++++++++++++++++++++++++++++++++++++++++++++--
test/unit/client/cell.py | 20 ++++++++++++-
3 files changed, 132 insertions(+), 4 deletions(-)
diff --git a/stem/client/__init__.py b/stem/client/__init__.py
index 1fd05f9d..01c6b749 100644
--- a/stem/client/__init__.py
+++ b/stem/client/__init__.py
@@ -49,6 +49,29 @@ a wrapper for :class:`~stem.socket.RelaySocket`, much the same way as
**AUTHENTICATE** RSA1024 AUTHENTICATE cell link certificate
**UNKNOWN** unrecognized certificate type
===================== ===========
+
+.. data:: CloseReason (enum)
+
+ Reason a relay is closed.
+
+ ===================== ===========
+ CloseReason Description
+ ===================== ===========
+ **NONE** no reason given
+ **PROTOCOL** tor protocol violation
+ **INTERNAL** internal error
+ **REQUESTED** client sent a TRUNCATE command
+ **HIBERNATING** relay suspended, trying to save bandwidth
+ **RESOURCELIMIT** out of memory, sockets, or circuit IDs
+ **CONNECTFAILED** unable to reach relay
+ **OR_IDENTITY** connected, but its OR identity was not as expected
+ **OR_CONN_CLOSED** connection that was carrying this circuit died
+ **FINISHED** circuit has expired for being dirty or old
+ **TIMEOUT** circuit construction took too long
+ **DESTROYED** circuit was destroyed without a client TRUNCATE
+ **NOSUCHSERVICE** request was for an unknown hidden service
+ **UNKNOWN** unrecognized reason
+ ===================== ===========
"""
import io
@@ -81,6 +104,23 @@ CertType = stem.util.enum.UppercaseEnum(
'UNKNOWN',
)
+CloseReason = stem.util.enum.UppercaseEnum(
+ 'NONE',
+ 'PROTOCOL',
+ 'INTERNAL',
+ 'REQUESTED',
+ 'HIBERNATING',
+ 'RESOURCELIMIT',
+ 'CONNECTFAILED',
+ 'OR_IDENTITY',
+ 'OR_CONN_CLOSED',
+ 'FINISHED',
+ 'TIMEOUT',
+ 'DESTROYED',
+ 'NOSUCHSERVICE',
+ 'UNKNOWN',
+)
+
def split(content, size):
"""
diff --git a/stem/client/cell.py b/stem/client/cell.py
index 6f107b79..76a75108 100644
--- a/stem/client/cell.py
+++ b/stem/client/cell.py
@@ -44,7 +44,7 @@ import random
import sys
from stem import UNDEFINED
-from stem.client import ZERO, Address, Certificate, Size, split
+from stem.client import ZERO, Address, Certificate, CloseReason, Size, split
from stem.util import _hash_attr, datetime_to_unix
FIXED_PAYLOAD_LEN = 509
@@ -261,10 +261,80 @@ class RelayCell(CircuitCell):
class DestroyCell(CircuitCell):
+ """
+ Closes the given circuit.
+
+ :var stem.client.CloseReason reason: reason the circuit is being closed
+ :var int reason_int: integer value of our closure reason
+ """
+
NAME = 'DESTROY'
VALUE = 4
IS_FIXED_SIZE = True
+ REASON_FOR_INT = {
+ 0: CloseReason.NONE,
+ 1: CloseReason.PROTOCOL,
+ 2: CloseReason.INTERNAL,
+ 3: CloseReason.REQUESTED,
+ 4: CloseReason.HIBERNATING,
+ 5: CloseReason.RESOURCELIMIT,
+ 6: CloseReason.CONNECTFAILED,
+ 7: CloseReason.OR_IDENTITY,
+ 8: CloseReason.OR_CONN_CLOSED,
+ 9: CloseReason.FINISHED,
+ 10: CloseReason.TIMEOUT,
+ 11: CloseReason.DESTROYED,
+ 12: CloseReason.NOSUCHSERVICE,
+ }
+
+ INT_FOR_REASON = dict((v, k) for k, v in REASON_FOR_INT.items())
+
+ def __init__(self, circ_id, reason):
+ super(DestroyCell, self).__init__(circ_id)
+
+ if isinstance(reason, int):
+ self.reason = DestroyCell.REASON_FOR_INT.get(reason, CloseReason.UNKNOWN)
+ self.reason_int = reason
+ elif reason in CloseReason:
+ self.reason = reason
+ self.reason_int = DestroyCell.INT_FOR_REASON.get(reason, -1)
+ else:
+ raise ValueError('Invalid closure reason: %s' % reason)
+
+ @classmethod
+ def pack(cls, link_version, circ_id, reason = CloseReason.NONE):
+ """
+ Provides payload to close the given circuit.
+
+ :param int link_version: link protocol version
+ :param int circ_id: circuit id
+ :param stem.client.CloseReason reason: reason to close the circuit
+
+ :returns: **bytes** to close the circuit
+ """
+
+ reason = DestroyCell.INT_FOR_REASON.get(reason, reason)
+
+ if not isinstance(reason, int):
+ raise ValueError('Invalid closure reason: %s' % reason)
+
+ return cls._pack(link_version, Size.CHAR.pack(reason), circ_id)
+
+ @classmethod
+ def _unpack(cls, content, circ_id, link_version):
+ content = content.rstrip(ZERO)
+
+ if not content:
+ content = ZERO
+ elif len(content) > 1:
+ raise ValueError('Circuit closure reason should be a single byte, but was %i' % len(content))
+
+ return DestroyCell(circ_id, Size.CHAR.unpack(content))
+
+ def __hash__(self):
+ return _hash_attr(self, 'circ_id', 'reason_int')
+
class CreateFastCell(CircuitCell):
"""
@@ -309,7 +379,7 @@ class CreateFastCell(CircuitCell):
return CreateFastCell(circ_id, content)
def __hash__(self):
- return _hash_attr(self, 'key_material')
+ return _hash_attr(self, 'circ_id', 'key_material')
class CreatedFastCell(CircuitCell):
@@ -362,7 +432,7 @@ class CreatedFastCell(CircuitCell):
return CreatedFastCell(circ_id, content[:HASH_LEN], content[HASH_LEN:])
def __hash__(self):
- return _hash_attr(self, 'derivative_key', 'key_material')
+ return _hash_attr(self, 'circ_id', 'derivative_key', 'key_material')
class VersionsCell(Cell):
diff --git a/test/unit/client/cell.py b/test/unit/client/cell.py
index 577114c3..5b8db2bb 100644
--- a/test/unit/client/cell.py
+++ b/test/unit/client/cell.py
@@ -6,13 +6,14 @@ import datetime
import os
import unittest
-from stem.client import ZERO, AddrType, CertType, Address, Certificate
+from stem.client import ZERO, AddrType, CertType, CloseReason, Address, Certificate
from test.unit.client import test_data
from stem.client.cell import (
FIXED_PAYLOAD_LEN,
Cell,
PaddingCell,
+ DestroyCell,
CreateFastCell,
CreatedFastCell,
VersionsCell,
@@ -29,6 +30,11 @@ PADDING_CELLS = {
'\x00\x00\x00' + RANDOM_PAYLOAD: RANDOM_PAYLOAD,
}
+DESTROY_CELLS = {
+ '\x80\x00\x00\x00\x04\x00' + ZERO * 508: (2147483648, CloseReason.NONE, 0),
+ '\x80\x00\x00\x00\x04\x03' + ZERO * 508: (2147483648, CloseReason.REQUESTED, 3),
+}
+
CREATE_FAST_CELLS = {
('\x80\x00\x00\x00\x05\x92O\x0c\xcb\xa8\xac\xfb\xc9\x7f\xd0\rz\x1a\x03u\x91\xceas\xce' + ZERO * 489): (2147483648, '\x92O\x0c\xcb\xa8\xac\xfb\xc9\x7f\xd0\rz\x1a\x03u\x91\xceas\xce'),
}
@@ -128,6 +134,18 @@ class TestCell(unittest.TestCase):
self.assertEqual(cell_bytes, PaddingCell.pack(2, payload))
self.assertEqual(payload, Cell.unpack(cell_bytes, 2)[0].payload)
+ def test_destroy_packing(self):
+ for cell_bytes, (circ_id, reason, reason_int) in DESTROY_CELLS.items():
+ self.assertEqual(cell_bytes, DestroyCell.pack(5, circ_id, reason))
+ self.assertEqual(cell_bytes, DestroyCell.pack(5, circ_id, reason_int))
+
+ cell = Cell.unpack(cell_bytes, 5)[0]
+ self.assertEqual(circ_id, cell.circ_id)
+ self.assertEqual(reason, cell.reason)
+ self.assertEqual(reason_int, cell.reason_int)
+
+ self.assertRaisesRegexp(ValueError, 'Circuit closure reason should be a single byte, but was 2', Cell.unpack, '\x80\x00\x00\x00\x04\x01\x01' + ZERO * 507, 5)
+
def test_create_fast(self):
for cell_bytes, (circ_id, key_material) in CREATE_FAST_CELLS.items():
self.assertEqual(cell_bytes, CreateFastCell.pack(5, circ_id, key_material))
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits