/// <summary> /// Send the proper authentication packet and parse the response /// </summary> /// <param name="password">Current server password</param> /// <returns>True if the connection has been authenticated; False elsewhere</returns> /// <remarks>This method must be called prior to sending any other command</remarks> /// <exception cref="ArgumentException">Is thrown if <paramref name="password"/> parameter is null or empty</exception> public async Task <bool> AuthenticateAsync(string password) { if (string.IsNullOrEmpty(password)) { throw new ArgumentException("password parameter must be a non null non empty string"); } var authPacket = new RconPacket(PacketType.Auth, password); var response = await _socket.SendDataAndReadResponseAsync(authPacket.GetBytes()); var responsePacket = RconPacket.FromBytes(response); return(responsePacket.Id != -1); }
/// <summary> /// Send a command encapsulated into an Rcon message packet and get the response /// </summary> /// <param name="command">Command to be executed</param> /// <returns>The response to this command</returns> /// <exception cref="ArgumentException">Is thrown if <paramref name="command"/> parameter is null or empty</exception> /// <exception cref="InvalidOperationException">Is thrown if the connection is not properly opened and authenticated</exception> public async Task <string> ExecuteCommandAsync(string command) { if (string.IsNullOrEmpty(command)) { throw new ArgumentException("command parameter must be a non null non empty string"); } if (!_socket.IsConnected) { throw new InvalidOperationException("You must authenticate the connection before sending any command to the server"); } var commandPacket = new RconPacket(PacketType.ExecCommand, command); var response = await _socket.SendDataAndReadResponseAsync(commandPacket.GetBytes()); var responsePacket = RconPacket.FromBytes(response); return(responsePacket.Body); }
private async Task ReadFromPipeAsync(PipeReader reader) { while (true) { var readResult = await reader.ReadAsync(); var buffer = readResult.Buffer; var startPosition = buffer.Start; if (buffer.Length < 4) // not enough bytes to get the packet length, need to read more { if (readResult.IsCompleted) { break; } reader.AdvanceTo(startPosition, buffer.End); continue; } var packetSize = BitConverter.ToInt32(buffer.Slice(startPosition, 4).ToArray()); if (buffer.Length >= packetSize + 4) { var endPosition = buffer.GetPosition(packetSize + 4, startPosition); var rconPacket = RconPacket.FromBytes(buffer.Slice(startPosition, endPosition).ToArray()); if (!rconPacket.IsDummy) { var currentOperation = operations.Peek(); currentOperation.Add(rconPacket); if (currentOperation.OriginalRequest.Type == PacketType.Auth && rconPacket.Type == PacketType.Response) { // According to RCON documentation an empty RESPONSE packet must be sent after the auth request, but at the moment only CS:GO does so .. } else if (currentOperation.IsMultiPacketResponse && !string.IsNullOrEmpty(rconPacket.Body)) { // Accumulate and move on } else { operations.Dequeue(); if (rconPacket.Id == -1) { currentOperation.TaskCompletionSource.SetException(new AuthenticationException("Invalid password")); } else { currentOperation.TaskCompletionSource.SetResult(currentOperation.Body); } } } reader.AdvanceTo(endPosition); } else { reader.AdvanceTo(startPosition, buffer.End); } if (buffer.IsEmpty && readResult.IsCompleted) { break; } } await reader.CompleteAsync(); }