private void Client_MessageReceived(RconClient sender, RconPacket packet) { switch (packet.Type) { case PacketType.SERVERDATA_AUTH: Authenticate(sender, packet); break; case PacketType.SERVERDATA_EXECCOMMAND: if (sender.IsAuthed) { HandleCommand(sender, packet); } else { sender.SendPacket(new RconPacket(packet.Id, PacketType.SERVERDATA_RESPONSE_VALUE, "Not authenticated")); } break; default: Log.Error($"Recieved an unsupported packet type {packet.Type}. Id: {packet.Id}"); sender.SendPacket(new RconPacket(packet.Id, PacketType.SERVERDATA_RESPONSE_VALUE, "Invalid packet type")); break; } }
private void HandleCommand(RconClient sender, RconPacket packet) { var message = CommandHandler?.Invoke(packet.Body) ?? "No command handler defined on host."; var response = new RconPacket { Id = packet.Id, Type = PacketType.SERVERDATA_RESPONSE_VALUE, Body = message }; sender.SendPacket(response); }
private void Authenticate(RconClient sender, RconPacket packet) { var hash = Md5Util.HashString(packet.Body); if (_pwHash != null && hash.SequenceEqual(_pwHash)) { Log.Info($"{sender.RemoteEndPoint}: Authorized"); sender.SendPacket(new RconPacket(packet.Id, PacketType.SERVERDATA_AUTHRESPONSE, string.Empty)); sender.IsAuthed = true; } else { Log.Warn($"{sender.RemoteEndPoint}: Incorrect password attempt"); sender.SendPacket(new RconPacket(-1, PacketType.SERVERDATA_AUTHRESPONSE, string.Empty)); sender.IsAuthed = false; } }
private void DoEndAccept(IAsyncResult ar) { try { var listener = (Socket)ar.AsyncState; var remoteSocket = listener.EndAccept(ar); var client = new RconClient(remoteSocket); Log.Info($"Accepted client {remoteSocket.RemoteEndPoint}"); _clients.Add(client); client.MessageReceived += Client_MessageReceived; client.ConnectionClosed += x => _clients.Remove(x); client.StartListening(); listener.BeginAccept(DoEndAccept, listener); } catch { // Dies when the socket is disposed, but we don't care. } }
private void Authenticate(RconClient sender, RconPacket packet) { var hash = Md5Util.HashString(packet.Body); if (_pwHash != null && hash.SequenceEqual(_pwHash)) { Log.Info($"{sender.RemoteEndPoint}: Authorized"); // Necessary to send an empty RESPONSE_VALUE before sending the AUTHRESPONSE, otherwise most RCON clients don´t realize AUTH has been successfully. // See https://developer.valvesoftware.com/wiki/Source_RCON_Protocol#SERVERDATA_AUTH_RESPONSE // >> When the server receives an auth request, it will respond with an empty SERVERDATA_RESPONSE_VALUE, followed immediately by a SERVERDATA_AUTH_RESPONSE indicating whether authentication succeeded or failed. sender.SendPacket(new RconPacket(packet.Id, PacketType.SERVERDATA_RESPONSE_VALUE, string.Empty)); sender.SendPacket(new RconPacket(packet.Id, PacketType.SERVERDATA_AUTHRESPONSE, string.Empty)); sender.IsAuthed = true; } else { Log.Warn($"{sender.RemoteEndPoint}: Incorrect password attempt"); sender.SendPacket(new RconPacket(packet.Id, PacketType.SERVERDATA_RESPONSE_VALUE, string.Empty)); // same here by definition although most clients realized the wrong AUTH info sender.SendPacket(new RconPacket(-1, PacketType.SERVERDATA_AUTHRESPONSE, string.Empty)); sender.IsAuthed = false; } }