AwinSimpleP2P/provider.py
2025-05-31 15:28:20 +08:00

183 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()