Initial commit of PyFrp project

This commit is contained in:
GVSADS
2026-01-29 22:53:58 +08:00
commit 9cee40db86
12 changed files with 1769 additions and 0 deletions

295
client.py Normal file
View File

@@ -0,0 +1,295 @@
import socket
import threading
import json
import traceback
import sys
import time
class PortForwardClient:
def __init__(self, ServerDomain="127.0.0.1", ServerPort=5000, Forwards=None, Key="07A36AEF1907843"):
self.ServerDomain = ServerDomain
self.ServerPort = ServerPort
self.Forwards = Forwards or []
self.Key = Key
self.ServerSocket = None
self.Running = True
self.ForwardMap = {}
self.ConnectionMap = {}
self.Lock = threading.Lock()
self.Buffer = b''
self.MessageSeparator = b'|||'
def Start(self):
try:
self.ServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.ServerSocket.connect((self.ServerDomain, self.ServerPort))
print(f"Connected to server {self.ServerDomain}:{self.ServerPort}")
self.Authenticate()
threading.Thread(target=self.ReceiveFromServer, daemon=True).start()
self.SetupForwards()
while self.Running:
time.sleep(5)
except Exception as e:
print(f"Client start error: {e}")
traceback.print_exc()
self.Stop()
def Stop(self):
self.Running = False
with self.Lock:
for forwardId, forwardData in self.ForwardMap.items():
try:
self.SendToServer({'type': 'close_forward', 'forward_id': forwardId})
except:
pass
for connId, conn in forwardData['connections'].items():
try:
conn.close()
except:
pass
self.ForwardMap.clear()
self.ConnectionMap.clear()
if self.ServerSocket:
try:
self.ServerSocket.close()
except:
pass
print("Client stopped")
def Authenticate(self):
self.SendToServer({'type': 'auth', 'key': self.Key})
response = self.ServerSocket.recv(4096)
if not response:
raise ConnectionError("Server closed connection during authentication")
self.Buffer += response
self.ProcessBuffer()
def SetupForwards(self):
for forward in self.Forwards:
forwardDomain = forward.get('forward_domain', '127.0.0.1')
forwardPort = forward.get('forward_port')
targetPort = forward.get('target_port')
mode = forward.get('mode', 'tcp').upper()
if not all([forwardPort, targetPort]):
print("Invalid forward configuration, skipping")
continue
self.SendToServer({
'type': 'forward_request',
'forward_domain': forwardDomain,
'forward_port': forwardPort,
'target_port': targetPort,
'mode': mode
})
def ReceiveFromServer(self):
while self.Running and self.ServerSocket:
try:
self.ServerSocket.settimeout(1)
data = self.ServerSocket.recv(4096)
if not data:
print("Server disconnected")
self.Running = False
break
self.Buffer += data
self.ProcessBuffer()
except socket.timeout:
continue
except Exception as e:
print(f"Server communication error: {e}")
traceback.print_exc()
self.Running = False
break
def ProcessBuffer(self):
while self.MessageSeparator in self.Buffer:
msgEnd = self.Buffer.index(self.MessageSeparator)
messageData = self.Buffer[:msgEnd]
self.Buffer = self.Buffer[msgEnd + len(self.MessageSeparator):]
try:
message = json.loads(messageData.decode('utf-8'))
self.ProcessServerMessage(message)
except json.JSONDecodeError:
print("Received invalid JSON from server")
except Exception as e:
print(f"Error processing server data: {e}")
traceback.print_exc()
def ProcessServerMessage(self, message):
if message.get('type') == 'forward_response':
self.HandleForwardResponse(message)
elif message.get('type') == 'new_connection':
self.HandleNewConnection(message)
elif message.get('type') == 'data':
self.HandleData(message)
elif message.get('type') == 'close_connection':
self.HandleCloseConnection(message)
elif message.get('type') == 'error':
print(f"Server error: {message.get('message')}")
def HandleForwardResponse(self, message):
if message.get('success'):
forwardId = message.get('forward_id')
targetPort = message.get('target_port')
forwardConfig = next((f for f in self.Forwards if f.get('target_port') == targetPort), None)
if forwardConfig and forwardId:
with self.Lock:
self.ForwardMap[forwardId] = {
'config': forwardConfig,
'connections': {}
}
print(f"Forward established: {forwardId}")
else:
print(f"Received forward response for unknown target port {targetPort}")
else:
print(f"Forward request failed: {message.get('message')}")
def HandleNewConnection(self, message):
forwardId = message.get('forward_id')
connId = message.get('conn_id')
if not all([forwardId, connId]):
return
with self.Lock:
if forwardId not in self.ForwardMap:
print(f"Received connection for unknown forward {forwardId}")
return
forwardData = self.ForwardMap[forwardId]
config = forwardData['config']
try:
if config.get('mode', 'tcp').upper() == 'TCP':
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.connect((config['forward_domain'], config['forward_port']))
with self.Lock:
forwardData['connections'][connId] = conn
self.ConnectionMap[connId] = forwardId
threading.Thread(target=self.ForwardToServer, args=(forwardId, connId, conn), daemon=True).start()
print(f"Established connection {connId} for forward {forwardId}")
else:
print(f"Unsupported mode for forward {forwardId}")
except Exception as e:
print(f"Error establishing connection for {forwardId}: {e}")
traceback.print_exc()
self.SendToServer({
'type': 'close_connection',
'forward_id': forwardId,
'conn_id': connId
})
def ForwardToServer(self, forwardId, connId, conn):
try:
while self.Running:
conn.settimeout(1)
try:
with self.Lock:
if connId not in self.ConnectionMap or self.ConnectionMap[connId] != forwardId:
break
data = conn.recv(4096)
if not data:
break
self.SendToServer({
'type': 'data',
'forward_id': forwardId,
'conn_id': connId,
'data': data.hex()
})
except socket.timeout:
continue
except Exception as e:
print(f"Forward to server error: {e}")
traceback.print_exc()
break
finally:
try:
conn.close()
except:
pass
with self.Lock:
if forwardId in self.ForwardMap and connId in self.ForwardMap[forwardId]['connections']:
del self.ForwardMap[forwardId]['connections'][connId]
if connId in self.ConnectionMap:
del self.ConnectionMap[connId]
self.SendToServer({
'type': 'close_connection',
'forward_id': forwardId,
'conn_id': connId
})
print(f"Closed connection {connId} for forward {forwardId}")
def HandleData(self, message):
forwardId = message.get('forward_id')
connId = message.get('conn_id')
dataHex = message.get('data')
if not all([forwardId, connId, dataHex]):
return
try:
data = bytes.fromhex(dataHex)
with self.Lock:
if forwardId not in self.ForwardMap or connId not in self.ForwardMap[forwardId]['connections']:
print(f"Received data for unknown connection {connId}")
return
conn = self.ForwardMap[forwardId]['connections'][connId]
conn.sendall(data)
except Exception as e:
print(f"Data handling error: {e}")
traceback.print_exc()
self.SendToServer({
'type': 'close_connection',
'forward_id': forwardId,
'conn_id': connId
})
def HandleCloseConnection(self, message):
forwardId = message.get('forward_id')
connId = message.get('conn_id')
with self.Lock:
if forwardId in self.ForwardMap and connId in self.ForwardMap[forwardId]['connections']:
try:
self.ForwardMap[forwardId]['connections'][connId].close()
except:
pass
del self.ForwardMap[forwardId]['connections'][connId]
if connId in self.ConnectionMap:
del self.ConnectionMap[connId]
print(f"Connection {connId} for forward {forwardId} closed by server")
def SendToServer(self, message):
if not self.ServerSocket or not self.Running:
return
try:
data = json.dumps(message).encode('utf-8') + self.MessageSeparator
self.ServerSocket.sendall(data)
except Exception as e:
print(f"Error sending to server: {e}")
traceback.print_exc()
self.Running = False
def main():
config = {
"ServerDomain": "127.0.0.1",
"ServerPort": 5000,
"Key": "07A36AEF1907843",
"Forwards": [
{
"forward_domain": "127.0.0.1",
"forward_port": 36667,
"target_port": 5002,
"mode": "TCP"
}
]
}
if len(sys.argv) > 1:
try:
with open(sys.argv[1], 'r') as f:
config.update(json.load(f))
except Exception as e:
print(f"Error loading config file: {e}")
traceback.print_exc()
client = PortForwardClient(
ServerDomain=config["ServerDomain"],
ServerPort=int(config["ServerPort"]),
Forwards=config["Forwards"],
Key=config["Key"]
)
client.Start()
if __name__ == "__main__":
main()