public long ReadPacketSize(byte[] packetData) { // Attempt an initial parse to get the headers. CommandServerPacket packet = new CommandServerPacket(); CommandServerPacketSerializer.Parse(packet, packetData); long length = 0; // If the headers contain a content-length variable, use that. if (packet.Headers[HttpRequestHeader.ContentLength] != null) { // todo Potential issues with content encoding? if (long.TryParse(packet.Headers[HttpRequestHeader.ContentLength], out length) == false) { length = packetData.Length; } else { length += packet.Header.Length; } // The header/content separator. length += 4; } return length; }
/// <summary> /// Takes in a request and does it's best to convert it to a command within Potato for execution. /// </summary> /// <param name="request">The http request for this command</param> /// <returns>The deserialized command or null if an error occured during deserialization</returns> public static ICommand DeserializeCommand(CommandServerPacket request) { ICommand command = null; try { switch (request.Headers[HttpRequestHeader.ContentType].ToLower()) { default: command = Core.Shared.Serialization.JsonSerialization.Minimal.Deserialize<Command>(request.Content); break; } if (command != null) { command.Origin = CommandOrigin.Remote; HttpCommandRequest commandRequest = new HttpCommandRequest() { Content = new List<String>() { request.Header, request.Content }, Packets = new List<IPacket>() { request.Packet } }; commandRequest.AppendTags(request.Headers); commandRequest.AppendTags(request.Query); command.Request = commandRequest; } } catch { command = null; } return command; }
public IPacketWrapper Deserialize(byte[] packetData) { CommandServerPacket packet = new CommandServerPacket(); CommandServerPacketSerializer.Parse(packet, packetData); packet.Query = CommandServerPacketSerializer.CombineNameValueCollections(CommandServerPacketSerializer.ParseGet(packet), CommandServerPacketSerializer.ParsePost(packet)); return packet; }
/// <summary> /// Serializes all of the header data to a byte array for transfer. /// </summary> /// <param name="packet"></param> /// <param name="content"></param> /// <returns></returns> protected byte[] SerializeHeader(CommandServerPacket packet, byte[] content) { StringBuilder builder = new StringBuilder(); // Ensure a couple of headers are through.. packet.Headers[HttpRequestHeader.Connection] = "close"; packet.Headers[HttpRequestHeader.ContentLength] = content.Length.ToString(CultureInfo.InvariantCulture); builder.AppendFormat("HTTP/{0}.{1} {2} {3}\r\n", packet.ProtocolVersion.Major, packet.ProtocolVersion.Minor, (int)packet.StatusCode, packet.StatusCode); builder.Append(packet.Headers); // Already adds the double new lines. return Encoding.UTF8.GetBytes(builder.ToString()); }
/// <summary> /// Serializes the content to a byte array, optionally compressing it if the client has sent /// through that it accepts the encoding. /// </summary> /// <param name="packet"></param> /// <returns></returns> protected byte[] SerializeContent(CommandServerPacket packet) { byte[] data = Encoding.UTF8.GetBytes(packet.Content); if (packet.Headers[HttpRequestHeader.ContentEncoding] != null) { String contentEncoding = packet.Headers[HttpRequestHeader.ContentEncoding].ToLowerInvariant(); if (contentEncoding.Contains("gzip") == true) { data = CommandServerPacketSerializer.GzipCompress(data); } else if (contentEncoding.Contains("deflate") == true) { data = CommandServerPacketSerializer.DeflateCompress(data); } } return data; }
protected static NameValueCollection ParsePost(CommandServerPacket packet) { NameValueCollection query = new NameValueCollection(); if (packet.Content != null) { query = HttpUtility.ParseQueryString(packet.Content); } return query; }
protected static NameValueCollection ParseGet(CommandServerPacket packet) { return HttpUtility.ParseQueryString(packet.Request.Query); }
protected static void Parse(CommandServerPacket packet, byte[] packetData) { String[] packetStringData = Regex.Split(Encoding.UTF8.GetString(packetData), @"\r\n\r\n"); packet.Header = packetStringData.FirstOrDefault(); // Fetch the rest of the content data for later. packet.Content = packetStringData.LastOrDefault(); if (packet.Header != null) { string[] headers = packet.Header.Split(new [] {"\r\n"}, StringSplitOptions.RemoveEmptyEntries); if (headers.Length > 0) { List<String> status = headers.First().Wordify(); var headerValues = packet.Header.Split(new [] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).Skip(1).ToDictionary( line => (line.Split(new[] { ":" }, 2, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault() ?? "").Trim(), line => (line.Split(new[] { ":" }, 2, StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? "").Trim() ); if (status.Count == 3 && headerValues.ContainsKey("Host") == true) { packet.Request = new Uri("http://" + headerValues["Host"] + status[1]); packet.Method = status[0]; packet.ProtocolVersion = new Version(status[2].Replace("HTTP/", "")); foreach (var header in headerValues) { try { packet.Headers.Set(header.Key, header.Value); } catch { packet.Headers.Set(header.Key, ""); } } } } } }
/// <summary> /// Serializes the result of the issued command back into the http response for the user. /// </summary> /// <param name="contentType">The content type to serialize the response to</param> /// <param name="response">The existing response packet to be modified with additional data/changes</param> /// <param name="result">The result of the command issued in the request</param> /// <returns>The existing response packet, modified with the result of the command execution.</returns> public static CommandServerPacket CompleteResponsePacket(String contentType, CommandServerPacket response, ICommandResult result) { switch (contentType) { case Mime.ApplicationJavascript: case Mime.TextCss: case Mime.TextHtml: response.Headers.Add(HttpRequestHeader.ContentType, contentType); response.Content = result.Now.Content != null ? result.Now.Content.FirstOrDefault() : ""; response.StatusCode = HttpStatusCode.OK; break; default: response.Headers.Add(HttpRequestHeader.ContentType, Mime.ApplicationJson); using (StringWriter writer = new StringWriter()) { Core.Shared.Serialization.JsonSerialization.Minimal.Serialize(writer, result); response.Content = writer.ToString(); } response.StatusCode = HttpStatusCode.OK; break; } return response; }
protected virtual void OnPacketReceived(IClient client, CommandServerPacket request) { var handler = PacketReceived; if (handler != null) { handler(client, request); } }
/// <summary> /// Respondes to a packet, making sure the response matches the request. /// We've purposfully allowed direct packets to be sent in case we needed /// to deliberately bypass some of these. /// </summary> /// <param name="sender">The client that received the response.</param> /// <param name="request">The original packet received by the listener.</param> /// <param name="response">The response to send to the server.</param> public void Respond(IClient sender, CommandServerPacket request, CommandServerPacket response) { response.Method = request.Method; response.ProtocolVersion = request.ProtocolVersion; if (request.Headers[HttpRequestHeader.AcceptEncoding] != null) { String acceptEncoding = request.Headers[HttpRequestHeader.AcceptEncoding].ToLowerInvariant(); if (acceptEncoding.Contains("gzip") == true) { response.Headers[HttpRequestHeader.ContentEncoding] = "gzip"; } else if (acceptEncoding.Contains("deflate") == true) { response.Headers[HttpRequestHeader.ContentEncoding] = "deflate"; } } sender.Send(response); }
/// <summary> /// Called when a packet is recieved from the listening command server. /// </summary> /// <param name="client">The client to send back the response</param> /// <param name="request">The request packet recieved</param> public void OnPacketReceived(IClient client, CommandServerPacket request) { CommandServerPacket response = new CommandServerPacket() { Packet = { Type = PacketType.Response, Origin = PacketOrigin.Client, }, ProtocolVersion = request.ProtocolVersion, Method = request.Method, StatusCode = HttpStatusCode.NotFound, Headers = new WebHeaderCollection() { { HttpRequestHeader.Connection, "close" } } }; ICommand command = CommandServerSerializer.DeserializeCommand(request); if (command != null) { ICommandResult authentication = this.Authenticate(request, command); if (authentication.Success == true) { // If all they wanted to do was check the authentication.. if (String.CompareOrdinal(command.Name, CommandType.SecurityAccountAuthenticate.ToString()) == 0 || String.CompareOrdinal(command.Name, CommandType.SecurityAccountAuthenticateToken.ToString()) == 0) { // Success response = CommandServerSerializer.CompleteResponsePacket(CommandServerSerializer.ResponseContentType(command), response, authentication); } else { // Propagate their command ICommandResult result = this.Tunnel(command); response = CommandServerSerializer.CompleteResponsePacket(CommandServerSerializer.ResponseContentType(command), response, result); } } else { // They are not authorized to login or issue this command. response = CommandServerSerializer.CompleteResponsePacket(CommandServerSerializer.ResponseContentType(command), response, authentication); } } else { // Something wrong during deserialization, issue a bad request. response.StatusCode = HttpStatusCode.BadRequest; } this.CommandServerListener.Respond(client, request, response); }
/// <summary> /// Authenticate the command given the request information. /// </summary> /// <remarks> /// <para>This command only checks if the user is authenticated with our system, not if they can execute the command. This is accomplished while executing the command.</para> /// </remarks> /// <param name="request">The request information</param> /// <param name="command">The command to authenticate</param> /// <returns>The result of authentication</returns> protected ICommandResult Authenticate(CommandServerPacket request, ICommand command) { ICommandResult result = null; if (String.IsNullOrEmpty(command.Authentication.Username) == false && String.IsNullOrEmpty(command.Authentication.PasswordPlainText) == false) { result = this.Shared.Security.Tunnel(CommandBuilder.SecurityAccountAuthenticate(command.Authentication.Username, command.Authentication.PasswordPlainText, this.ExtractIdentifer(request)).SetOrigin(CommandOrigin.Remote)); } else if (command.Authentication.TokenId != Guid.Empty && String.IsNullOrEmpty(command.Authentication.Token) == false) { result = this.Shared.Security.Tunnel(CommandBuilder.SecurityAccountAuthenticateToken(command.Authentication.TokenId, command.Authentication.Token, this.ExtractIdentifer(request)).SetOrigin(CommandOrigin.Remote)); } else { result = new CommandResult() { Success = false, CommandResultType = CommandResultType.Failed, Message = "Invalid username or password" }; } return result; }
/// <summary> /// Pulls the identifer out of the request (endpoint address) /// </summary> /// <param name="request">The request received from the client</param> /// <returns>The extracted identifer or an empty string if none exists</returns> protected String ExtractIdentifer(CommandServerPacket request) { var identifer = ""; if (request != null && request.Packet != null && request.Packet.RemoteEndPoint != null && request.Packet.RemoteEndPoint.Address != null) { identifer = request.Packet.RemoteEndPoint.Address.ToString(); } return identifer; }