[tor-commits] [stem/master] Stub initial RelaySocket

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


commit a11428262d283dcfb02d70fd2e99080cdc1033fc
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Dec 31 14:34:05 2017 -0800

    Stub initial RelaySocket
    
    Ok, time to start adding ORPort support. Adding a socket class similar to our
    ControlSocket. Unfotunately we need to fill this out a bit more before it'll be
    testable.
---
 stem/socket.py | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 96 insertions(+), 9 deletions(-)

diff --git a/stem/socket.py b/stem/socket.py
index 6bf13a39..1b9cea5f 100644
--- a/stem/socket.py
+++ b/stem/socket.py
@@ -47,6 +47,10 @@ Tor...
 ::
 
   BaseSocket - Thread safe socket.
+    |- RelaySocket - Socket for a relay's ORPort.
+    |  |- send - sends a message to the socket
+    |  +- recv - receives a response from the socket
+    |
     |- ControlSocket - Socket wrapper that speaks the tor control protocol.
     |  |- ControlPort - Control connection via a port.
     |  |- ControlSocketFile - Control connection via a local file socket.
@@ -86,6 +90,10 @@ ERROR_MSG = 'Error while receiving a control message (%s): %s'
 
 TRUNCATE_LOGS = 10
 
+# maximum number of bytes to read at a time from a relay socket
+
+MAX_READ_BUFFER_LEN = 10 * 1024 * 1024
+
 
 class BaseSocket(object):
   """
@@ -336,6 +344,81 @@ class BaseSocket(object):
     raise NotImplementedError('Unsupported Operation: this should be implemented by the BaseSocket subclass')
 
 
+class RelaySocket(BaseSocket):
+  """
+  `Link-level connection
+  <https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt>`_ to a Tor
+  relay.
+
+  :var str address: address our socket connects to
+  :var int port: ORPort our socket connects to
+  """
+
+  def __init__(self, address = '127.0.0.1', port = 9050, connect = True):
+    """
+    RelaySocket constructor.
+
+    :param str address: ip address of the relay
+    :param int port: orport of the relay
+    :param bool connect: connects to the socket if True, leaves it unconnected otherwise
+
+    :raises: :class:`stem.SocketError` if connect is **True** and we're
+      unable to establish a connection
+    """
+
+    super(RelaySocket, self).__init__()
+    self.address = address
+    self.port = port
+
+    if connect:
+      self.connect()
+
+  def send(self, message):
+    """
+    Sends a message to the relay's ORPort.
+
+    :param str message: message to be formatted and sent to the socket
+
+    :raises:
+      * :class:`stem.SocketError` if a problem arises in using the socket
+      * :class:`stem.SocketClosed` if the socket is known to be shut down
+    """
+
+    self._send(message, _write_to_socket)
+
+  def recv(self, max_response_size = MAX_READ_BUFFER_LEN):
+    """
+    Receives a message from the relay.
+
+    :param int max_response_size: maximum bytes to return
+
+    :returns: bytes for the message received
+
+    :raises:
+      * :class:`stem.ProtocolError` the content from the socket is malformed
+      * :class:`stem.SocketClosed` if the socket closes before we receive a complete message
+    """
+
+    # TODO: Not really sure what we'll want here. To start with just copying
+    # endosome's behavior.
+
+    def _read(control_file):
+      return control_file.read(max_response_size)
+
+    return self._recv(_read)
+
+  def is_localhost(self):
+    return self.address == '127.0.0.1'
+
+  def _make_socket(self):
+    try:
+      relay_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+      relay_socket.connect((self.address, self.port))
+      return relay_socket
+    except socket.error as exc:
+      raise stem.SocketError(exc)
+
+
 class ControlSocket(BaseSocket):
   """
   Wrapper for a socket connection that speaks the Tor control protocol. To the
@@ -531,16 +614,20 @@ def send_message(control_file, message, raw = False):
   if not raw:
     message = send_formatting(message)
 
-  try:
-    control_file.write(stem.util.str_tools._to_bytes(message))
-    control_file.flush()
+  _write_to_socket(control_file, message)
 
-    if log.is_tracing():
-      log_message = message.replace('\r\n', '\n').rstrip()
-      msg_div = '\n' if '\n' in log_message else ' '
-      log.trace('Sent to tor:%s%s' % (msg_div, log_message))
+  if log.is_tracing():
+    log_message = message.replace('\r\n', '\n').rstrip()
+    msg_div = '\n' if '\n' in log_message else ' '
+    log.trace('Sent to tor:%s%s' % (msg_div, log_message))
+
+
+def _write_to_socket(socket_file, message):
+  try:
+    socket_file.write(stem.util.str_tools._to_bytes(message))
+    socket_file.flush()
   except socket.error as exc:
-    log.info('Failed to send message: %s' % exc)
+    log.info('Failed to send: %s' % exc)
 
     # When sending there doesn't seem to be a reliable method for
     # distinguishing between failures from a disconnect verses other things.
@@ -554,7 +641,7 @@ def send_message(control_file, message, raw = False):
     # if the control_file has been closed then flush will receive:
     # AttributeError: 'NoneType' object has no attribute 'sendall'
 
-    log.info('Failed to send message: file has been closed')
+    log.info('Failed to send: file has been closed')
     raise stem.SocketClosed('file has been closed')
 
 





More information about the tor-commits mailing list