74 lines
1.9 KiB
Python
74 lines
1.9 KiB
Python
import logging
|
|
import threading
|
|
import select
|
|
import socket
|
|
|
|
from typing import Callable
|
|
|
|
logger = logging.getLogger(__name__)
|
|
should_run = {}
|
|
|
|
|
|
def handler(chan, local_port, host, port):
|
|
key = local_port, host, port
|
|
sock = socket.socket()
|
|
try:
|
|
sock.connect((host, port))
|
|
except Exception as e:
|
|
logger.warning('Forwarding request to {}:{} failed: {}'.format(host, port, e))
|
|
return
|
|
|
|
logger.info('Connected! Tunnel open {} -> {} -> {}'.format(
|
|
chan.origin_addr, chan.getpeername(), (host, port)))
|
|
|
|
while should_run.get(key):
|
|
r, w, x = select.select([sock, chan], [], [])
|
|
if sock in r:
|
|
data = sock.recv(1024)
|
|
if len(data) == 0:
|
|
break
|
|
chan.send(data)
|
|
|
|
if chan in r:
|
|
data = chan.recv(1024)
|
|
if len(data) == 0:
|
|
break
|
|
sock.send(data)
|
|
|
|
chan.close()
|
|
sock.close()
|
|
logger.info('Tunnel closed from {}'.format(chan.origin_addr))
|
|
|
|
|
|
def reverse_tunnel(server_port, remote_host, remote_port, transport, bind_addr='') -> Callable:
|
|
def server():
|
|
transport.request_port_forward(bind_addr, server_port)
|
|
key = server_port, remote_host, remote_port
|
|
should_run[key] = True
|
|
|
|
while should_run.get(key):
|
|
try:
|
|
chan = transport.accept(1)
|
|
assert chan is not None
|
|
except Exception as e:
|
|
logger.warning(e)
|
|
continue
|
|
|
|
thr = threading.Thread(
|
|
target=handler, args=(chan, server_port, remote_host, remote_port))
|
|
|
|
thr.setDaemon(True)
|
|
thr.start()
|
|
|
|
return server
|
|
|
|
|
|
def close_tunnel(server_port, remote_host, remote_port):
|
|
key = server_port, remote_host, remote_port
|
|
if key not in should_run:
|
|
logger.info('No such active tunnel: {}:{}:{}'.format(server_port, remote_host, remote_port))
|
|
|
|
del should_run[key]
|
|
|
|
|
|
# vim:sw=4:ts=4:et:
|