1

我正在使用 stem 库,并且正在为 Stem Controller 类编写一个薄包装器。

我对实例化控制器时创建的连接数以及关闭控制器时删除的连接数有一些疑问。

这是我到目前为止的代码:

import logging

import sys
import subprocess
from optparse import OptionParser

import stem
from stem.control import Controller

SOCKET_ERROR_CODE = 2
MISSING_PWD_ERROR_CODE = 3
PWD_FAIL_ERROR_CODE = 4
AUTH_FAIL_ERROR_CODE = 5
UNKNOWN_ERROR_CODE = -1


class StemController():
    def __init__(self):
        # Added print statements for debugging - Yes, I know, shell=True is bad
        print(
            "[__init__ start] ",
            str(int(subprocess.check_output('netstat -na | grep 9051 | wc -l', shell=True).strip()))
        )

        # Controller connection and credentials
        self.tor_router_ip = "127.0.0.1"
        self.tor_router_port = 9051
        self.tor_password = "controllportpassword"  # Add yours to test

        #  Create a controller - This might actually fail
        try:
            # Tried both and one at a time, still two connections
            self.controller = Controller.from_port(
                # address=self.tor_router_ip,
                 port=self.tor_router_port
            )
        except stem.SocketError as e:
            logging.info("SocketError when opening controller. Now existing with code %s." % (
                SOCKET_ERROR_CODE
            ))
            sys.exit(SOCKET_ERROR_CODE)
        except Exception as e:
            logging.exception(e)
            sys.exit(UNKNOWN_ERROR_CODE)

        print(
            "[Controller created] ",
            str(int(subprocess.check_output('netstat -na | grep 9051 | wc -l', shell=True).strip()))
        )

        # Authenticate controller - This might fail as well
        try:
            self.controller.authenticate(password=self.tor_password)
        except stem.connection.MissingPassword:
            logging.info(
                "MissingPassword when authenticating controller. Now existing with code %s." % MISSING_PWD_ERROR_CODE
            )
            self.controller.close()
            sys.exit(MISSING_PWD_ERROR_CODE)
        except stem.connection.PasswordAuthFailed:
            logging.info(
                "PasswordAuthFailed when authenticating controller. Now existing with code %s." % PWD_FAIL_ERROR_CODE
            )
            self.controller.close()
            sys.exit(PWD_FAIL_ERROR_CODE)
        except stem.connection.AuthenticationFailure:
            logging.info(
                "AuthenticationFailure when authenticating controller. Now existing with code %s." % AUTH_FAIL_ERROR_CODE
            )
            self.controller.close()
            sys.exit(AUTH_FAIL_ERROR_CODE)
        except Exception as e:
            logging.exception(e)
            self.controller.close()
            sys.exit(UNKNOWN_ERROR_CODE)

        print(
            "[Controller auth] ",
            str(int(subprocess.check_output('netstat -na | grep 9051 | wc -l', shell=True).strip()))
        )

    def __del__(self):
        init_tor_connections = int(subprocess.check_output('netstat -na | grep 9051 | wc -l', shell=True).strip())
        print(
            "\nDeleting and cleaning up controller. Before cleanup there are %s tor connections." % init_tor_connections
        )

        self.controller.close()

        final_tor_connections = int(subprocess.check_output('netstat -na | grep 9051 | wc -l', shell=True).strip())
        print(
            "After deletion of controller. After cleanup there are %s tor connections." % final_tor_connections
        )


import unittest


class TestStemController(unittest.TestCase):
    def setUp(self):
        #  This is a darknet site that is almost always up
        self.up_site = "deepdot35wvmeyd5.onion"

    def test_stem_controller(self):
        # Make sure that the controller opens and closes correctly
        # Count how many connections on port 9051 we have
        pre_stem_controller = int(subprocess.check_output('netstat -na | grep 9051 | wc -l', shell=True).strip())
        print("pre_stem_controller: ", pre_stem_controller)
        test_stem_controller = StemController()
        # Count how many connections on port 9051 we have after initializing the controller
        post_stem_controller = int(subprocess.check_output('netstat -na | grep 9051 | wc -l', shell=True).strip())
        print("post_stem_controller: ", post_stem_controller)
        # We should have one more connection, since we created another once when we initialized the controller
        self.assertEqual(post_stem_controller, pre_stem_controller + 1)
        # Delete the controller
        del test_stem_controller
        # Count how many connections on port 9051 we have after deleting the controller
        post_del_stem_controller = int(subprocess.check_output('netstat -na | grep 9051 | wc -l', shell=True).strip())
        print("post_del_stem_controller: ", post_del_stem_controller)
        # We should have as many as we had in the beginning
        self.assertEqual(post_del_stem_controller, pre_stem_controller)


def suite():
    test_suite = unittest.TestSuite()
    test_suite.addTest(TestStemController('test_stem_controller'))
    return test_suite


if __name__ == '__main__':
    arguments = sys.argv[1:]
    parse = OptionParser("This is the stem controller script script.")
    parse.add_option(
        "-u",
        "--unittests",
        help="Action: Run unittests.",
        default=False,
        action='store_true'
    )

    (options, arguments) = parse.parse_args()

    if options.unittests:
        test_suite = suite()
        unittest.TextTestRunner().run(test_suite)
        logging.info("Unittests done. Now existing.")
        sys.exit()

TL; 代码的 DR:计算端口 9051 上的连接数,创建一个控制器,再次计算端口 9051 上的连接数,并断言数量增加一。除了断言连接数减少了一个之外,删除也是一样的。

我用 python3 stem_code.py -u 运行我的代码并得到,例如:

pre_stem_controller:  1
[__init__ start]  1
[Controller created]  3
[Controller auth]  3
post_stem_controller:  3
F
Deleting and cleaning up controller. Before cleanup there are 3 tor connections.
After deletion of controller. After cleanup there are 2 tor connections.

======================================================================
FAIL: test_stem_controller (__main__.TestStemController)
----------------------------------------------------------------------
Traceback (most recent call last):
  self.assertEqual(post_stem_controller, pre_stem_controller + 1)
AssertionError: 3 != 2

----------------------------------------------------------------------
Ran 1 test in 0.050s

FAILED (failures=1)

我认为最相关的部分是:

[__init__ start]  1
[Controller created]  3

我的第一个问题是:为什么在这里创建两个连接?

我已经提出了一个理论为什么会这样,但我不确定。

很好奇这两个连接是什么,我在实例化控制器后这样做了:

netstat -na | grep 9051

结果是这样的:

tcp        0      0 127.0.0.1:9051          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:9051          127.0.0.1:40606         ESTABLISHED
tcp        0      0 127.0.0.1:40606         127.0.0.1:9051          ESTABLISHED

因此,我让 tor 客户端在端口 9051(第一行)和两个连接上侦听,一个从 tor 到 stem(9051 到 40606,第二行),一个从 tor 到 tor(40606 到 9051,第三行)。

这种双重连接,tor to stem 和 stem to to 是创建两个连接的原因吗?

在此之后,我决定接受 2 个连接按原样创建的事实。因此,我将单元测试从 +1 更改为 +2 以通过该特定断言。测试继续进行,但未能通过初始化前连接数等于删除后连接数的断言。

    self.assertEqual(post_del_stem_controller, pre_stem_controller)
AssertionError: 2 != 1

使用与连接情况相同的过程,我这样做netstat -na了:

tcp        0      0 127.0.0.1:9051          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:9051          127.0.0.1:40636         TIME_WAIT

Tor 到 Stem 的连接似乎仍然存在。

我是否正确地认为,当我这样做时, controller.close() 我只关闭了茎到 tor 的连接,但 tor 到茎的连接仍然处于活动状态(至少在一段时间内,TIME_WAIT 状态)?

现在,假设我到目前为止是正确的:

  1. 有什么方法可以强制 tor 关闭它的连接端?

  2. 是否有任何理由试图强制 tor 关闭它的连接?(我的推理是这样的。我知道到 tor 客户端的最大连接数为 256 个。我希望尽可能多地免费)。否则,处于 TIME_WAIT 状态的连接算作实际连接?例如,如果我有 255 个 ESTABLISHED 连接和一个 TIME_WAIT,我还能与 tor 建立另一个连接吗?

  3. 你认为这是测试我的包装类的正确方法,还是有更好的方法来确保控制器正确打开和关闭?

谢谢!

4

1 回答 1

1
  1. 就 Tor 而言,连接已关闭。也就是说,它已释放该连接并且不再认为该客户端已连接。当套接字处于TIME_WAIT状态时,套接字会关闭,但操作系统会保留它,以确保没有来自旧连接的延迟数据包到达,这些数据包可能会被来自同一组端口的后续连接接受(例如,您的示例中的 40606)。

您可以减少TIME_WAIT操作系统中的时间段,但这并不能真正解决问题。有关更多信息,请参见http://www.isi.edu/touch/pubs/infocomm99/infocomm99-web/http://www.linuxbrigade.com/reduce-time_wait-socket-connections/

  1. 不会。一旦您关闭与控制器的连接,Tor 就会减少连接的客户端数量,以便腾出空间来接受更多。我不会担心的。

  2. 可能不是。当您可以向控制器发出命令并读取响应时,为什么要使用 netstat 来测试您的连接?

您看到看起来像 3 个连接的原因是因为首先是 9051 上的侦听套接字,然后当您连接时,netstat 向您显示同一连接的两端(因为它是本地的)。因此,您可以看到使用 port 与 Tor 建立了连接然后40636您(因为它是本地连接)也可以在 port 上看到来自控制客户端的连接40636。连接的这两端实际上代表一个连接。

因此,只要您连接到本地 Tor 实例,netstat 中的行数就会为每个连接增加 2。

您可以通过将 grep 更改为从输出中消除本地客户端连接

netstat -ano|grep -E ':9051[[:space:]]+[[:digit:]]'

然后,您可以进一步过滤掉 LISTENING 连接:

netstat -ano|grep -E ':9051[[:space:]]+[[:digit:]]'|grep -v 'LISTENING'

我是否正确地认为当我执行 controller.close() 时,我只关闭了 tor 的连接,但 tor 的连接仍然处于活动状态(至少在一段时间内,TIME_WAIT 状态)?

当你这样做时controller.close(),两者都是关闭的。Tor 不再认为连接是打开的,它只是因为上述原因被操作系统持有;确保不会建立使用相同端口组合的另一个连接,并可能在新连接上接受来自前一个连接的延迟数据包。它不再在 Tor 的眼中打开,它已经从它的连接列表中删除了客户端。

希望能回答你的问题。

于 2017-01-18T16:10:37.770 回答