zhuhai111 commited on
Commit
34fe2fb
·
verified ·
1 Parent(s): fa0b568

Upload 2 files

Browse files
Files changed (2) hide show
  1. Dockerfile +40 -0
  2. main.py +122 -0
Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use the scottyhardy/docker-remote-desktop as the base image
2
+ FROM scottyhardy/docker-remote-desktop:latest
3
+
4
+ # Set up environment variables
5
+ ENV PYTHONUNBUFFERED=1
6
+ # Set RDP to start automatically
7
+ ENV RDP_SERVER=yes
8
+
9
+ # Install Python dependencies
10
+ RUN apt-get update && \
11
+ apt-get install -y python3 python3-pip python3-venv python3-full && \
12
+ apt-get clean && \
13
+ rm -rf /var/lib/apt/lists/*
14
+
15
+ # Create a working directory
16
+ WORKDIR /app
17
+
18
+ # Copy application files
19
+ COPY main.py /app/
20
+
21
+ # Create and activate virtual environment
22
+ RUN python3 -m venv /app/venv
23
+ ENV PATH="/app/venv/bin:$PATH"
24
+
25
+ # Install Python dependencies in the virtual environment
26
+ RUN /app/venv/bin/pip install --no-cache-dir fastapi uvicorn websockets
27
+
28
+ # Expose both WebSocket tunnel port and RDP port
29
+ EXPOSE 7860 3389
30
+
31
+ # Create startup script to run both services
32
+ RUN echo '#!/bin/bash\n\
33
+ # Start RDP service in background\n\
34
+ /usr/bin/entrypoint.sh &\n\
35
+ # Start WebSocket tunnel\n\
36
+ /app/venv/bin/python /app/main.py\n\
37
+ ' > /app/start.sh && chmod +x /app/start.sh
38
+
39
+ # Set the entrypoint to run the startup script
40
+ CMD ["/app/start.sh"]
main.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # main.py
2
+ import asyncio
3
+ from fastapi import FastAPI, WebSocket
4
+ import uvicorn
5
+
6
+ # 创建 FastAPI 应用实例
7
+ app = FastAPI()
8
+
9
+ @app.websocket("/")
10
+ async def tunnel(websocket: WebSocket):
11
+ """
12
+ WebSocket 隧道入口:
13
+ 1. 接受客户端 WebSocket 连接;
14
+ 2. 等待客户端发送 CONNECT 请求,解析出目标主机及端口;
15
+ 3. 尝试与目标主机建立 TCP 连接;
16
+ 4. 返回 HTTP 200 成功建立隧道;
17
+ 5. 启动双向数据转发(TCP <--> WebSocket)。
18
+ """
19
+ await websocket.accept()
20
+ try:
21
+ # ---------------------------
22
+ # 1. 等待客户端发来的 CONNECT 请求
23
+ # ---------------------------
24
+ # CONNECT 请求格式示例:
25
+ # CONNECT destHost:destPort HTTP/1.1\r\nHost: destHost:destPort\r\n\r\n
26
+ request_text = await websocket.receive_text()
27
+ lines = request_text.splitlines()
28
+ if not lines:
29
+ await websocket.send_text("HTTP/1.1 400 Bad Request\r\n\r\n")
30
+ await websocket.close()
31
+ return
32
+
33
+ # 解析第一行
34
+ first_line = lines[0].strip()
35
+ parts = first_line.split()
36
+ if len(parts) < 3 or parts[0].upper() != "CONNECT":
37
+ await websocket.send_text("HTTP/1.1 400 Bad Request\r\n\r\n")
38
+ await websocket.close()
39
+ return
40
+
41
+ # 从第二个字段中获取目标主机及端口,如 destHost:destPort
42
+ dest = parts[1]
43
+ if ":" not in dest:
44
+ await websocket.send_text("HTTP/1.1 400 Bad Request\r\n\r\n")
45
+ await websocket.close()
46
+ return
47
+
48
+ dest_parts = dest.split(":", 1)
49
+ dest_host = dest_parts[0]
50
+ try:
51
+ dest_port = int(dest_parts[1])
52
+ except Exception:
53
+ await websocket.send_text("HTTP/1.1 400 Bad Request\r\n\r\n")
54
+ await websocket.close()
55
+ return
56
+
57
+ # ---------------------------
58
+ # 2. 建立到目标主机的 TCP 连接
59
+ # ---------------------------
60
+ try:
61
+ reader, writer = await asyncio.open_connection(dest_host, dest_port)
62
+ except Exception as e:
63
+ err_msg = f"HTTP/1.1 502 Bad Gateway\r\n\r\n无法连接 {dest_host}:{dest_port},错误:{e}"
64
+ await websocket.send_text(err_msg)
65
+ await websocket.close()
66
+ return
67
+
68
+ # ---------------------------
69
+ # 3. 向客户端返回 200 成功响应
70
+ # ---------------------------
71
+ await websocket.send_text("HTTP/1.1 200 Connection Established\r\n\r\n")
72
+
73
+ # ---------------------------
74
+ # 4. 双向数据转发
75
+ # ---------------------------
76
+ async def tcp_to_ws():
77
+ """
78
+ 从 TCP 连接中读取数据,通过 WebSocket 以二进制方式发送给客户端
79
+ """
80
+ try:
81
+ while True:
82
+ data = await reader.read(1024)
83
+ if not data:
84
+ break
85
+ await websocket.send_bytes(data)
86
+ except Exception as e:
87
+ # 读取异常或对方关闭连接时退出
88
+ print("tcp_to_ws 异常:", e)
89
+
90
+ async def ws_to_tcp():
91
+ """
92
+ 从客户端通过 WebSocket 发送的数据写入 TCP 连接
93
+ """
94
+ try:
95
+ while True:
96
+ message = await websocket.receive()
97
+ # 接收到的数据可能是文本或二进制,这里尽量以二进制方式处理
98
+ if "bytes" in message:
99
+ data = message["bytes"]
100
+ elif "text" in message:
101
+ # 若收到文本数据,则转换为 bytes(可能只在握手阶段出现)
102
+ data = message["text"].encode("utf-8")
103
+ else:
104
+ break
105
+ writer.write(data)
106
+ await writer.drain()
107
+ except Exception as e:
108
+ print("ws_to_tcp 异常:", e)
109
+
110
+ # 并发执行数据转发任务,任一方向关闭则结束隧道
111
+ await asyncio.gather(tcp_to_ws(), ws_to_tcp())
112
+ except Exception as e:
113
+ print("WebSocket 隧道处理异常:", e)
114
+ finally:
115
+ # 关闭连接
116
+ await websocket.close()
117
+
118
+ # ---------------------------
119
+ # 启动服务器:监听 0.0.0.0:7860
120
+ # ---------------------------
121
+ if __name__ == "__main__":
122
+ uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=False)