mirror of https://github.com/infosecn1nja/C3.git
172 lines
6.2 KiB
C#
172 lines
6.2 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.CSharp.RuntimeBinder;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using FSecure.C3.WebController.Comms.GatewayResponses;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace FSecure.C3.WebController.Comms
|
|
{
|
|
public partial class GatewayConnectionHandler
|
|
{
|
|
private Tuple<byte[], byte[]> SessionKeys;
|
|
|
|
private class InvalidMessage : Exception
|
|
{
|
|
public InvalidMessage(string errorMessage) : base(errorMessage) { }
|
|
|
|
public InvalidMessage(string errorMessage, Exception innerException) : base(errorMessage, innerException) { }
|
|
};
|
|
|
|
private async Task StartReceivingTask()
|
|
{
|
|
try
|
|
{
|
|
var cancellationToken = anyCTS.Token;
|
|
|
|
await ExchangeKeys(cancellationToken);
|
|
await ProcessInitialMessage(cancellationToken);
|
|
while (true)
|
|
{
|
|
await ProcessMessages(cancellationToken);
|
|
}
|
|
}
|
|
catch (InvalidMessage e)
|
|
{
|
|
Console.WriteLine($"Invalid initial message, aborting connection. {e.Message}");
|
|
}
|
|
catch (CommandQueues.InvalidGateway e)
|
|
{
|
|
await SendRequest(new GatewayRequests.GatewayRequest(new GatewayRequests.Error(e.Message)));
|
|
}
|
|
catch (Exception e)
|
|
when (e is OperationCanceledException || e is System.IO.IOException)
|
|
{
|
|
// Task canceled, stopping
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine($"Unknown exception. Aborting connection. {e.Message}");
|
|
}
|
|
|
|
selfCTS.Cancel();
|
|
if (SendingTask != null)
|
|
await SendingTask;
|
|
selfCTS.Dispose();
|
|
anyCTS.Dispose();
|
|
await CloseConnection();
|
|
Console.WriteLine("Gateway receiving background task stopped.");
|
|
}
|
|
|
|
private async Task ExchangeKeys(CancellationToken cancellationToken)
|
|
{
|
|
using (var keys = Crypto.KeyExchange.GenerateKeyPair())
|
|
{
|
|
var clientPublicKey = await tcpClient.ReceiveAsync(cancellationToken);
|
|
if (clientPublicKey.Length != Crypto.KeyExchange.KeyBytes)
|
|
throw new InvalidMessage("Received invalid key");
|
|
|
|
SessionKeys = Crypto.KeyExchange.GenerateServerSessionKeys(keys, clientPublicKey);
|
|
await tcpClient.SendAsync(keys.PublicKey);
|
|
}
|
|
}
|
|
|
|
|
|
private async Task ProcessInitialMessage(CancellationToken cancellationToken)
|
|
{
|
|
var rawResponse = await ReceiveMessage(cancellationToken);
|
|
// Parse response, dispatch
|
|
|
|
GatewayResponse response = ParseResponse(rawResponse);
|
|
if (response.MessageType.ToLower() != "getcapability")
|
|
throw new InvalidMessage("First message is not GetCapability");
|
|
await BeginConnection(response);
|
|
}
|
|
|
|
private async Task ProcessMessages(CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
var rawResponse = await ReceiveMessage(cancellationToken);
|
|
|
|
// Parse response, dispatch
|
|
var response = ParseResponse(rawResponse);
|
|
await ProcessResponse(response);
|
|
}
|
|
catch (TypeLoadException e)
|
|
{
|
|
Console.WriteLine($"Received invlid or unrecognized message type: {e.TypeName}");
|
|
}
|
|
catch (InvalidMessage e)
|
|
{
|
|
Console.WriteLine($"Received invlid or unrecognized message: {e.Message}");
|
|
}
|
|
catch (Microsoft.EntityFrameworkCore.DbUpdateException e)
|
|
{
|
|
Console.WriteLine($"Failed to process message. [{e.GetType().Name}]: {e.InnerException.Message}");
|
|
}
|
|
}
|
|
|
|
private async Task<byte[]> ReceiveMessage(CancellationToken ct)
|
|
{
|
|
var message = await tcpClient.ReceiveAsync(ct);
|
|
return Crypto.KeyExchange.Decrypt(message, SessionKeys.Item1);
|
|
}
|
|
|
|
private GatewayResponses.GatewayResponse ParseResponse(byte[] responseData)
|
|
{
|
|
var message = System.Text.Encoding.ASCII.GetString(responseData);
|
|
try
|
|
{
|
|
return JsonConvert.DeserializeObject<GatewayResponses.GatewayResponse>(message);
|
|
}
|
|
catch (JsonException e)
|
|
{
|
|
throw new InvalidMessage("Failed to deserialize message", e);
|
|
}
|
|
}
|
|
|
|
private async Task BeginConnection(GatewayResponses.GatewayResponse response)
|
|
{
|
|
gatewayId = response.GetMessage().Gate.AgentId;
|
|
if (!commandQueues.AddGateway(gatewayId))
|
|
throw new CommandQueues.InvalidGateway($"Gateway [ID: {gatewayId}] already connected");
|
|
SendingTask = StartSendingTask();
|
|
await ProcessResponse(response);
|
|
}
|
|
|
|
private async Task CloseConnection()
|
|
{
|
|
using (var serviceScope = Services.CreateScope())
|
|
{
|
|
var responseProcessor = serviceScope.ServiceProvider.GetRequiredService<GatewayResponseProcessor>();
|
|
await responseProcessor.MarkInactive(gatewayId);
|
|
}
|
|
}
|
|
|
|
private async Task ProcessResponse(GatewayResponse response)
|
|
{
|
|
try
|
|
{
|
|
using (var serviceScope = Services.CreateScope())
|
|
{
|
|
{
|
|
var commandQueues = serviceScope.ServiceProvider.GetRequiredService<CommandQueues>();
|
|
commandQueues.TryPostResponse(gatewayId, response);
|
|
}
|
|
if (!response.HasError())
|
|
{
|
|
var responseProcessor = serviceScope.ServiceProvider.GetRequiredService<GatewayResponseProcessor>();
|
|
await responseProcessor.ProcessResponse(response.GetMessage());
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine($"Unknown exception during message processing. {e.Message}");
|
|
}
|
|
}
|
|
}
|
|
}
|