Initial commit of PyFrp project
This commit is contained in:
343
server.py
Normal file
343
server.py
Normal file
@@ -0,0 +1,343 @@
|
||||
import socket
|
||||
import threading
|
||||
import json
|
||||
import traceback
|
||||
import sys
|
||||
import re
|
||||
from collections import defaultdict
|
||||
|
||||
class PortForwardServer:
|
||||
def __init__(self, InternalDataPort=5000, AllowedPortRange="5001-5500", MaxPortsPerClient=5, Key="07A36AEF1907843"):
|
||||
self.InternalDataPort = InternalDataPort
|
||||
self.AllowedPortRange = AllowedPortRange
|
||||
self.MaxPortsPerClient = MaxPortsPerClient
|
||||
self.Key = Key
|
||||
self.ParsePortRange()
|
||||
self.ServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.ServerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.Clients = {}
|
||||
self.ClientLocks = defaultdict(threading.Lock)
|
||||
self.ForwardMap = {}
|
||||
self.ForwardLocks = defaultdict(threading.Lock)
|
||||
self.Running = True
|
||||
self.MessageSeparator = b'|||'
|
||||
|
||||
def ParsePortRange(self):
|
||||
match = re.match(r'^(\d+)-(\d+)$', self.AllowedPortRange)
|
||||
if not match:
|
||||
raise ValueError("Invalid port range format")
|
||||
self.MinPort = int(match.group(1))
|
||||
self.MaxPort = int(match.group(2))
|
||||
if self.MinPort >= self.MaxPort or self.MinPort < 1 or self.MaxPort > 65535:
|
||||
raise ValueError("Invalid port range values")
|
||||
|
||||
def IsPortAllowed(self, port):
|
||||
return self.MinPort <= port <= self.MaxPort
|
||||
|
||||
def Start(self):
|
||||
try:
|
||||
self.ServerSocket.bind(('0.0.0.0', self.InternalDataPort))
|
||||
self.ServerSocket.listen(5)
|
||||
print(f"Server started on port {self.InternalDataPort}")
|
||||
acceptThread = threading.Thread(target=self.AcceptClients, daemon=True)
|
||||
acceptThread.start()
|
||||
while self.Running:
|
||||
cmd = input("Enter 'exit' to stop server: ")
|
||||
if cmd.lower() == 'exit':
|
||||
self.Running = False
|
||||
break
|
||||
self.Stop()
|
||||
except Exception as e:
|
||||
print(f"Server start error: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
def Stop(self):
|
||||
self.Running = False
|
||||
self.ServerSocket.close()
|
||||
for clientId, clientData in self.Clients.items():
|
||||
with self.ClientLocks[clientId]:
|
||||
if clientData['socket']:
|
||||
try:
|
||||
clientData['socket'].close()
|
||||
except:
|
||||
pass
|
||||
with self.ForwardLocks[clientId]:
|
||||
for forwardId, forwardData in clientData['forwards'].items():
|
||||
try:
|
||||
forwardData['server'].close()
|
||||
except:
|
||||
pass
|
||||
print("Server stopped")
|
||||
|
||||
def AcceptClients(self):
|
||||
while self.Running:
|
||||
try:
|
||||
clientSocket, addr = self.ServerSocket.accept()
|
||||
print(f"New client connection from {addr}")
|
||||
clientThread = threading.Thread(target=self.HandleClient, args=(clientSocket, addr), daemon=True)
|
||||
clientThread.start()
|
||||
except Exception as e:
|
||||
if self.Running:
|
||||
print(f"Accept error: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
def HandleClient(self, clientSocket, addr):
|
||||
clientId = f"{addr[0]}:{addr[1]}"
|
||||
self.Clients[clientId] = {'socket': clientSocket, 'forwards': {}, 'addr': addr, 'buffer': b''}
|
||||
try:
|
||||
while self.Running:
|
||||
clientSocket.settimeout(30)
|
||||
try:
|
||||
data = clientSocket.recv(4096)
|
||||
if not data:
|
||||
print(f"Client {clientId} disconnected")
|
||||
break
|
||||
self.Clients[clientId]['buffer'] += data
|
||||
self.ProcessBuffer(clientId)
|
||||
except socket.timeout:
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"Client communication error: {e}")
|
||||
traceback.print_exc()
|
||||
break
|
||||
finally:
|
||||
with self.ClientLocks[clientId]:
|
||||
if clientId in self.Clients:
|
||||
del self.Clients[clientId]
|
||||
with self.ForwardLocks[clientId]:
|
||||
for forwardId in list(self.ForwardMap.keys()):
|
||||
if forwardId.startswith(clientId):
|
||||
del self.ForwardMap[forwardId]
|
||||
try:
|
||||
clientSocket.close()
|
||||
except:
|
||||
pass
|
||||
print(f"Client {clientId} handler cleaned up")
|
||||
|
||||
def ProcessBuffer(self, clientId):
|
||||
clientData = self.Clients.get(clientId)
|
||||
if not clientData:
|
||||
return
|
||||
while self.MessageSeparator in clientData['buffer']:
|
||||
msgEnd = clientData['buffer'].index(self.MessageSeparator)
|
||||
messageData = clientData['buffer'][:msgEnd]
|
||||
clientData['buffer'] = clientData['buffer'][msgEnd + len(self.MessageSeparator):]
|
||||
try:
|
||||
message = json.loads(messageData.decode('utf-8'))
|
||||
self.ProcessClientMessage(clientId, message)
|
||||
except json.JSONDecodeError:
|
||||
print(f"Invalid JSON from client {clientId}")
|
||||
self.SendToClient(clientId, {'type': 'error', 'message': 'Invalid JSON'})
|
||||
except Exception as e:
|
||||
print(f"Error processing message: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
def ProcessClientMessage(self, clientId, message):
|
||||
if message.get('type') == 'auth':
|
||||
self.HandleAuth(clientId, message)
|
||||
elif message.get('type') == 'forward_request':
|
||||
self.HandleForwardRequest(clientId, message)
|
||||
elif message.get('type') == 'data':
|
||||
self.HandleData(clientId, message)
|
||||
elif message.get('type') == 'close_forward':
|
||||
self.HandleCloseForward(clientId, message)
|
||||
else:
|
||||
self.SendToClient(clientId, {'type': 'error', 'message': 'Unknown message type'})
|
||||
|
||||
def HandleAuth(self, clientId, message):
|
||||
clientData = self.Clients.get(clientId)
|
||||
if not clientData:
|
||||
return
|
||||
if message.get('key') == self.Key:
|
||||
clientData['authenticated'] = True
|
||||
self.SendToClient(clientId, {'type': 'auth_response', 'success': True})
|
||||
print(f"Client {clientId} authenticated successfully")
|
||||
else:
|
||||
self.SendToClient(clientId, {'type': 'auth_response', 'success': False, 'message': 'Invalid key'})
|
||||
clientData['socket'].close()
|
||||
print(f"Client {clientId} failed authentication")
|
||||
|
||||
def HandleForwardRequest(self, clientId, message):
|
||||
clientData = self.Clients.get(clientId)
|
||||
if not clientData or not clientData.get('authenticated', False):
|
||||
self.SendToClient(clientId, {'type': 'forward_response', 'success': False, 'message': 'Not authenticated'})
|
||||
return
|
||||
with self.ClientLocks[clientId]:
|
||||
if len(clientData['forwards']) >= self.MaxPortsPerClient:
|
||||
self.SendToClient(clientId, {'type': 'forward_response', 'success': False, 'message': 'Max ports per client reached'})
|
||||
return
|
||||
targetPort = message.get('target_port')
|
||||
mode = message.get('mode', 'tcp').upper()
|
||||
if not self.IsPortAllowed(targetPort):
|
||||
self.SendToClient(clientId, {'type': 'forward_response', 'success': False, 'message': 'Target port not allowed'})
|
||||
return
|
||||
forwardId = f"{clientId}:{targetPort}"
|
||||
if forwardId in self.ForwardMap:
|
||||
self.SendToClient(clientId, {'type': 'forward_response', 'success': False, 'message': 'Port already in use'})
|
||||
return
|
||||
try:
|
||||
if mode == 'TCP':
|
||||
forwardServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
forwardServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
forwardServer.bind(('0.0.0.0', targetPort))
|
||||
forwardServer.listen(5)
|
||||
with self.ClientLocks[clientId]:
|
||||
clientData['forwards'][forwardId] = {'server': forwardServer, 'mode': mode, 'connections': {}}
|
||||
with self.ForwardLocks[clientId]:
|
||||
self.ForwardMap[forwardId] = clientId
|
||||
threading.Thread(target=self.AcceptForwardConnections, args=(clientId, forwardId, forwardServer), daemon=True).start()
|
||||
self.SendToClient(clientId, {'type': 'forward_response', 'success': True, 'target_port': targetPort, 'forward_id': forwardId})
|
||||
print(f"Forward created: {forwardId}")
|
||||
else:
|
||||
self.SendToClient(clientId, {'type': 'forward_response', 'success': False, 'message': 'Unsupported mode'})
|
||||
except Exception as e:
|
||||
print(f"Forward creation error: {e}")
|
||||
traceback.print_exc()
|
||||
self.SendToClient(clientId, {'type': 'forward_response', 'success': False, 'message': str(e)})
|
||||
|
||||
def AcceptForwardConnections(self, clientId, forwardId, forwardServer):
|
||||
try:
|
||||
while self.Running and forwardId in self.ForwardMap and self.ForwardMap[forwardId] == clientId:
|
||||
forwardServer.settimeout(1)
|
||||
try:
|
||||
conn, addr = forwardServer.accept()
|
||||
connId = f"{addr[0]}:{addr[1]}"
|
||||
print(f"New connection to forward {forwardId} from {connId}")
|
||||
with self.ClientLocks[clientId]:
|
||||
clientData = self.Clients.get(clientId)
|
||||
if not clientData or forwardId not in clientData['forwards']:
|
||||
conn.close()
|
||||
continue
|
||||
clientData['forwards'][forwardId]['connections'][connId] = conn
|
||||
self.SendToClient(clientId, {
|
||||
'type': 'new_connection',
|
||||
'forward_id': forwardId,
|
||||
'conn_id': connId
|
||||
})
|
||||
threading.Thread(target=self.ForwardToClient, args=(clientId, forwardId, connId, conn), daemon=True).start()
|
||||
except socket.timeout:
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"Forward accept error: {e}")
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
try:
|
||||
forwardServer.close()
|
||||
except:
|
||||
pass
|
||||
print(f"Forward listener {forwardId} stopped")
|
||||
|
||||
def ForwardToClient(self, clientId, forwardId, connId, conn):
|
||||
try:
|
||||
while self.Running:
|
||||
conn.settimeout(1)
|
||||
try:
|
||||
data = conn.recv(4096)
|
||||
if not data:
|
||||
break
|
||||
self.SendToClient(clientId, {
|
||||
'type': 'data',
|
||||
'forward_id': forwardId,
|
||||
'conn_id': connId,
|
||||
'data': data.hex()
|
||||
})
|
||||
except socket.timeout:
|
||||
if not self.Running or clientId not in self.Clients:
|
||||
break
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"Forward to client error: {e}")
|
||||
traceback.print_exc()
|
||||
break
|
||||
finally:
|
||||
try:
|
||||
conn.close()
|
||||
except:
|
||||
pass
|
||||
with self.ClientLocks[clientId]:
|
||||
clientData = self.Clients.get(clientId)
|
||||
if clientData and forwardId in clientData['forwards']:
|
||||
if connId in clientData['forwards'][forwardId]['connections']:
|
||||
del clientData['forwards'][forwardId]['connections'][connId]
|
||||
self.SendToClient(clientId, {
|
||||
'type': 'close_connection',
|
||||
'forward_id': forwardId,
|
||||
'conn_id': connId
|
||||
})
|
||||
print(f"Connection {connId} to forward {forwardId} closed")
|
||||
|
||||
def HandleData(self, clientId, 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.ClientLocks[clientId]:
|
||||
clientData = self.Clients.get(clientId)
|
||||
if not clientData or forwardId not in clientData['forwards']:
|
||||
return
|
||||
forwardData = clientData['forwards'][forwardId]
|
||||
if connId not in forwardData['connections']:
|
||||
return
|
||||
conn = forwardData['connections'][connId]
|
||||
conn.sendall(data)
|
||||
except Exception as e:
|
||||
print(f"Data handling error: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
def HandleCloseForward(self, clientId, message):
|
||||
forwardId = message.get('forward_id')
|
||||
if not forwardId:
|
||||
return
|
||||
with self.ClientLocks[clientId]:
|
||||
clientData = self.Clients.get(clientId)
|
||||
if clientData and forwardId in clientData['forwards']:
|
||||
try:
|
||||
clientData['forwards'][forwardId]['server'].close()
|
||||
except:
|
||||
pass
|
||||
del clientData['forwards'][forwardId]
|
||||
with self.ForwardLocks[clientId]:
|
||||
if forwardId in self.ForwardMap:
|
||||
del self.ForwardMap[forwardId]
|
||||
print(f"Forward {forwardId} closed by client")
|
||||
|
||||
def SendToClient(self, clientId, message):
|
||||
try:
|
||||
with self.ClientLocks[clientId]:
|
||||
clientData = self.Clients.get(clientId)
|
||||
if not clientData or not clientData['socket']:
|
||||
return
|
||||
clientSocket = clientData['socket']
|
||||
data = json.dumps(message).encode('utf-8') + self.MessageSeparator
|
||||
clientSocket.sendall(data)
|
||||
except Exception as e:
|
||||
print(f"Error sending to client {clientId}: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
def main():
|
||||
config = {
|
||||
"InternalDataPort": 5000,
|
||||
"AllowedPortRange": "5001-5500",
|
||||
"MaxPortsPerClient": 5,
|
||||
"Key": "07A36AEF1907843"
|
||||
}
|
||||
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()
|
||||
server = PortForwardServer(
|
||||
InternalDataPort=int(config["InternalDataPort"]),
|
||||
AllowedPortRange=config["AllowedPortRange"],
|
||||
MaxPortsPerClient=int(config["MaxPortsPerClient"]),
|
||||
Key=config["Key"]
|
||||
)
|
||||
server.Start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user