我正在使用 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 状态)?
现在,假设我到目前为止是正确的:
有什么方法可以强制 tor 关闭它的连接端?
是否有任何理由试图强制 tor 关闭它的连接?(我的推理是这样的。我知道到 tor 客户端的最大连接数为 256 个。我希望尽可能多地免费)。否则,处于 TIME_WAIT 状态的连接算作实际连接?例如,如果我有 255 个 ESTABLISHED 连接和一个 TIME_WAIT,我还能与 tor 建立另一个连接吗?
你认为这是测试我的包装类的正确方法,还是有更好的方法来确保控制器正确打开和关闭?
谢谢!