【问题标题】:Number of connections to tor via stem library - Controller creates 2 connections but closing the controller removes only one connection通过 stem 库与 tor 的连接数 - 控制器创建 2 个连接,但关闭控制器仅删除一个连接
【发布时间】:2017-01-18 14:52:31
【问题描述】:

我正在使用 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,第二行),一个从 Stem 到 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. 您认为这是测试我的包装类的正确方法,还是有更好的方法来确保控制器正确打开和关闭?

谢谢!

【问题讨论】:

    标签: python tcp tor stem


    【解决方案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 会向您显示同一连接的两端(因为它是本地连接)。所以你可以看到有一个使用端口40636的连接 Tor,然后你也(因为它是一个本地连接)在端口上看到来自你的控制客户端的连接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 眼中打开,它已将客户端从其连接列表中删除。

    希望能回答您的问题。

    【讨论】:

    • 感谢您的详细回复!这很有帮助。
    • @random_person_123 不客气!如果您还有其他问题,请告诉我!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    相关资源
    最近更新 更多