#python #networking #python-asyncio #tls1.2 #python-3.8
#python #сеть #python-asyncio #tls1.2 #python-3.8
Вопрос:
Я пытаюсь создать RDP-соединение с виртуальной машиной Windows 10, используя протокол python asyncio. После первого обмена сообщениями, согласовывающего соединение TLS, необходимо обновить соединение до TLS, для чего я использую start_tls. Все работает нормально, пока я не попытаюсь закрыть соединение после второго пакета, вызвав transport.close(). В документации говорится, что метод «connection_lost» экземпляра протокола вызывается с None после того, как все выходные буферы очищены (источник). В моем случае вызывается close() и вызов завершается, но connection_lost() никогда не вызывается, что заставляет мое приложение ждать, пока сервер не отправит сообщение RST после тайм-аута, которое правильно запускает connection_lost . Это происходит, если сервер согласовывает TLS 1.2, но не для TLS 1.3. Я проверил, что выходной буфер пуст, и я просмотрел исходный код cpython, но не смог выяснить, где он зависает. С помощью Wireshark я увидел, что при вызове close() ничего не происходит, мой клиент ничего не отправляет на сервер. Я использую python 3.8.5 в Ubuntu 20.04.
У кого-нибудь есть идея, почему connection_lost не вызывается?
Смотрите мой минимальный рабочий пример ниже:
#!/usr/bin/env python3
import asyncio
import ssl
import binascii
# first two packets for RDP, tested thoroughly
PACKET_NEGO = binascii.unhexlify("0300002d28e00000000000436f6f6b69653a206d737473686173683d6c696f6e6c656c0d0a0100080001000000")
PACKET_CONN = binascii.unhexlify("030001ca02f0807f658201be0401010401010101ff30200202002202020002020200000202000102020000020200010202ffff020200023020020200010202000102020001020200010202000002020001020204200202000230200202ffff0202fc170202ffff0202000102020000020200010202ffff020200020482014b000500147c00018142000800100001c00044756361813401c0d800040008000004000301ca03aa09040000280a00006e006f0076006f007300690062006900720073006b000000000000000000000004000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ca01000000000018000b0001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000002c00c00000000000000000003c0440005000000636c697072647200c0a00000726470736e640000c0000000736e646462670000c0000000726470647200000080800000647264796e766300c000000004c00c000d00000000000000")
class RDPConnection(asyncio.Protocol):
def __init__(self, on_con_close, on_tls, loop, ip):
self.on_tls = on_tls
self.on_con_close = on_con_close
self.loop = loop
self.state = 0
self.ip = ip
def connection_made(self, transport):
self.transport = transport
if self.state == 0:
transport.write(PACKET_NEGO) # write first packet on initial connection
else:
transport.write(PACKET_CONN) # write second packet on TLS connection
def data_received(self, data):
if self.state == 0:
self.state = 1
self.on_tls.set_result(True) # start TLS connection
else:
print("closing connection")
self.transport.close()
print("closed connection")
def connection_lost(self, exc):
print("connection lost called")
self.on_con_close.set_result(True)
print("{}: {}".format(self.ip, exc))
def _timeout(self):
if self.transport:
self.transport.close()
async def handle_connection(loop, ip, timeout):
on_tls = loop.create_future() # called when connection is ready to be upgraded to TLS
con_close = loop.create_future() # called when connection is closed
try:
transport, protocol = await asyncio.wait_for(loop.create_connection(lambda: RDPConnection(con_close, on_tls, loop, ip), ip, 3389), timeout=timeout)
except:
return
await on_tls # wait for first response
ssl_ctx = ssl._create_unverified_context() # VM uses self signed certificate
try:
transport2 = await asyncio.wait_for(loop.start_tls(transport, protocol, ssl_ctx), timeout=timeout) # upgrade to TLS
protocol.connection_made(transport2) # set TLS transport
except:
return
await con_close # wait for connection_lost to be called
return
async def main():
loop = asyncio.get_running_loop()
await handle_connection(loop, "my VM", 10)
asyncio.run(main())
Вывод на моей машине:
closing connection
closed connection
connection lost called
my VM: [Errno 104] Connection reset by peer
Пожалуйста, обратите внимание, что «соединение с потерянным вызовом» вызывается только тогда, когда соединение сбрасывается сервером (через ~ 30 секунд)
Ответ №1:
transport.abort()
это ваш друг.
Комментарии:
1. Было бы лучше, если бы вы могли добавить больше деталей….