183 lines
6.7 KiB
Python
183 lines
6.7 KiB
Python
import socket
|
||
import threading
|
||
import json
|
||
import hashlib
|
||
import time
|
||
import struct
|
||
|
||
|
||
# 定义 Provider 类,用于处理与协调器的连接和P2P通信
|
||
class Provider:
|
||
def __init__(self, coordinator_host='www.awin-x.top', coordinator_port=5000):
|
||
# 初始化协调器的主机和端口
|
||
self.coordinator_addr = (coordinator_host, coordinator_port)
|
||
# 创建与协调器的TCP连接
|
||
self.coord_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
self.coord_conn.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||
self.coord_conn.connect(self.coordinator_addr)
|
||
self.local_port = self.coord_conn.getsockname()[1]
|
||
# 用于存储认证令牌
|
||
self.token = None
|
||
# 定义可提供的服务及其默认端口
|
||
self.service_ports = {'ssh': 22, 'alist': 5244, 'minecraft': 25565}
|
||
# 存储连接的客户端
|
||
self.connections = {}
|
||
# 用于线程安全操作的锁
|
||
self.lock = threading.Lock()
|
||
|
||
def connect_to_coordinator(self):
|
||
# 发送登录请求
|
||
self._send_json({'action': 'login', 'account': 'admin'})
|
||
response = self._recv_json()
|
||
|
||
# 处理协调器返回的盐值并进行密码哈希验证
|
||
if response.get('status') == 'salt':
|
||
salt = response['salt']
|
||
password_hash = hashlib.sha256((salt + "admin_password").encode()).hexdigest()
|
||
self._send_json({'action': 'auth', 'hash': password_hash})
|
||
response = self._recv_json()
|
||
|
||
# 如果认证成功,存储令牌并注册服务
|
||
if response.get('status') == 'success':
|
||
self.token = response['token']
|
||
print(f"Authenticated. Token: {self.token}")
|
||
|
||
self._send_json({
|
||
'action': 'register_service',
|
||
'services': list(self.service_ports.keys()),
|
||
'token': self.token
|
||
})
|
||
response = self._recv_json()
|
||
if response.get('status') == 'success':
|
||
print("Services registered")
|
||
return True
|
||
print("Connection to coordinator failed")
|
||
return False
|
||
|
||
def handle_punch_request(self, data):
|
||
connector_addr = tuple(data['connector_addr'])
|
||
service_name = data['service_name']
|
||
print(f"Punching hole to connector at {connector_addr}, waiting 10 seconds...")
|
||
|
||
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||
udp_socket.bind(('0.0.0.0', self.local_port))
|
||
# 向对方发送打洞包
|
||
for i in range(10):
|
||
udp_socket.sendto(b'punch punch punch punch', connector_addr)
|
||
time.sleep(0.2)
|
||
udp_socket.close()
|
||
|
||
time.sleep(3)
|
||
|
||
punch_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
punch_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||
punch_sock.settimeout(10)
|
||
punch_sock.bind(('0.0.0.0', self.local_port))
|
||
|
||
try:
|
||
punch_sock.connect(connector_addr)
|
||
print("Successfully connected to connector after delay")
|
||
threading.Thread(
|
||
target=self.handle_connector_connection,
|
||
args=(punch_sock, service_name),
|
||
daemon=True
|
||
).start()
|
||
except socket.error as e:
|
||
print(f"Punching failed: {e}")
|
||
punch_sock.close()
|
||
|
||
def handle_connector_connection(self, sock, service_name):
|
||
# 处理与客户端的连接,启动心跳机制
|
||
threading.Thread(target=self.send_heartbeats, args=(sock,), daemon=True).start()
|
||
|
||
try:
|
||
while True:
|
||
# 接收连接头信息
|
||
header = sock.recv(5)
|
||
if not header:
|
||
break
|
||
|
||
conn_id, data_len = struct.unpack("!I B", header)
|
||
data = sock.recv(data_len) if data_len > 0 else b''
|
||
|
||
if not data:
|
||
with self.lock:
|
||
if conn_id in self.connections:
|
||
self.connections[conn_id].close()
|
||
del self.connections[conn_id]
|
||
continue
|
||
|
||
with self.lock:
|
||
if conn_id not in self.connections:
|
||
service_port = self.service_ports.get(service_name, 22)
|
||
service_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
service_sock.connect(('127.0.0.1', service_port))
|
||
self.connections[conn_id] = service_sock
|
||
threading.Thread(
|
||
target=self.forward_data,
|
||
args=(service_sock, sock, conn_id),
|
||
daemon=True
|
||
).start()
|
||
|
||
self.connections[conn_id].sendall(data)
|
||
except ConnectionResetError:
|
||
pass
|
||
finally:
|
||
sock.close()
|
||
with self.lock:
|
||
for conn_id, service_sock in list(self.connections.items()):
|
||
service_sock.close()
|
||
self.connections.clear()
|
||
|
||
def forward_data(self, src, dst, conn_id):
|
||
# 转发数据
|
||
try:
|
||
while True:
|
||
data = src.recv(4096)
|
||
if not data:
|
||
break
|
||
header = struct.pack("!I B", conn_id, len(data))
|
||
dst.sendall(header + data)
|
||
finally:
|
||
src.close()
|
||
with self.lock:
|
||
if conn_id in self.connections:
|
||
del self.connections[conn_id]
|
||
|
||
def send_heartbeats(self, sock):
|
||
# 发送心跳包以保持连接
|
||
while True:
|
||
try:
|
||
sock.sendall(b'\x00\x00\x00\x00\x00') # Empty heartbeat
|
||
time.sleep(5)
|
||
except:
|
||
break
|
||
|
||
def start(self):
|
||
# 启动提供者,连接到协调器并开始处理请求
|
||
if not self.connect_to_coordinator():
|
||
return
|
||
|
||
try:
|
||
while True:
|
||
data = self._recv_json()
|
||
if data and data.get('action') == 'punch_request':
|
||
self.handle_punch_request(data)
|
||
except (ConnectionResetError, json.JSONDecodeError):
|
||
print("Disconnected from coordinator")
|
||
|
||
def _send_json(self, data):
|
||
# 发送JSON数据
|
||
self.coord_conn.sendall(json.dumps(data).encode())
|
||
|
||
def _recv_json(self):
|
||
# 接收JSON数据
|
||
data = self.coord_conn.recv(4096)
|
||
return json.loads(data.decode()) if data else None
|
||
|
||
|
||
if __name__ == "__main__":
|
||
provider = Provider()
|
||
provider.start()
|