From d3145b2ffc0ffff359e0099486a4495285b7a384 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 14 Nov 2022 02:25:22 +0400 Subject: [PATCH] Remove proxy server from vcmi repo --- proxyServer.py | 628 ------------------------------------------------- 1 file changed, 628 deletions(-) delete mode 100755 proxyServer.py diff --git a/proxyServer.py b/proxyServer.py deleted file mode 100755 index 6ef416242..000000000 --- a/proxyServer.py +++ /dev/null @@ -1,628 +0,0 @@ -import socket -import re -import uuid -import struct -import logging -from threading import Thread - -PROTOCOL_VERSION_MIN = 1 -PROTOCOL_VERSION_MAX = 1 - -# server's IP address -SERVER_HOST = "0.0.0.0" -SERVER_PORT = 5002 # port we want to use - -#logging -logHandlerHighlevel = logging.FileHandler('proxyServer.log') -logHandlerHighlevel.setLevel(logging.INFO) - -logHandlerLowlevel = logging.FileHandler('proxyServer_debug.log') -logHandlerLowlevel.setLevel(logging.DEBUG) - -logging.basicConfig(handlers=[logHandlerHighlevel, logHandlerLowlevel], level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S') - -def receive_packed(sock): - # Read message length and unpack it into an integer - raw_msglen = recvall(sock, 4) - if not raw_msglen: - return None - msglen = struct.unpack(' None: - self.server = None - self.client = None - pass - - -class Room: - total = 1 # total amount of players - joined = 0 # amount of players joined to the session - password = "" # password to connect - protected = False # if True, password is required to join to the session - name: str # name of session - host: socket # player socket who created the room - players = [] # list of sockets of players, joined to the session - started = False - - def __init__(self, host: socket, name: str) -> None: - self.name = name - self.host = host - self.players = [host] - self.joined += 1 - - def isJoined(self, player: socket) -> bool: - return player in self.players - - def join(self, player: socket): - if not self.isJoined(player) and self.joined < self.total: - self.players.append(player) - self.joined += 1 - - def leave(self, player: socket): - if not self.isJoined(player) or player == self.host: - return - - self.players.remove(player) - self.joined -= 1 - - -class Session: - name: str # name of session - host_uuid: str # uuid of vcmiserver for hosting player - clients_uuid: list # list od vcmiclients uuid - players: list # list of sockets of players, joined to the session - connections: list # list of GameConnections for vcmiclient/vcmiserver (game mode) - - def __init__(self) -> None: - self.name = "" - self.host_uuid = "" - self.clients_uuid = [] - self.connections = [] - pass - - def addConnection(self, conn: socket, isServer: bool): - #find uninitialized server connection - for gc in self.connections: - if isServer and not gc.serverInit: - gc.server = conn - gc.serverInit = True - if not isServer and not gc.clientInit: - gc.client = conn - gc.clientInit = True - - #no existing connection - create the new one - gc = GameConnection() - if isServer: - gc.server = conn - gc.serverInit = True - else: - gc.client = conn - gc.clientInit = True - self.connections.append(gc) - - def removeConnection(self, conn: socket): - newConnections = [] - for c in self.connections: - if c.server == conn: - c.server = None - c.serverInit = False - if c.client == conn: - c.client = None - c.clientInit = False - if c.server != None or c.client != None: - newConnections.append(c) - self.connections = newConnections - - def validPipe(self, conn) -> bool: - for gc in self.connections: - if gc.server == conn or gc.client == conn: - return gc.serverInit and gc.clientInit - return False - - def getPipe(self, conn) -> socket: - for gc in self.connections: - if gc.server == conn: - return gc.client - if gc.client == conn: - return gc.server - - -class Client: - auth: bool - - def __init__(self) -> None: - self.auth = False - - -class ClientLobby(Client): - joined: bool - username: str - room: Room - protocolVersion: int - encoding: str - ready: bool - vcmiversion: str #TODO: check version compatibility - - def __init__(self) -> None: - super().__init__() - self.room = None - self.joined = False - self.username = "" - self.protocolVersion = 0 - self.encoding = 'utf8' - self.ready = False - self.vcmiversion = "" - - -class ClientPipe(Client): - apptype: str #client/server - prevmessages: list - session: Session - uuid: str - - def __init__(self) -> None: - super().__init__() - self.prevmessages = [] - self.session = None - self.apptype = "" - self.uuid = "" - - -class Sender: - address: str #full client address - client: Client - - def __init__(self) -> None: - self.client = None - pass - - def isLobby(self) -> bool: - return isinstance(self.client, ClientLobby) - - def isPipe(self) -> bool: - return isinstance(self.client, ClientPipe) - - -# create a TCP socket -s = socket.socket() -# make the port as reusable port -s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -# bind the socket to the address we specified -s.bind((SERVER_HOST, SERVER_PORT)) -# listen for upcoming connections -s.listen(10) -logging.critical(f"[*] Listening as {SERVER_HOST}:{SERVER_PORT}") - -# active rooms -rooms = {} - -# list of active sessions -sessions = [] - -# initialize list/set of all connected client's sockets -client_sockets = {} - - -def handleDisconnection(client: socket): - logging.warning(f"[!] Disconnecting client {client}") - - if not client in client_sockets: - return - - sender = client_sockets[client] - if sender.isLobby() and sender.client.joined: - if not sender.client.room.started: - if sender.client.room.host == client: - #destroy the session, sending messages inside the function - deleteRoom(sender.client.room) - else: - sender.client.room.leave(client) - sender.client.joined = False - message = f":>>KICK:{sender.client.room.name}:{sender.client.username}" - broadcast(sender.client.room.players, message.encode()) - updateStatus(sender.client.room) - updateRooms() - - if sender.isPipe() and sender.client.auth: - if sender.client.session in sessions: - sender.client.session.removeConnection(client) - if not len(sender.client.session.connections): - logging.warning(f"[*] Destroying session {sender.client.session.name}") - sessions.remove(sender.client.session) - - client.close() - client_sockets.pop(client) - - -def send(client: socket, message: str): - if client in client_sockets.keys(): - sender = client_sockets[client] - client.send(message.encode(errors='replace')) - - -def broadcast(clients: list, message: str): - for c in clients: - if client_sockets[c].isLobby() and client_sockets[c].client.auth: - send(c, message) - - -def sendRooms(client: socket): - msg2 = "" - counter = 0 - for s in rooms.values(): - if not s.started: - msg2 += f":{s.name}:{s.joined}:{s.total}:{s.protected}" - counter += 1 - msg = f":>>SESSIONS:{counter}{msg2}" - - send(client, msg) - - -def updateRooms(): - for s in client_sockets.keys(): - sendRooms(s) - - -def deleteRoom(room: Room): - msg = f":>>KICK:{room.name}" - for player in room.players: - client_sockets[player].client.joined = False - msg2 = msg + f":{client_sockets[player].client.username}" - send(player, msg2) - - logging.warning(f"[*] Destroying room {room.name}") - rooms.pop(room.name) - - -def updateStatus(room: Room): - msg = f":>>STATUS:{room.joined}" - for player in room.players: - msg += f":{client_sockets[player].client.username}:{client_sockets[player].client.ready}" - broadcast(room.players, msg) - - -def startRoom(room: Room): - room.started = True - session = Session() - session.name = room.name - sessions.append(session) - logging.warning(f"[*] Starting session {session.name}") - session.host_uuid = str(uuid.uuid4()) - hostMessage = f":>>HOST:{session.host_uuid}:{room.joined - 1}" #one client will be connected locally - logging.debug(f" host: {session.host_uuid} connections {room.joined - 1}") - #host message must be before start message - send(room.host, hostMessage) - - for player in room.players: - _uuid = str(uuid.uuid4()) - session.clients_uuid.append(_uuid) - msg = f":>>START:{_uuid}" - send(player, msg) - #remove this connection - player.close - client_sockets.pop(player) - - #this room shall not exist anymore - logging.info(f"[*] Destroying room {room.name}") - rooms.pop(room.name) - - -def dispatch(cs: socket, sender: Sender, arr: bytes): - - if arr == None or len(arr) == 0: - return - - #check for game mode connection - msg = str(arr) - if msg.find("Aiya!") != -1: - sender.client = ClientPipe() - logging.debug(" vcmi recognized") - - if sender.isPipe(): - if sender.client.auth: #if already playing - sending raw bytes as is - sender.client.prevmessages.append(arr) - else: - sender.client.prevmessages.append(struct.pack(' PROTOCOL_VERSION_MAX: - logging.critical(f"[!] Error: client has incompatbile protocol version {arr[0]}") - send(cs, ":>>ERROR:Cannot connect to remote server due to protocol incompatibility") - return - - # second byte is an encoding str size - if arr[1] == 0: - sender.client.encoding = "utf8" - else: - if len(arr) < arr[1] + 2: - send(cs, ":>>ERROR:Protocol error") - return - # read encoding string - sender.client.encoding = arr[2:(arr[1] + 2)].decode(errors='ignore') - arr = arr[(arr[1] + 2):] - msg = str(arr) - - msg = arr.decode(encoding=sender.client.encoding, errors='replace') - _open = msg.partition('<') - _close = _open[2].partition('>') - if _open[0] != '' or _open[1] == '' or _open[2] == '' or _close[0] == '' or _close[1] == '': - logging.error(f"[!] Incorrect message from {sender.address}: {msg}") - return - - _nextTag = _close[2].partition('<') - tag = _close[0] - tag_value = _nextTag[0] - - #greetings to the server - if tag == "GREETINGS": - if sender.client.auth: - logging.error(f"[!] Greetings from authorized user {sender.client.username} {sender.address}") - send(cs, ":>>ERROR:User already authorized") - return - - if len(tag_value) < 3: - send(cs, f":>>ERROR:Too short username {tag_value}") - return - - for user in client_sockets.values(): - if user.isLobby() and user.client.username == tag_value: - send(cs, f":>>ERROR:Can't connect with the name {tag_value}. This login is already occpupied") - return - - logging.info(f"[*] {sender.address} autorized as {tag_value}") - sender.client.username = tag_value - sender.client.auth = True - sendRooms(cs) - - #VCMI version received - if tag == "VER" and sender.client.auth: - logging.info(f"[*] User {sender.client.username} has version {tag_value}") - sender.client.vcmiversion = tag_value - - #message received - if tag == "MSG" and sender.client.auth: - message = f":>>MSG:{sender.client.username}:{tag_value}" - if sender.client.joined: - broadcast(sender.client.room.players, message) #send message only to players in the room - else: - targetClients = [i for i in client_sockets.keys() if not client_sockets[i].client.joined] - broadcast(targetClients, message) - - #new room - if tag == "NEW" and sender.client.auth and not sender.client.joined: - if tag_value in rooms: - #refuse creating game - message = f":>>ERROR:Cannot create session with name {tag_value}, session with this name already exists" - send(cs, message) - return - - if tag_value == "" or tag_value.startswith(" ") or len(tag_value) < 3: - #refuse creating game - message = f":>>ERROR:Cannot create session with invalid name {tag_value}" - send(cs, message) - return - - rooms[tag_value] = Room(cs, tag_value) - sender.client.joined = True - sender.client.ready = False - sender.client.room = rooms[tag_value] - - #set password for the session - if tag == "PSWD" and sender.client.auth and sender.client.joined and sender.client.room.host == cs: - sender.client.room.password = tag_value - sender.client.room.protected = bool(tag_value != "") - - #set amount of players to the new room - if tag == "COUNT" and sender.client.auth and sender.client.joined and sender.client.room.host == cs: - if sender.client.room.total != 1: - #refuse changing amount of players - message = f":>>ERROR:Changing amount of players is not possible for existing session" - send(cs, message) - return - - if int(tag_value) < 2 or int(tag_value) > 8: - #refuse and cleanup room - deleteRoom(sender.client.room) - message = f":>>ERROR:Cannot create room with invalid amount of players" - send(cs, message) - return - - sender.client.room.total = int(tag_value) - message = f":>>CREATED:{sender.client.room.name}" - send(cs, message) - #now room is ready to be broadcasted - message = f":>>JOIN:{sender.client.room.name}:{sender.client.username}" - send(cs, message) - updateStatus(sender.client.room) - updateRooms() - - #join session - if tag == "JOIN" and sender.client.auth and not sender.client.joined: - if tag_value not in rooms: - message = f":>>ERROR:Room with name {tag_value} doesn't exist" - send(cs, message) - return - - if rooms[tag_value].joined >= rooms[tag_value].total: - message = f":>>ERROR:Room {tag_value} is full" - send(cs, message) - return - - if rooms[tag_value].started: - message = f":>>ERROR:Session {tag_value} is started" - send(cs, message) - return - - sender.client.joined = True - sender.client.ready = False - sender.client.room = rooms[tag_value] - - if tag == "PSWD" and sender.client.auth and sender.client.joined and sender.client.room.host != cs: - if not sender.client.room.protected or sender.client.room.password == tag_value: - sender.client.room.join(cs) - message = f":>>JOIN:{sender.client.room.name}:{sender.client.username}" - broadcast(sender.client.room.players, message) - updateStatus(sender.client.room) - updateRooms() - - else: - sender.client.joined = False - message = f":>>ERROR:Incorrect password" - send(cs, message) - return - - #leaving session - if tag == "LEAVE" and sender.client.auth and sender.client.joined and sender.client.room.name == tag_value: - if sender.client.room.host == cs: - #destroy the session, sending messages inside the function - deleteRoom(sender.client.room) - else: - message = f":>>KICK:{sender.client.room.name}:{sender.client.username}" - broadcast(sender.client.room.players, message) - sender.client.room.leave(cs) - sender.client.joined = False - updateStatus(sender.client.room) - updateRooms() - - if tag == "READY" and sender.client.auth and sender.client.joined and sender.client.room.name == tag_value: - if sender.client.room.joined > 0 and sender.client.room.host == cs: - startRoom(sender.client.room) - updateRooms() - - dispatch(cs, sender, (_nextTag[1] + _nextTag[2]).encode()) - - -def listen_for_client(cs): - """ - This function keep listening for a message from `cs` socket - Whenever a message is received, broadcast it to all other connected clients - """ - while True: - try: - # keep listening for a message from `cs` socket - if client_sockets[cs].isPipe() and client_sockets[cs].client.auth: - msg = cs.recv(4096) - else: - msg = receive_packed(cs) - - if msg == None or msg == b'': - handleDisconnection(cs) - return - - dispatch(cs, client_sockets[cs], msg) - - except Exception as e: - # client no longer connected - logging.error(f"[!] Error: {e}") - handleDisconnection(cs) - return - - -while True: - # we keep listening for new connections all the time - client_socket, client_address = s.accept() - logging.warning(f"[+] {client_address} connected.") - # add the new connected client to connected sockets - client_sockets[client_socket] = Sender() - client_sockets[client_socket].address = client_address - # start a new thread that listens for each client's messages - t = Thread(target=listen_for_client, args=(client_socket,)) - # make the thread daemon so it ends whenever the main thread ends - t.daemon = True - # start the thread - t.start() - - # close client sockets -for cs in client_sockets: - cs.close() -# close server socket -s.close() \ No newline at end of file