| |
|
| | import subprocess
|
| | import threading
|
| | import time
|
| | import os
|
| | import sys
|
| | import logging
|
| | import traceback
|
| | import socket
|
| | import select
|
| | import fcntl
|
| |
|
| |
|
| | logging.basicConfig(
|
| | level=logging.INFO,
|
| | format='%(asctime)s - %(levelname)s - %(message)s'
|
| | )
|
| | logger = logging.getLogger(__name__)
|
| |
|
| |
|
| | logger.info("=" * 50)
|
| | logger.info("VNC SSH反向隧道服务启动")
|
| | logger.info(f"当前工作目录: {os.getcwd()}")
|
| | logger.info(f"Python版本: {sys.version}")
|
| | logger.info("=" * 50)
|
| |
|
| |
|
| | SSH_SERVER = "www.ip188.net"
|
| | SSH_PORT = 30001
|
| | SSH_USERNAME = "aauser"
|
| | SSH_PASSWORD = "aa123456"
|
| | LOCAL_PORT = 5901
|
| | REMOTE_PORT = 13698
|
| |
|
| | def read_nonblocking(pipe):
|
| | """非阻塞读取管道内容"""
|
| | output = ""
|
| | if pipe:
|
| |
|
| | flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
|
| | fcntl.fcntl(pipe, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
| |
|
| |
|
| | reads, _, _ = select.select([pipe], [], [], 0)
|
| | if pipe in reads:
|
| | try:
|
| | data = pipe.read()
|
| | if data:
|
| | output = data.decode('utf-8', errors='replace')
|
| | except (OSError, IOError) as e:
|
| | logger.error(f"读取管道错误: {e}")
|
| | return output
|
| |
|
| | def create_ssh_tunnel_paramiko():
|
| | """使用paramiko库创建SSH反向隧道"""
|
| | try:
|
| | import paramiko
|
| | logger.info("使用paramiko库创建SSH反向隧道")
|
| |
|
| | while True:
|
| | try:
|
| |
|
| | ssh_client = paramiko.SSHClient()
|
| | ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
| | logger.info(f"连接到SSH服务器 {SSH_SERVER}:{SSH_PORT}...")
|
| |
|
| | ssh_client.connect(
|
| | SSH_SERVER,
|
| | port=SSH_PORT,
|
| | username=SSH_USERNAME,
|
| | password=SSH_PASSWORD,
|
| | timeout=60,
|
| | allow_agent=False,
|
| | look_for_keys=False
|
| | )
|
| |
|
| |
|
| | transport = ssh_client.get_transport()
|
| | transport.set_keepalive(60)
|
| |
|
| |
|
| | logger.info(f"建立反向隧道 localhost:{LOCAL_PORT} -> {REMOTE_PORT}")
|
| | transport.request_port_forward("", REMOTE_PORT)
|
| |
|
| |
|
| | class ForwardServer(threading.Thread):
|
| | def __init__(self, transport):
|
| | threading.Thread.__init__(self)
|
| | self.transport = transport
|
| | self.daemon = True
|
| |
|
| | def run(self):
|
| | logger.info("SSH VNC隧道已建立,等待连接...")
|
| |
|
| | try:
|
| | while transport.is_active():
|
| | try:
|
| | chan = transport.accept(60)
|
| | if chan is None:
|
| | continue
|
| |
|
| |
|
| | thr = threading.Thread(target=self.handler, args=(chan,))
|
| | thr.daemon = True
|
| | thr.start()
|
| | except Exception as e:
|
| | if transport.is_active():
|
| | logger.error(f"处理隧道连接时出错: {e}")
|
| | else:
|
| | break
|
| |
|
| | logger.info("隧道传输已关闭")
|
| | except Exception as e:
|
| | logger.error(f"转发服务器错误: {e}")
|
| |
|
| | def handler(self, chan):
|
| | try:
|
| | sock = socket.socket()
|
| | try:
|
| | sock.connect(('127.0.0.1', LOCAL_PORT))
|
| | except ConnectionRefusedError:
|
| | logger.error(f"连接本地VNC端口 {LOCAL_PORT} 被拒绝")
|
| | chan.close()
|
| | return
|
| |
|
| | logger.info(f"处理转发连接: {REMOTE_PORT} -> localhost:{LOCAL_PORT}")
|
| |
|
| |
|
| | while True:
|
| | 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)
|
| | except Exception as e:
|
| | logger.error(f"处理VNC连接时出错: {e}")
|
| | finally:
|
| | try:
|
| | sock.close()
|
| | chan.close()
|
| | except:
|
| | pass
|
| |
|
| |
|
| | forward_server = ForwardServer(transport)
|
| | forward_server.start()
|
| |
|
| |
|
| | while transport.is_active():
|
| | time.sleep(60)
|
| | logger.info("SSH VNC隧道保持活跃状态")
|
| |
|
| | logger.info("SSH连接已关闭,准备重新连接")
|
| |
|
| | except Exception as e:
|
| | logger.error(f"SSH VNC隧道错误: {e}")
|
| | logger.error(traceback.format_exc())
|
| | time.sleep(10)
|
| |
|
| | except ImportError:
|
| | logger.error("未找到paramiko库,尝试安装...")
|
| | try:
|
| | subprocess.run([sys.executable, "-m", "pip", "install", "paramiko"], check=True)
|
| | logger.info("paramiko库安装成功,重新尝试创建隧道")
|
| | create_ssh_tunnel_paramiko()
|
| | except Exception as install_error:
|
| | logger.error(f"安装paramiko失败: {install_error}")
|
| | create_ssh_tunnel_command()
|
| |
|
| | def create_ssh_tunnel_command():
|
| | """使用ssh命令创建反向隧道"""
|
| | logger.info("使用ssh命令创建VNC反向隧道")
|
| |
|
| | while True:
|
| | try:
|
| |
|
| | ssh_check = subprocess.run(["which", "ssh"], capture_output=True, text=True)
|
| | if ssh_check.returncode != 0:
|
| | logger.error("未找到ssh命令,请确保已安装openssh-client")
|
| | time.sleep(30)
|
| | continue
|
| |
|
| |
|
| | ssh_cmd = [
|
| | "ssh",
|
| | "-o", "StrictHostKeyChecking=no",
|
| | "-o", "UserKnownHostsFile=/dev/null",
|
| | "-N",
|
| | "-R", f"{REMOTE_PORT}:localhost:{LOCAL_PORT}",
|
| | "-p", str(SSH_PORT),
|
| | f"{SSH_USERNAME}@{SSH_SERVER}"
|
| | ]
|
| |
|
| |
|
| | use_sshpass = False
|
| | sshpass_check = subprocess.run(["which", "sshpass"], capture_output=True, text=True)
|
| | if sshpass_check.returncode == 0:
|
| | use_sshpass = True
|
| | ssh_cmd = ["sshpass", "-p", SSH_PASSWORD] + ssh_cmd
|
| | logger.info("使用sshpass提供密码")
|
| |
|
| | logger.info(f"执行SSH命令: {' '.join(ssh_cmd if not use_sshpass else ['sshpass', '-p', '********'] + ssh_cmd[3:])}")
|
| |
|
| |
|
| | process = subprocess.Popen(
|
| | ssh_cmd,
|
| | stdout=subprocess.PIPE,
|
| | stderr=subprocess.PIPE,
|
| | stdin=subprocess.PIPE if not use_sshpass else None
|
| | )
|
| |
|
| |
|
| | if not use_sshpass:
|
| | try:
|
| | process.stdin.write(f"{SSH_PASSWORD}\n".encode())
|
| | process.stdin.flush()
|
| | except:
|
| | logger.error("无法提供密码")
|
| |
|
| | logger.info("SSH VNC隧道已启动")
|
| |
|
| |
|
| | while process.poll() is None:
|
| |
|
| | stdout_data = read_nonblocking(process.stdout)
|
| | stderr_data = read_nonblocking(process.stderr)
|
| |
|
| | if stdout_data:
|
| | logger.info(f"SSH输出: {stdout_data.strip()}")
|
| | if stderr_data:
|
| | logger.error(f"SSH错误: {stderr_data.strip()}")
|
| |
|
| | time.sleep(5)
|
| |
|
| |
|
| | exit_code = process.poll()
|
| | logger.info(f"SSH VNC隧道进程退出,退出码: {exit_code}")
|
| |
|
| |
|
| | time.sleep(10)
|
| |
|
| | except Exception as e:
|
| | logger.error(f"创建SSH VNC隧道时出错: {e}")
|
| | logger.error(traceback.format_exc())
|
| | time.sleep(10)
|
| |
|
| | def main():
|
| | """主函数"""
|
| | logger.info("启动SSH VNC反向隧道服务")
|
| |
|
| |
|
| | try:
|
| |
|
| | import paramiko
|
| | create_ssh_tunnel_paramiko()
|
| | except ImportError:
|
| | logger.warning("未找到paramiko库,尝试使用ssh命令")
|
| | create_ssh_tunnel_command()
|
| |
|
| | if __name__ == "__main__":
|
| | try:
|
| | main()
|
| | except Exception as e:
|
| | logger.error(f"主程序发生错误: {e}")
|
| | logger.error(traceback.format_exc())
|
| | sys.exit(1) |