mirror of
https://github.com/openziti/desktop-edge-win.git
synced 2026-09-22 02:43:31 +00:00
369 lines
14 KiB
C#
369 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.IO.Pipes;
|
|
using System.Security.Principal;
|
|
using System.Security.AccessControl;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
using Newtonsoft.Json;
|
|
using NLog;
|
|
|
|
using ZitiDesktopEdge.DataStructures;
|
|
using ZitiDesktopEdge.Server;
|
|
|
|
/// <summary>
|
|
/// The implementation will abstract away the setup of the communication to
|
|
/// the service. This implementation will communicate to the service over a
|
|
/// a NamedPipe.
|
|
///
|
|
/// All communication is effectively serial - one or more messages sent and
|
|
/// one or more messages returned.
|
|
///
|
|
/// </summary>
|
|
namespace ZitiDesktopEdge.ServiceClient {
|
|
public class DataClient : AbstractClient {
|
|
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
|
protected override Logger Logger { get { return _logger; } }
|
|
|
|
public const int EXPECTED_API_VERSION = 1;
|
|
|
|
public event EventHandler<TunnelStatusEvent> OnTunnelStatusEvent;
|
|
public event EventHandler<List<Identity>> OnMetricsEvent;
|
|
public event EventHandler<IdentityEvent> OnIdentityEvent;
|
|
public event EventHandler<ServiceEvent> OnServiceEvent;
|
|
public event EventHandler<MfaEvent> OnMfaEvent;
|
|
|
|
protected override void ShutdownEvent(StatusEvent e) {
|
|
Logger.Debug("Clean shutdown detected from ziti");
|
|
CleanShutdown = true;
|
|
base.ShutdownEvent(e);
|
|
}
|
|
|
|
protected virtual void TunnelStatusEvent(TunnelStatusEvent e) {
|
|
OnTunnelStatusEvent?.Invoke(this, e);
|
|
}
|
|
|
|
protected virtual void MetricsEvent(List<Identity> e) {
|
|
OnMetricsEvent?.Invoke(this, e);
|
|
}
|
|
|
|
protected virtual void IdentityEvent(IdentityEvent e) {
|
|
OnIdentityEvent?.Invoke(this, e);
|
|
}
|
|
|
|
protected virtual void ServiceEvent(ServiceEvent e) {
|
|
OnServiceEvent?.Invoke(this, e);
|
|
}
|
|
|
|
protected virtual void MfaEvent(MfaEvent e) {
|
|
OnMfaEvent?.Invoke(this, e);
|
|
}
|
|
|
|
protected override void ClientConnected(object e) {
|
|
base.ClientConnected(e);
|
|
}
|
|
|
|
protected override void ClientDisconnected(object e) {
|
|
Reconnect();
|
|
Connected = false;
|
|
base.ClientDisconnected(e);
|
|
}
|
|
|
|
const string ipcPipe = @"OpenZiti\ziti\ipc";
|
|
const string logPipe = @"OpenZiti\ziti\logs";
|
|
const string eventPipe = @"OpenZiti\ziti\events";
|
|
|
|
public DataClient() : base() {
|
|
}
|
|
|
|
PipeSecurity CreateSystemIOPipeSecurity() {
|
|
PipeSecurity pipeSecurity = new PipeSecurity();
|
|
|
|
var id = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
|
|
|
|
// Allow Everyone read and write access to the pipe.
|
|
pipeSecurity.SetAccessRule(new PipeAccessRule(id, PipeAccessRights.ReadWrite, AccessControlType.Allow));
|
|
|
|
return pipeSecurity;
|
|
}
|
|
|
|
async protected override Task ConnectPipesAsync() {
|
|
await semaphoreSlim.WaitAsync();
|
|
try {
|
|
pipeClient = new NamedPipeClientStream(localPipeServer, ipcPipe, PipeDirection.InOut);
|
|
eventClient = new NamedPipeClientStream(localPipeServer, eventPipe, PipeDirection.In);
|
|
await eventClient.ConnectAsync(ServiceConnectTimeout);
|
|
await pipeClient.ConnectAsync(ServiceConnectTimeout);
|
|
ClientConnected(null);
|
|
} catch (Exception ex) {
|
|
semaphoreSlim.Release();
|
|
throw new ServiceException("Could not connect to the service.", 1, ex.Message);
|
|
}
|
|
semaphoreSlim.Release();
|
|
}
|
|
|
|
async public Task<ZitiTunnelStatus> GetStatusAsync() {
|
|
try {
|
|
await sendAsync(new ServiceFunction() { Function = "Status" });
|
|
var rtn = await readAsync<ZitiTunnelStatus>(ipcReader);
|
|
return rtn;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//setupPipe();
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
ServiceFunction AddIdentityFunction = new ServiceFunction() { Function = "AddIdentity" };
|
|
|
|
async public Task<Identity> AddIdentityAsync(string identityName, bool activate, string jwt) {
|
|
try {
|
|
Identity id = new Identity {
|
|
Active = activate,
|
|
Name = identityName
|
|
};
|
|
|
|
NewIdentity newId = new NewIdentity() {
|
|
Id = id,
|
|
Flags = new EnrollmentFlags() {
|
|
JwtString = jwt
|
|
}
|
|
};
|
|
|
|
await sendAsync(AddIdentityFunction);
|
|
await sendAsync(newId);
|
|
var resp = await readAsync<IdentityResponse>(ipcReader);
|
|
Logger.Debug(resp.ToString());
|
|
if (resp.Code != 0) {
|
|
throw new ServiceException(resp.Message, resp.Code, resp.Error);
|
|
}
|
|
return resp.Payload;
|
|
} catch (Exception ex) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//setupPipe();
|
|
//throw;
|
|
Logger.Error(ex, "Unexpected error");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
async public Task RemoveIdentityAsync(string fingerPrint) {
|
|
if (string.IsNullOrEmpty(fingerPrint)) {
|
|
//nothing to do...
|
|
return;
|
|
}
|
|
|
|
try {
|
|
FingerprintFunction removeFunction = new FingerprintFunction() {
|
|
Function = "RemoveIdentity",
|
|
Payload = new FingerprintPayload() { Fingerprint = fingerPrint }
|
|
};
|
|
await sendAsync(removeFunction);
|
|
var r = await readAsync<SvcResponse>(ipcReader);
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//setupPipe();
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return;
|
|
}
|
|
|
|
async public Task SetLogLevelAsync(string level) {
|
|
try {
|
|
await sendAsync(new SetLogLevelFunction(level));
|
|
SvcResponse resp = await readAsync<SvcResponse>(ipcReader);
|
|
return;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//setupPipe();
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return;
|
|
}
|
|
|
|
async public Task SetLogLevelAsync(LogLevelEnum level) {
|
|
try {
|
|
await sendAsync(new SetLogLevelFunction(Enum.GetName(level.GetType(), level)));
|
|
SvcResponse resp = await readAsync<SvcResponse>(ipcReader);
|
|
return;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//setupPipe();
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return;
|
|
}
|
|
|
|
async public Task<Identity> IdentityOnOffAsync(string fingerprint, bool onOff) {
|
|
try {
|
|
await sendAsync(new IdentityToggleFunction(fingerprint, onOff));
|
|
IdentityResponse idr = await readAsync<IdentityResponse>(ipcReader);
|
|
return idr.Payload;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//setupPipe();
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async public Task<SvcResponse> EnableMFA(string fingerprint) {
|
|
try {
|
|
await sendAsync(new EnableMFAFunction(fingerprint));
|
|
SvcResponse mfa = await readAsync<SvcResponse>(ipcReader);
|
|
return mfa;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async public Task<SvcResponse> VerifyMFA(string fingerprint, string totp) {
|
|
try {
|
|
await sendAsync(new VerifyMFAFunction(fingerprint, totp));
|
|
SvcResponse mfa = await readAsync<SvcResponse>(ipcReader);
|
|
return mfa;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async public Task<SvcResponse> AuthMFA(string fingerprint, string totp) {
|
|
try {
|
|
await sendAsync(new AuthMFAFunction(fingerprint, totp));
|
|
SvcResponse mfa = await readAsync<SvcResponse>(ipcReader);
|
|
return mfa;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async public Task<MfaRecoveryCodesResponse> ReturnMFACodes(string fingerprint, string totpOrRecoveryCode) {
|
|
try {
|
|
await sendAsync(new ReturnMFACodesFunction(fingerprint, totpOrRecoveryCode));
|
|
MfaRecoveryCodesResponse mfa = await readAsync<MfaRecoveryCodesResponse>(ipcReader);
|
|
return mfa;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async public Task<MfaRecoveryCodesResponse> GenerateMFACodes(string fingerprint, string totpOrRecoveryCode) {
|
|
try {
|
|
await sendAsync(new GenerateMFACodesFunction(fingerprint, totpOrRecoveryCode));
|
|
MfaRecoveryCodesResponse mfa = await readAsync<MfaRecoveryCodesResponse>(ipcReader);
|
|
return mfa;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected override void ProcessLine(string line) {
|
|
try {
|
|
string respAsString = line;
|
|
var jsonReaderEvt = new JsonTextReader(new StringReader(respAsString));
|
|
StatusEvent evt = serializer.Deserialize<StatusEvent>(jsonReaderEvt);
|
|
var jsonReader = new JsonTextReader(new StringReader(respAsString));
|
|
|
|
switch (evt.Op) {
|
|
case "metrics":
|
|
MetricsEvent m = serializer.Deserialize<MetricsEvent>(jsonReader);
|
|
|
|
if (m != null) {
|
|
MetricsEvent(m.Identities);
|
|
}
|
|
break;
|
|
case "status": //break here to see status on startup
|
|
TunnelStatusEvent tse = serializer.Deserialize<TunnelStatusEvent>(jsonReader);
|
|
|
|
if (tse != null) {
|
|
TunnelStatusEvent(tse);
|
|
}
|
|
break;
|
|
case "identity":
|
|
IdentityEvent id = serializer.Deserialize<IdentityEvent>(jsonReader);
|
|
|
|
if (id != null) {
|
|
IdentityEvent(id);
|
|
}
|
|
break;
|
|
case "service":
|
|
ServiceEvent svc = serializer.Deserialize<ServiceEvent>(jsonReader);
|
|
|
|
if (svc != null) {
|
|
ServiceEvent(svc);
|
|
}
|
|
break;
|
|
case "shutdown":
|
|
Logger.Debug("shutdown message received");
|
|
var se = new StatusEvent();
|
|
se.Op = "clean";
|
|
ShutdownEvent(se);
|
|
break;
|
|
case "mfa":
|
|
Logger.Debug("mfa event received");
|
|
MfaEvent mfa = serializer.Deserialize<MfaEvent>(jsonReader);
|
|
MfaEvent(mfa);
|
|
break;
|
|
default:
|
|
Logger.Debug("unexpected operation! " + evt.Op);
|
|
break;
|
|
}
|
|
} catch (Exception e) {
|
|
Logger.Debug(e.Message);
|
|
}
|
|
}
|
|
|
|
async public Task zitiDump() {
|
|
try {
|
|
await sendAsync(new ServiceFunction() { Function = "ZitiDump" });
|
|
var rtn = await readAsync<SvcResponse>(ipcReader);
|
|
return; // rtn;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//setupPipe();
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return;
|
|
}
|
|
|
|
async public Task<ZitiTunnelStatus> debugAsync() {
|
|
try {
|
|
await sendAsync(new ServiceFunction() { Function = "Debug" });
|
|
var rtn = await readAsync<ZitiTunnelStatus>(ipcReader);
|
|
return rtn;
|
|
} catch (Exception ioe) {
|
|
//almost certainly a problem with the pipe - recreate the pipe...
|
|
//setupPipe();
|
|
//throw ioe;
|
|
Logger.Error(ioe, "Unexpected error");
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
}
|