/// <summary> /// Abort all active connections. Call this function only when shutting down. /// </summary> public static void AbortAll() { Console.WriteLine("Closing all active connections, please wait..."); lock (connectionListLock) { foreach (TCPConnection connection in connections) { try { connection.Abort(); } catch { } } } // Terminate all connection handler threads ConnectionThreadManager.TerminateAll(); }
/// <summary> /// Create a new connection instance to handle an inbound connection /// </summary> /// <param name="socket">TCP socket for communicating with the remote server</param> /// <param name="connectionLogWriter">Log writer module</param> /// <param name="serverList">Server List object</param> /// <param name="geoIP">GeoIP resolver</param> /// <param name="md5Manager">MD5 database manager</param> /// <param name="banManager">IP ban manager</param> /// <param name="cdKeyValidator">CD key validator</param> /// <param name="gameStats">Game stats module</param> public Connection(Socket socket, IConnectionLogWriter logWriter, ServerList serverList, GeoIP geoIP, MD5Manager md5Manager, IPBanManager banManager, ICDKeyValidator cdKeyValidator, IGameStatsLog gameStats) : this(socket, logWriter, serverList, geoIP) { // Raise the NewConnection event for connected packet analysers OnNewConnection(); // Check whether the remote host is banned if (banManager.IsBanned((socket.RemoteEndPoint as IPEndPoint).Address)) { ConnectionLog("BANNED"); socket.Close(); return; } this.md5Manager = md5Manager; this.cdKeyValidator = cdKeyValidator; this.gameStats = gameStats; // Handle this connection in a new thread ConnectionThreadManager.CreateStart(new ThreadStart(Handle)); }
/// <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); }