使用回调时无法从传入的 paramiko 端口转发连接读取数据。
请参阅此要点进行演示:
https://gist.github.com/tatome/0d3e6479f35b25bbb31c9f94610eab6b
这里有一个示例脚本,演示如何使用 paramiko 设置反向隧道。 该脚本请求端口转发,然后在主线程中保持
accept
连接,并将处理这些连接委托给另一个线程:
transport.request_port_forward("", server_port)
while True:
chan = transport.accept(1000)
if chan is None:
continue
thr = threading.Thread(
target=handler, args=(chan, remote_host, remote_port)
)
不幸的是,使用这种方法,我们不知道传入连接访问哪个端口,因此我们不能在我们不同对待的不同服务器端口上拥有多个隧道。
但是,我们可以将
handler
参数传递给
request_port_forward
,handler
确实会获取客户端访问的主机名和端口:def handler(channel, source_address, target_address):
target_host, target_port = target_address
...
client.get_transport().request_port_forward('', port, handler)
问题是:由于某种原因,通过 transport.accept()
检索的通道和传递给
handler
的通道对我来说表现不同。当我这样做时:
client.connect(
'localhost',
22,
username=USER,
look_for_keys=True
)
def handler(channel, source_address, target_address):
print("Selecting...")
selects = select.select([channel],[],[])
print("Selected: " + str(selects))
transport = client.get_transport()
p = transport.request_port_forward('', 30000)
print(p)
channel = transport.accept(1000)
handler(channel, None, None)
然后,在外壳上:
wget localhost:30000
我明白了:
30000
Selecting...
Selected: ([<paramiko.Channel 0 (open) window=2097152 in-buffer=130 -> <paramiko.Transport at 0x43b80fd0 (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>], [], [])
如果我将处理程序传递给
request_port_forward
,但是,像这样:
def handler(channel, source_address, target_address):
print("Selecting...")
selects = select.select([channel],[],[])
print("Selected: " + str(selects))
transport = client.get_transport()
transport.request_port_forward('', 30000, handler)
我明白了:
Selecting...
当我调试脚本时,它会停在显示
selects = select.select([channel],[],[])
的行中。
wget
表示它发送了 HTTP 请求,但我的脚本从未从连接中读取任何内容。
问题:我做错了什么?
select.select([channel])
transport.accept()
返回还是传递给处理程序?如何对多个不同的反向隧道使用相同的传输?handler
在主线程上调用,而在第二个代码中,
handler
在另一个线程上调用。我不认为 Paramiko 是线程安全的。 我会尝试的是仅使用
handler
来通知主线程循环(在
request_port_forward
之后)隧道已打开并让它处理它。快速而肮脏的代码(只是为了测试我是否正确)
tunnel_channel = None
def handler(channel, source_address, target_address):
global tunnel_channel
print("Tunnel opened.")
tunnel_channel = channel
transport = client.get_transport()
transport.request_port_forward('', 30000, handler)
while True:
if tunnel_channel != None:
print("Selecting...")
selects = select.select([channel],[],[])
print("Selected: " + str(selects))
time.sleep(1)