通过 stem 库与 tor 的连接数 - 控制器创建 2 个连接,但关闭控制器只会删除一个连接

Number of connections to tor via stem library - Controller creates 2 connections but closing the controller removes only one connection

我正在使用 stem 库并在 Stem 控制器上编写一个薄包装程序 class。

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

这是我目前的代码:

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端口的连接数,创建一个controller,再次统计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,第二行),一个从 stem 到 tor(40606 到 9051,第三行) ).

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

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

    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() 我只关闭了 stem 到 tor 的连接,但 tor 到 stem 的连接仍然有效(至少有一段时间,TIME_WAIT 状态)?

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

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

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

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

谢谢!

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

您可以减少 OS 中的 TIME_WAIT 周期,但这并不能真正解决问题。有关详细信息,请参阅 http://www.isi.edu/touch/pubs/infocomm99/infocomm99-web/ and http://www.linuxbrigade.com/reduce-time_wait-socket-connections/

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

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

之所以看到看起来像 3 个连接,是因为首先是 9051 上的侦听套接字,然后当您连接时,netstat 会向您显示同一连接的两端(因为它是本地连接)。所以你可以看到有一个连接 to Tor 使用端口 40636,然后你也(因为它是一个本地连接)看到连接 from 您的控制客户端也在端口 40636 上。连接的这两个端点实际上代表一个连接。

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

您可以通过将 grep 更改为

从输出中消除本地客户端连接
netstat -ano|grep -E ':9051[[:space:]]+[[:digit:]]'

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

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

Am I correct in thinking that when I do controller.close() I only close the stem to tor connection but the tor to stem connection remains active ( at least for a while, TIME_WAIT state )?

当您执行 controller.close() 时,两者都会关闭。 Tor 不再将连接视为打开状态,它只是由于上述原因被 OS 占用;以确保不会建立使用相同端口组合的另一个连接,并可能在新连接上接受来自先前连接的延迟数据包。它不再在 Tor 的眼中打开,它已将客户端从其连接列表中删除。

希望这能回答您的问题。