public async Task Process(InboundPacket packet) { foreach (var frame in packet.AsFrames()) { await Process(frame); } }
/// <summary> /// Client has queried the server list /// </summary> /// <param name="clientRequest"></param> private void HandleQuery(InboundPacket clientRequest) { // Query arrives as an array of QueryData structs QueryData[] queries = clientRequest.PopStructArray <QueryData>(); // Write query to log and notify master server for stats LogQuery(queries); // Get a filtered list of servers based on the queries which were receieved List <Server> servers = serverList.Query(queries); // Server count is the first reply following the query and tells the player how many servers to expect OutboundPacket serverCountPacket = new OutboundPacket(); serverCountPacket.Append(servers.Count); serverCountPacket.Append((byte)0x01); Send(serverCountPacket); // Send server list if any were found if (servers.Count > 0) { foreach (Server server in servers) { OutboundPacket serverListPacket = new OutboundPacket(); serverListPacket.AppendStruct <ServerListEntry>(server.ListEntry); Send(serverListPacket); } } }
/// <summary> /// Client has made an unrecognised request /// </summary> /// <param name="clientRequest"></param> private void HandleUnknownRequest(InboundPacket clientRequest) { clientRequest.Rewind(); ConnectionLog("UNKNOWN REQUEST CODE={0}", clientRequest.PopByte()); //MasterServer.Log("Client at {0} sent unrecognised query ", socket.RemoteEndPoint.ToString()); }
/// <summary> /// Handles the player connection, the connection is closed once the request is completed /// </summary> protected override void Handle() { try { InboundPacket clientRequest = Receive(); ClientToMaster clientRequestType = (ClientToMaster)clientRequest.PopByte(); ConnectionLog("REQUEST TYPE={0}", clientRequestType); switch (clientRequestType) { case ClientToMaster.Query: HandleQuery(clientRequest); break; case ClientToMaster.GetMOTD: HandleMOTDRequest(clientRequest); break; case ClientToMaster.QueryUpgrade: HandleQueryUpgrade(clientRequest); break; default: HandleUnknownRequest(clientRequest); break; } } catch (ThreadAbortException) { aborted = true; } catch (Exception) // ex) { //if (!aborted) // MasterServer.Log("Client connection error: {0}", ex.Message); } }
/// <summary> /// Try to login with the specified details and get the response /// </summary> /// <param name="cdKey">CD key hash for login</param> /// <param name="saltedKey">Salted key hash for login</param> /// <param name="type">Remote client type (eg. CLIENT or SERVER)</param> /// <param name="version">Remote client version</param> /// <param name="locale">Remote client locale</param> /// <returns>Login response from the server</returns> public string Login(string cdKey, string saltedKey, string type, int version, string locale) { string response = Protocol.LOGIN_RESPONSE_DENIED; if (!connectionError) { try { OutboundPacket login = new OutboundPacket(); login.Append(cdKey).Append(saltedKey).Append(type).Append(version); if (type == Protocol.HOST_SERVER) { login.Append(-1); // stats enabled flag for server } login.Append((byte)0x04).Append(locale); Send(login); InboundPacket loginResponse = Receive(); response = loginResponse.PopString(); } catch { connectionError = true; } } Close(); return(response); }
/// <summary> /// Overridden so that we can wrap the received data in a ServerInfoPacket instead of an InboundPacket /// </summary> /// <returns></returns> protected override InboundPacket Receive() { byte[] packetData = ReceieveData(); InboundPacket packet = (packetData.Length > 4) ? (InboundPacket) new ServerInfoPacket(packetData) : new EmptyInboundPacket(); outerConnection.OnReceivedPacket(packet); return(packet); }
/// <summary> /// Receive data from the socket and wrap them in an InboundPacket /// </summary> /// <returns>InboundPacket containing the received data</returns> protected virtual InboundPacket Receive() { byte[] packetData = ReceieveData(); InboundPacket packet = (packetData.Length > 4) ? new InboundPacket(packetData) : new EmptyInboundPacket(); OnReceivedPacket(packet); return(packet); }
/// <summary> /// Raise the ReceivedPacket event /// </summary> /// <param name="packet">Packet that was received</param> public void OnReceivedPacket(InboundPacket packet) { ReceivedPacketEventHandler receivedPacket = this.ReceivedPacket; if (receivedPacket != null) { receivedPacket(this, packet); } }
/// <summary> /// Client has requested the MOTD /// </summary> /// <param name="clientRequest"></param> private void HandleMOTDRequest(InboundPacket clientRequest) { ConnectionLog("SENDING MOTD"); // Response packet contains the MOTD string OutboundPacket MOTD = new OutboundPacket(MasterServer.GetMOTD(locale, true)); // Send the MR_OptionalUpgrade value if this connection is valid but player is an outdated version if (outerConnection.Version < Protocol.OPTIONALUPGRADE_VERSION) { MOTD.Append(Protocol.OPTIONALUPGRADE_VERSION); } // Send the MOTD packet Send(MOTD); }
/// <summary> /// Handles a connection from a CLIENT /// </summary> /// <param name="login">Login response packet</param> protected virtual void HandleClientConnection(InboundPacket login) { ConnectionLog(Protocol.LOGIN_RESPONSE_APPROVED); Send(Protocol.LOGIN_RESPONSE_APPROVED); byte osByte = login.PopByte(); // Host's detected operating system locale = login.PopString(); // Host's locale, eg. int, est // Map the OS value to the relevant enum value if it's valid operatingSystem = Enum.IsDefined(typeof(OperatingSystem), osByte) ? (OperatingSystem)osByte : OperatingSystem.UnknownOS; ConnectionLog("{0} OS={1} LOCALE={2}", type, operatingSystem, locale); innerConnection = new ClientConnection(this, socket, logWriter, serverList, geoIP, operatingSystem, locale); innerConnection.Handle(); // Handle the connection in this thread }
/// <summary> /// Establish the connection to the remote master server and get the challenge salt /// </summary> /// <returns>Master server challenge salt (or 0 on failure)</returns> public int Connect() { int salt = 0; try { socket.Connect(host, port); InboundPacket challenge = Receive(); salt = int.Parse(challenge.PopString()); } catch { connectionError = true; } return(salt); }
/// <summary> /// Handles a connection from a SERVER /// </summary> /// <param name="login">Login response packet</param> protected virtual void HandleServerConnection(InboundPacket login) { ConnectionLog(Protocol.LOGIN_RESPONSE_APPROVED); Send(Protocol.LOGIN_RESPONSE_APPROVED); bool bStatLogging = (login.PopInt() == 0); // Seems to be -1 if disabled, 0 if enabled byte osByte = login.PopByte(); // Host's detected operating system locale = login.PopString(); // Host's locale, eg. int // Map the OS value to the relevant enum value if it's valid operatingSystem = Enum.IsDefined(typeof(OperatingSystem), osByte) ? (OperatingSystem)osByte : OperatingSystem.UnknownOS; ConnectionLog("{0} BSTATLOGGING={1} OS={2} LOCALE={3}", type, bStatLogging, operatingSystem, locale); innerConnection = new ServerConnection(this, socket, logWriter, serverList, geoIP, operatingSystem, locale, bStatLogging, md5Manager, cdKeyValidator, gameStats); innerConnection.Handle(); // Handle the connection in this thread }
public static async IAsyncEnumerable <InboundPacket> AsDecrypted(this IAsyncEnumerable <InboundEncryptedPacket> encPackets, KeyManager keyMan) { await foreach (var p in encPackets) { InboundPacket d = null; try { if (p is InboundEncryptedHandshakePacket) { await Task.Delay(5000); } d = p.AsDecryptedPacket(keyMan); } catch { } if (d != null) { yield return(d); } } }
/// <summary> /// Creates a new player object from information in a received server data packet /// </summary> /// <param name="packet">Packet containing player information at the current pointer position</param> /// <param name="address">Player address, from the server info header</param> public Player(Server server, InboundPacket packet, string address) { Address = address; PlayerNumber = packet.PopInt(); OnPropertyChanged("PlayerNumber"); if (server.Version > 2225) { PlayerID = packet.PopInt(); OnPropertyChanged("PlayerID"); } Name = packet.PopString(); OnPropertyChanged("Name"); Ping = packet.PopInt(); OnPropertyChanged("Ping"); Score = packet.PopInt(); OnPropertyChanged("Score"); StatsID = packet.PopInt(); OnPropertyChanged("StatsID"); if (server.Version > 2225) { Info = packet.PopKeyValueArray(); OnPropertyChanged("Info"); } else { Info = new Dictionary <string, string>(); } }
/// <summary> /// Handle the connection /// </summary> protected virtual void Handle() { try { // Initialise validation context for this session validationContext = cdKeyValidator.BeginValidation("login"); // Log the new connection to the connection log ConnectionLog("ACCEPT LOCALPORT={0} SALT={1}", LocalPort, cdKeyValidator.GetSalt(validationContext)); // Send the challenge salt Send(cdKeyValidator.GetSalt(validationContext).ToString()); // Read back the authentication from the player InboundPacket login = Receive(); cdKey = login.PopString(); // Get the first MD5 which should be the CD key hash saltedCDKey = login.PopString(); // Get the second MD5 which should be the CD key plus salt hash type = login.PopString(); // Type of client eg. CLIENT or SERVER version = login.PopInt(); // Client's engine version // Write the login info to the connection log ConnectionLog("CONNECT MD5={0} SALTED={1} TYPE={2} VERSION={3}", cdKey, saltedCDKey, type, version); // Set values into the validation context validationContext.SetClientInfo(cdKey, saltedCDKey, type, version); // Check the CD key if (Validate(validationContext)) { if (version < Protocol.MIN_SUPPORTED_CLIENT_VERSION) { ConnectionLog(Protocol.LOGIN_RESPONSE_UPGRADE); MasterServer.Log("{0} at {1} rejected, outdated version: got {2}", type, (socket.RemoteEndPoint as IPEndPoint).Address.ToString(), version); // This is my best guess for how an UPGRADE packet should be structured, if it's wrong it seems to crash the client OutboundPacket UpgradePacket = new OutboundPacket(Protocol.LOGIN_RESPONSE_UPGRADE); UpgradePacket.Append(Protocol.MIN_SUPPORTED_CLIENT_VERSION); UpgradePacket.Append(0x00); // Send the UPGRADE response Send(UpgradePacket); } else { // Send MSLIST packet if enabled, if the MSLIST is sent successfully then close the // connection (SendMSList() returns true if the MSLIST was sent) if (!SendMSList()) { switch (type) { case Protocol.HOST_CLIENT: HandleClientConnection(login); break; case Protocol.HOST_SERVER: HandleServerConnection(login); break; default: HandleUnknownConnection(login); break; } } } } } catch (ThreadAbortException) { aborted = true; } catch (Exception ex) { ConnectionLog("EXCEPTION: {0}", ex.Message); } try { socket.Close(); } catch { } ConnectionLog("CLOSED"); ConnectionThreadManager.Remove(Thread.CurrentThread); ConnectionManager.DeRegister(this); }
/// <summary> /// Handle the server connection /// </summary> protected override void Handle() { // Loop until connection is closed or forcibly aborted while (socket.Connected && !aborted) { try { // Read data from the socket InboundPacket packet = Receive(); if (!packet.Empty && packet.Valid) { ServerInfoPacket serverInfo = (ServerInfoPacket)packet; // Connection is considered "established" once the heartbeat has been established switch (State) { // Waiting for initial greeting from the server case ConnectionState.WaitingHello: HandleHello(serverInfo); break; // Waiting to receive heartbeat from the server case ConnectionState.WaitingHeartbeat: if ((DateTime.Now - waitingHeartbeatTime).TotalSeconds > waitForHeartbeatSeconds) { ConnectionLog("TIMEOUT WAITING HEARTBEAT IN STATE {0}", State); MasterServer.Log("[{0}] Timeout waiting for heartbeat response."); outerConnection.Abort(); } else { ConnectionLog("UNSOLICITED MESSAGE IN STATE {0}", State); } break; // Connection is established, process inbound packets as normal server conversation case ConnectionState.Established: switch (serverInfo.PacketCode) { case ServerToMaster.ClientResponse: HandleClientChallengeResponse(serverInfo); break; case ServerToMaster.GameState: HandleGameState(serverInfo); break; case ServerToMaster.Stats: HandleStats(serverInfo); break; case ServerToMaster.ClientDisconnectFailed: HandleClientDisconnectFailed(serverInfo); break; case ServerToMaster.MD5Version: HandleMD5Version(serverInfo); break; case ServerToMaster.CheckOptionReply: HandleCheckOptionReply(serverInfo); break; default: packet.Rewind(); ConnectionLog("INVALID MESSAGE STATE={0} CODE={1}", State, packet.PopByte()); break; } break; } } else if (socket.Connected) { ConnectionLog("INVALID PACKET STATE={0} DATA={1}", State, packet.PrintBytes(true)); Debug.WriteLine(String.Format("Invalid packet from server at {0} in state {1}", server.Address, State)); Debug.WriteLine(packet.Print()); if (State == ConnectionState.WaitingHeartbeat && (DateTime.Now - waitingHeartbeatTime).TotalSeconds > waitForHeartbeatSeconds) { MasterServer.Log("[{0}] Timeout waiting for heartbeat response.", server); ConnectionLog("TIMEOUT WAITING HEARTBEAT IN STATE {0}", State); outerConnection.Abort(); } } } catch (ThreadAbortException) { aborted = true; } catch (Exception ex) { if (!aborted) { ConnectionLog("ERROR: {0}", ex.Message); } break; } } if (!socket.Connected) { MasterServer.Log("[{0}] Connection closed", server); } serverList.Remove(server); server = null; }
/// <summary> /// Client has issued an upgrade query. I have no idea of the format of this packet. /// </summary> /// <param name="clientRequest"></param> private void HandleQueryUpgrade(InboundPacket clientRequest) { //MasterServer.Log("Client at {0} sent CTM_QueryUpgrade", socket.RemoteEndPoint.ToString()); }
public PacketAnalyser(InboundPacket packet) { this.packet = packet; this.tail = packet.Print(); }
/// <summary> /// Handles a connection from an unrecognised host type /// </summary> /// <param name="login">Login response packet</param> protected virtual void HandleUnknownConnection(InboundPacket login) { ConnectionLog("UNRECOGNISED LOGIN TYPE={0}", type); MasterServer.Log("Unrecognised LOGIN from {0}. Expecting {1} or {2}, got: '{3}'", (socket.RemoteEndPoint as IPEndPoint).Address.ToString(), Protocol.HOST_SERVER, Protocol.HOST_CLIENT, type); }
void InvokedHandleReceivedPacket(TCPConnection connection, InboundPacket packet) { packets[connection].Packets.Add(new PacketData(packet)); UpdatePacketList(); }
void HandleReceivedPacket(TCPConnection connection, InboundPacket packet) { Invoke(new ReceivedPacketEventHandler(InvokedHandleReceivedPacket), connection, packet); }
public PacketData(InboundPacket packet) { Name = String.Format("{0} ({1})", DateTime.Now.ToString("dd/MM HH:mm:ss:"), packet.Length); Packet = packet; Analyser = new PacketAnalyser(packet); }