private bool PopString(string name) { try { string s = packet.PopString(); return(Log("string {0} = \"{1}\"", name, s)); } catch { return(Error("No string available")); } }
/// <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> /// 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 }
/// <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); }