Initial commit of PyFrp project
This commit is contained in:
295
client.py
Normal file
295
client.py
Normal 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()
|
||||
Reference in New Issue
Block a user