/// <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 message encapsulated into an Rcon packet and get the response /// </summary> /// <param name="packet">Packet to be sent</param> /// <returns>The response to this command</returns> private async Task <string> SendPacketAsync(RconPacket packet, bool isMultiPacketResponse = false) { var packetDescription = new Operation(packet, isMultiPacketResponse); operations.Enqueue(packetDescription); await channel.SendAsync(packet.ToBytes()); if (isMultiPacketResponse) { await channel.SendAsync(RconPacket.Dummy.Value.ToBytes()); } return(await packetDescription.TaskCompletionSource.Task); }
/// <summary> /// Create a new RconPacket from a byte array /// </summary> /// <param name="buffer">Input buffer</param> /// <returns>Parsed RconPacket</returns> /// <exception cref="System.ArgumentException">When buffer is null or its size is less than 14</exception> public static RconPacket FromBytes(byte[] buffer) { if (buffer == null || buffer.Length < 14) { throw new ArgumentException("Invalid packet"); } RconPacket packet = new RconPacket() { Body = Encoding.UTF8.GetString(buffer, bodyIndex, buffer.ToInt32(sizeIndex) - 10), Id = buffer.ToInt32(idIndex) }; return(packet); }
/// <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); }
/// <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 = RconPacket.Create(PacketType.Auth, password); try { var response = await SendPacketAsync(authPacket); } catch (AuthenticationException ex) { return(false); } return(true); }
public void Add(RconPacket packet) => PacketsBuffer.Add(packet);
public Operation(RconPacket originalRequest, bool isMultiPacketResponse) { OriginalRequest = originalRequest; TaskCompletionSource = new TaskCompletionSource <string>(TaskCreationOptions.RunContinuationsAsynchronously); IsMultiPacketResponse = isMultiPacketResponse; }
public Task <string> ExecuteCommandAsync(string command, bool isMultiPacketResponse = false) { return(SendPacketAsync(RconPacket.Create(PacketType.ExecCommand, command), isMultiPacketResponse)); }
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(); }