示例#1
0
 def from_socket_file(infile):
     global GOT_SOCKET
     if THROW_SOCKET:
         raise stem.SocketError("Ded")
     else:
         GOT_SOCKET = infile
         return MockController()
示例#2
0
def _make_socket_monkey_patch(self):
    try:
        control_socket = _orig_socket(socket.AF_INET, socket.SOCK_STREAM)
        control_socket.connect((self.address, self.port))
        return control_socket
    except socket.error as exc:
        raise stem.SocketError(exc)
示例#3
0
 def test_start_persistent_method_catches_socket_error_exception_on_control_from_port_with_args(
         self, socket, controller):
     sub = self.x.run_check = MagicMock()
     controller.from_port.side_effect = stem.SocketError()
     target = self.x.put_error_to_queue = MagicMock()
     self.x.start_persistent()
     self.assertTrue(target.called)
示例#4
0
    def _make_socket(self):
        try:
            if "socket_noproxy" in dir(socket):  # Socket proxy-patched, use non-proxy one
                control_socket = socket.socket_noproxy(socket.AF_INET, socket.SOCK_STREAM)
            else:
                control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            # TODO: repeated code - consider making a separate method

            control_socket.connect((self._control_addr, self._control_port))
            return control_socket
        except socket.error as exc:
            raise stem.SocketError(exc)
示例#5
0
 def from_port(ip, port):
     if THROW_SOCKET:
         raise stem.SocketError("Ded")
     else:
         return MockController()
示例#6
0
  async def connect(address: str, port: int, link_protocols: Sequence['stem.client.datatype.LinkProtocol'] = DEFAULT_LINK_PROTOCOLS) -> 'stem.client.Relay':  # type: ignore
    """
    Establishes a connection with the given ORPort.

    :param address: ip address of the relay
    :param port: ORPort of the relay
    :param link_protocols: acceptable link protocol versions

    :raises:
      * **ValueError** if address or port are invalid
      * :class:`stem.SocketError` if we're unable to establish a connection
    """

    relay_addr = Address(address)

    if not stem.util.connection.is_valid_port(port):
      raise ValueError("'%s' isn't a valid port" % port)
    elif not link_protocols:
      raise ValueError("Connection can't be established without a link protocol.")

    try:
      conn = stem.socket.RelaySocket(address, port)
      await conn.connect()
    except stem.SocketError as exc:
      if 'Connect call failed' in str(exc):
        raise stem.SocketError("Failed to connect to %s:%i. Maybe it isn't an ORPort?" % (address, port))

      # If not an ORPort (for instance, mistakenly connecting to a ControlPort
      # instead) we'll likely fail during SSL negotiation. This can result
      # in a variety of responses so normalizing what we can...
      #
      #   Debian 9.5:     [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:661)
      #   Ubuntu 16.04:   [SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:590)
      #   Ubuntu 12.04:   [Errno 1] _ssl.c:504: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol

      if 'unknown protocol' in str(exc) or 'wrong version number' in str(exc):
        raise stem.SocketError("Failed to SSL authenticate to %s:%i. Maybe it isn't an ORPort?" % (address, port))

      raise

    # To negotiate our link protocol the first VERSIONS cell is expected to use
    # a circuit ID field size from protocol version 1-3 for backward
    # compatibility...
    #
    #   The first VERSIONS cell, and any cells sent before the
    #   first VERSIONS cell, always have CIRCID_LEN == 2 for backward
    #   compatibility.

    await conn.send(stem.client.cell.VersionsCell(link_protocols).pack(2))  # type: ignore
    response = await conn.recv()

    # Link negotiation ends right away if we lack a common protocol
    # version. (#25139)

    if not response:
      await conn.close()
      raise stem.SocketError('Unable to establish a common link protocol with %s:%i' % (address, port))

    versions_reply = stem.client.cell.Cell.pop(response, 2)[0]  # type: stem.client.cell.VersionsCell # type: ignore
    common_protocols = set(link_protocols).intersection(versions_reply.versions)

    if not common_protocols:
      await conn.close()
      raise stem.SocketError('Unable to find a common link protocol. We support %s but %s:%i supports %s.' % (', '.join(map(str, link_protocols)), address, port, ', '.join(map(str, versions_reply.versions))))

    # Establishing connections requires sending a NETINFO, but including our
    # address is optional. We can revisit including it when we have a usecase
    # where it would help.

    link_protocol = max(common_protocols)
    await conn.send(stem.client.cell.NetinfoCell(relay_addr, []).pack(link_protocol))

    return Relay(conn, link_protocol)
示例#7
0
文件: __init__.py 项目: dmr-x/stem
  def connect(address, port, link_protocols = DEFAULT_LINK_PROTOCOLS):
    """
    Establishes a connection with the given ORPort.

    :param str address: ip address of the relay
    :param int port: ORPort of the relay
    :param tuple link_protocols: acceptable link protocol versions

    :raises:
      * **ValueError** if address or port are invalid
      * :class:`stem.SocketError` if we're unable to establish a connection
    """

    relay_addr = Address(address)

    if not stem.util.connection.is_valid_port(port):
      raise ValueError("'%s' isn't a valid port" % port)
    elif not link_protocols:
      raise ValueError("Connection can't be established without a link protocol.")

    try:
      conn = stem.socket.RelaySocket(address, port)
    except stem.SocketError as exc:
      if 'Connection refused' in str(exc):
        raise stem.SocketError("Failed to connect to %s:%i. Maybe it isn't an ORPort?" % (address, port))
      elif 'SSL: ' in str(exc):
        raise stem.SocketError("Failed to SSL authenticate to %s:%i. Maybe it isn't an ORPort?" % (address, port))
      else:
        raise

    # To negotiate our link protocol the first VERSIONS cell is expected to use
    # a circuit ID field size from protocol version 1-3 for backward
    # compatibility...
    #
    #   The first VERSIONS cell, and any cells sent before the
    #   first VERSIONS cell, always have CIRCID_LEN == 2 for backward
    #   compatibility.

    conn.send(stem.client.cell.VersionsCell(link_protocols).pack(2))
    response = conn.recv()

    # Link negotiation ends right away if we lack a common protocol
    # version. (#25139)

    if not response:
      conn.close()
      raise stem.SocketError('Unable to establish a common link protocol with %s:%i' % (address, port))

    versions_reply = stem.client.cell.Cell.pop(response, 2)[0]
    common_protocols = set(link_protocols).intersection(versions_reply.versions)

    if not common_protocols:
      conn.close()
      raise stem.SocketError('Unable to find a common link protocol. We support %s but %s:%i supports %s.' % (', '.join(link_protocols), address, port, ', '.join(versions_reply.versions)))

    # Establishing connections requires sending a NETINFO, but including our
    # address is optional. We can revisit including it when we have a usecase
    # where it would help.

    link_protocol = max(common_protocols)
    conn.send(stem.client.cell.NetinfoCell(relay_addr, []).pack(link_protocol))

    return Relay(conn, link_protocol)
示例#8
0
文件: connect.py 项目: tlyu/stem
class TestConnect(unittest.TestCase):
    @patch('sys.stdout', new_callable=StringIO)
    @patch('stem.util.system.is_running')
    @patch('os.path.exists', Mock(return_value=True))
    @patch('stem.socket.ControlSocketFile',
           Mock(side_effect=stem.SocketError('failed')))
    @patch('stem.socket.ControlPort',
           Mock(side_effect=stem.SocketError('failed')))
    @patch('stem.connection._connect_auth', Mock())
    def test_failue_with_the_default_endpoint(self, is_running_mock,
                                              stdout_mock):
        is_running_mock.return_value = False
        self._assert_connect_fails_with(
            {}, stdout_mock,
            "Unable to connect to tor. Are you sure it's running?")

        is_running_mock.return_value = True
        self._assert_connect_fails_with(
            {}, stdout_mock,
            "Unable to connect to tor. Maybe it's running without a ControlPort?"
        )

    @patch('sys.stdout', new_callable=StringIO)
    @patch('os.path.exists')
    @patch('stem.util.system.is_running', Mock(return_value=True))
    @patch('stem.socket.ControlSocketFile',
           Mock(side_effect=stem.SocketError('failed')))
    @patch('stem.socket.ControlPort',
           Mock(side_effect=stem.SocketError('failed')))
    @patch('stem.connection._connect_auth', Mock())
    def test_failure_with_a_custom_endpoint(self, path_exists_mock,
                                            stdout_mock):
        path_exists_mock.return_value = True
        self._assert_connect_fails_with(
            {
                'control_port': ('127.0.0.1', 80),
                'control_socket': None
            }, stdout_mock, "Unable to connect to 127.0.0.1:80: failed")
        self._assert_connect_fails_with(
            {
                'control_port': None,
                'control_socket': '/tmp/my_socket'
            }, stdout_mock, "Unable to connect to '/tmp/my_socket': failed")

        path_exists_mock.return_value = False
        self._assert_connect_fails_with(
            {
                'control_port': ('127.0.0.1', 80),
                'control_socket': None
            }, stdout_mock, "Unable to connect to 127.0.0.1:80: failed")
        self._assert_connect_fails_with(
            {
                'control_port': None,
                'control_socket': '/tmp/my_socket'
            }, stdout_mock,
            "The socket file you specified (/tmp/my_socket) doesn't exist")

    @patch('stem.socket.ControlPort')
    @patch('os.path.exists', Mock(return_value=False))
    @patch('stem.connection._connect_auth', Mock())
    def test_getting_a_control_port(self, port_mock):
        stem.connection.connect()
        port_mock.assert_called_once_with('127.0.0.1', 9051)
        port_mock.reset_mock()

        stem.connection.connect(control_port=('255.0.0.10', 80),
                                control_socket=None)
        port_mock.assert_called_once_with('255.0.0.10', 80)

    @patch('stem.socket.ControlSocketFile')
    @patch('os.path.exists', Mock(return_value=True))
    @patch('stem.connection._connect_auth', Mock())
    def test_getting_a_control_socket(self, socket_mock):
        stem.connection.connect()
        socket_mock.assert_called_once_with('/var/run/tor/control')
        socket_mock.reset_mock()

        stem.connection.connect(control_port=None,
                                control_socket='/tmp/my_socket')
        socket_mock.assert_called_once_with('/tmp/my_socket')

    def _assert_connect_fails_with(self, args, stdout_mock, msg):
        result = stem.connection.connect(**args)

        if result is not None:
            self.fail()

        # Python 3.x seems to have an oddity where StringIO has prefixed null
        # characters (\x00) after we call truncate(). This could be addressed
        # a couple ways...
        #
        #   * Don't use a stdout mock more than once.
        #   * Strip the null characters.
        #
        # Opting for the second (which is admittedly a hack) so the tests are a
        # little nicer.

        stdout_output = stdout_mock.getvalue()
        stdout_mock.truncate(0)
        self.assertEqual(msg, stdout_output.strip().lstrip('\x00'))

    @patch('stem.connection.authenticate')
    def test_auth_success(self, authenticate_mock):
        control_socket = Mock()

        stem.connection._connect_auth(control_socket, None, False, None, None)
        authenticate_mock.assert_called_with(control_socket, None, None)
        authenticate_mock.reset_mock()

        stem.connection._connect_auth(control_socket, 's3krit!!!', False,
                                      '/my/chroot', None)
        authenticate_mock.assert_called_with(control_socket, 's3krit!!!',
                                             '/my/chroot')

    @patch('getpass.getpass')
    @patch('stem.connection.authenticate')
    def test_auth_success_with_password_prompt(self, authenticate_mock,
                                               getpass_mock):
        control_socket = Mock()

        def authenticate_mock_func(controller, password, *args):
            if password is None:
                raise stem.connection.MissingPassword('no password')
            elif password == 'my_password':
                return None  # success
            else:
                raise ValueError('Unexpected authenticate_mock input: %s' %
                                 password)

        authenticate_mock.side_effect = authenticate_mock_func
        getpass_mock.return_value = 'my_password'

        stem.connection._connect_auth(control_socket, None, True, None, None)
        authenticate_mock.assert_any_call(control_socket, None, None)
        authenticate_mock.assert_any_call(control_socket, 'my_password', None)

    @patch('sys.stdout', new_callable=StringIO)
    @patch('stem.connection.authenticate')
    def test_auth_failure(self, authenticate_mock, stdout_mock):
        control_socket = stem.socket.ControlPort(connect=False)

        authenticate_mock.side_effect = stem.connection.IncorrectSocketType(
            'unable to connect to socket')
        self._assert_authenticate_fails_with(
            control_socket, stdout_mock,
            'Please check in your torrc that 9051 is the ControlPort.')

        control_socket = stem.socket.ControlSocketFile(connect=False)

        self._assert_authenticate_fails_with(
            control_socket, stdout_mock,
            'Are you sure the interface you specified belongs to')

        authenticate_mock.side_effect = stem.connection.UnrecognizedAuthMethods(
            'unable to connect', ['telepathy'])
        self._assert_authenticate_fails_with(
            control_socket, stdout_mock,
            'Tor is using a type of authentication we do not recognize...\n\n  telepathy'
        )

        authenticate_mock.side_effect = stem.connection.IncorrectPassword(
            'password rejected')
        self._assert_authenticate_fails_with(control_socket, stdout_mock,
                                             'Incorrect password')

        authenticate_mock.side_effect = stem.connection.UnreadableCookieFile(
            'permission denied', '/tmp/my_cookie', False)
        self._assert_authenticate_fails_with(
            control_socket, stdout_mock,
            "We were unable to read tor's authentication cookie...\n\n  Path: /tmp/my_cookie\n  Issue: permission denied"
        )

        authenticate_mock.side_effect = stem.connection.OpenAuthRejected(
            'crazy failure')
        self._assert_authenticate_fails_with(
            control_socket, stdout_mock,
            'Unable to authenticate: crazy failure')

    def _assert_authenticate_fails_with(self, control_socket, stdout_mock,
                                        msg):
        result = stem.connection._connect_auth(control_socket, None, False,
                                               None, None)

        if result is not None:
            self.fail()  # _connect_auth() was successful

        stdout_output = stdout_mock.getvalue()
        stdout_mock.truncate(0)

        if msg not in stdout_output:
            self.fail(
                "Expected...\n\n%s\n\n... which couldn't be found in...\n\n%s"
                % (msg, stdout_output))